web-dev-qa-db-ja.com

REST APIでのユーザー登録/認証フロー

StackOverflowでトピックが処理されるのはこれが初めてではないことはわかっていますが、答えが見つからなかった、または他の質問が答えに反対したいくつかの質問があります。

私はかなり単純なREST API(Silex-PHP)を最初にたった1つのSPA(バックボーンアプリ)で使用するようにしています。この質問のいくつかの認証方法すべてにコメントしたくありませんこのトピックは既にSOで完全にカバーされているため、基本的に各ユーザーのトークンを作成し、このトークンはSPAによる認証を必要とするすべてのリクエストに添付されます。すべてのSPA-ServerトランザクションはHTTPSで実行されます。セッションごとに期限切れ/トークンが発生するトークンは、RESTのステートレス性に準拠していませんよね?セキュリティの改善の余地はたくさんあると思いますが、それは今のところ私のスコープです。

トークンのモデルがあるため、user_idへのFKを持つトークンのデータベースのテーブルがあります。つまり、トークンはユーザーモデルの一部ではありません。

登録

データベースにユーザーを作成し、新しいユーザーを返すPOST/users(認証不要))があります。これは、1つの要求と1つのリソースルールに準拠しています。 :

  • 私のアイデアは、その時点で新しいユーザーを作成し、そのユーザーの新しいトークンを作成し、すぐにレスポンスでそれを返すことであり、したがって、UXを改善することです。ユーザーはすぐにWebアプリの使用を開始できます。ただし、そのような応答のトークンを返すと、リソースのみを返すという規則が破られます。代わりに、2つのリクエストを一緒に行う必要がありますか? 1つはユーザーを作成し、もう1つはユーザーが資格情報を再入力する必要なくトークンを取得しますか?

  • ユーザーと一緒にトークンを返すことにした場合、POST/usersはAPIコンシューマーにとって混乱し、POST/auth/registerが表示されます。もう一度、動詞が関係しているため、この考えは嫌いです。 this answer で提供されるシンプルさが本当に好きですが、もう一度、2つのリクエストを一緒に行う必要があります。 POST/users and a POST/tokens。2つのリクエストを一緒に行うのはどのように間違っているのでしょうか。両方のリクエストが一緒に送信される場合、特定のユーザーに添付されますか?

今のところ、私のフローは次のように機能します。

1. Register form makes a POST /users request
2. Server creates a new user and a new token, returns both in the response (break REST rule)
3. Client now attaches token to every Request that needs Authorization

トークンの有効期限は切れず、REST statelessness。

メール検証

現在のWebアプリのほとんどは、ユーザーのUXを損なうことなく電子メールの検証が必要です。つまり、ユーザーは登録後すぐにWebアプリを使用できます。反対に、上記のように登録リクエストでトークンを返すと、ユーザーは電子メールを検証せずにすぐにすべてのリソースにアクセスできます。

通常、私は次のワークフローに行きます:

1. Register form sends POST /users request.
2. Server creates a new user with validated_email set to false and stores an email_validation_token. Additionally, the server sends an email generating an URL that contains the email_validation_token. 
3. The user clicks on the URL that makes a request: For example POST /users/email_validation/{email_validation_token}
4. Server validates email, sets validated_email to true, generates a token and returns it in the response, redirecting the user to his home page at the same time.

これは複雑すぎるように見え、UXを完全に台無しにします。どうだった?

ログインする

これは非常に簡単です、今のところ私はこのようにしていますので、間違っている場合は修正してください:

1. User fills a log in form which makes a request to POST /login sending Basic Auth credentials.
2. Server checks Basic Auth credentials and returns token for the given user.
3. Web app attached the given token to every future request.

loginは動詞であるため、RESTルールに違反しますが、この方法で全員が同意するようです。

ログアウト

誰もが/ auth/logoutエンドポイントを必要としているように見えるのはなぜですか?私の観点からは、Webアプリで「ログアウト」をクリックすると、基本的にアプリケーションからトークンが削除され、以降のリクエストでトークンが送信されません。サーバーはこれに関与しません。

ページの更新時にトークンが失われないように、トークンはlocalStorageに保持される可能性があるため、ログアウトはlocalStorageからトークンを削除することを意味します。それでも、これはサーバーには影響しません。 POST/logoutが基本的にセッショントークンを使用しているため、RESTのステートレス性が損なわれることを理解しています。

私を覚えてますか

私の場合、基本的には返されたトークンをlocalStorageに保存するかどうかを覚えていることを覚えています。これは正しいですか?

このトピックについてさらに読むことをお勧めする場合は、非常に感謝しています。ありがとう!

27
mezod

[〜#〜] register [〜#〜]

セッションごとに期限切れ/トークンを持つトークンは、RESTのステートレス性に準拠していませんか?

いいえ、それは悪いことではありません。多くのHTTP認証スキームには期限切れトークンがあります。 OAuth2はRESTサービスで非常に人気があり、多くのOAuth2実装はクライアントにアクセストークンを時々更新させる。

私のアイデアは、その時点で新しいユーザーを作成し、そのユーザーの新しいトークンを作成し、すぐにレスポンスでそれを返すことであり、したがって、UXを改善することです。ユーザーはすぐにWebアプリの使用を開始できます。ただし、そのような応答のトークンを返すと、リソースのみを返すという規則が破られます。代わりに、2つのリクエストを一緒に行う必要がありますか? 1つはユーザーを作成し、もう1つはユーザーが資格情報を再入力する必要なくトークンを取得しますか?

通常、RESTベストプラクティスに従って新しいリソースを作成する場合、このようなPOSTに応答して何かを返しません。これを行うと、 RPCのように呼び出しますので、ここであなたに同意します...完全にRESTfulではありません。これに対する2つのソリューションを提供します。

  1. これを無視し、ベストプラクティスを破ります。たぶんそれはこの場合に最適であり、もしそれらがもっと意味をなすなら例外を作ることは時々するべき最良のことです(慎重に検討した後)。
  2. よりRESTfulにしたい場合は、代替手段を提供します。

OAuth2を使用したいとします(悪い考えではありません!)。 OAuth2 APIは、いくつかの理由でRESTfulではありません。 RESTfulであるために独自のAPIをロールオーバーするよりも、明確に定義された認証APIを使用することをお勧めします。

APIでユーザーを作成し、この(POST)呼び出しに応答して、アクセス/更新トークンとして使用できるシークレットを返すという問題が残ります。

私の選択肢は次のとおりです:

セッションを開始するためにユーザーを用意する必要はありません

代わりにできることは、セッションを開始することですbeforeユーザーを作成します。これにより、今後の呼び出しで、同じクライアントと話していることがわかります。

OAuth2プロセスを開始し、アクセス/リフレッシュトークンを受け取った場合、認証済みのPOSTリクエストを/users。これは、システムが2種類の認証済みユーザーを認識する必要があることを意味します。

  1. ユーザー名/パスワード( `grant_type = passsword1)でログインしたユーザー。
  2. 「匿名で」ログインし、事後にユーザーを作成しようとするユーザー。 (grant_type = client_credentials)。

ユーザーが作成されると、以前作成された匿名セッションを新しく作成されたユーザーエンティティに割り当てることができるため、作成後にアクセス/更新トークン交換を行う必要はありません。

メール検証

どちらかに対するあなたの提案:

  • 電子メールの検証が完了するまで、ユーザーがアプリケーションを使用できないようにします。
  • ユーザーがアプリケーションをすぐに使用できるようにする

アプリケーションによって行われます。どちらがより適切であるかは、実際にはアプリケーションと最適なものによって異なります。ユーザーが所有していないメールでアカウントを使用し始めることに関連するリスクはありますか? 「いいえ」の場合、ユーザーをすぐに許可しても問題ありません。

これを行いたくない場合の例を次に示します。システムの他のメンバーがユーザーを友人として追加するためにメールアドレスを使用する場合、メールアドレスはIDの一種です。ユーザーにメールの検証を強制しない場合は、別のメールアドレスを持つユーザーに代わって行動できることを意味します。これは、招待状などを受信できることに似ています。これは攻撃ベクトルですか?次に、電子メールが検証されるまで、ユーザーによるアプリケーションの使用をブロックすることを検討します。

また、アプリケーションの特定の機能のみをブロックすることを検討することもできます。特定の機能では、メールアドレスが重要な場合があります。前の例では、電子メールが検証されるまで、他のユーザーからの招待が他のユーザーに表示されないようにすることができました。

ここには正しい答えはありません。メールアドレスをどのように使用するかによって異なります。

[〜#〜] login [〜#〜]

OAuth2を使用してください。説明するフローは、OAuth2の動作方法にすでにかなり近いものです。さらに一歩進んで実際にOAuth2を使用します。これは非常に素晴らしいことであり、プロトコルを理解するという最初のハードルを乗り越えると、思ったより簡単で、具体的なビットを実装するだけでかなり簡単になります。 APIが必要です。

ほとんどのPHP OAuth2サーバーの実装は素晴らしいものではありません。あまりにも多くの作業を行い、統合が多少困難です。独自のローリングはそのハードではなく、すでにかなり近いです同様の何かを構築する。

[〜#〜] logout [〜#〜]

ログアウトエンドポイントが必要な2つの理由は次のとおりです。

  1. Cookie /セッションベースの認証を使用し、セッションを忘れるようにサーバーに伝えたい場合。これはあなたにとって問題ではないようです。
  2. サーバーにアクセス/リフレッシュトークンをより早く期限切れにするように指示する場合。はい、ローカルストレージからそれらを削除するだけで十分です。それらをサーバー側で強制的に期限切れにすると、それほど自信が持てなくなる可能性があります。誰かがブラウザをMITMできて、トークンにアクセスできるようになったらどうしますか?すぐにログアウトして、既存のトークンをすべて期限切れにしたい場合があります。これはエッジのケースであり、私は個人的にこれをやったことはありませんが、そのがあなたがそれを望む理由になる可能性があります。

覚えている

ええ、ローカルストレージで "remember me"を実装するのは良い考えのようです。

11
Evert

私はもともと/LOGONおよび/LOGOUTアプローチ。私は/PRESENCE。誰かのステータスを知ることと認証の両方を組み合わせるのに役立つようです。

0 = Offline
1 = Available
2 = Busy

Offlineから他のものに移動するには、初期検証を含める必要があります(別名ユーザー名/パスワードが必要)。これにはPATCHまたはPUTを使用できます(表示方法に応じて)。

0
Tyler Montney