Skip to main content
prompt=login の仕組みは、ユーザーエージェント (ブラウザー) を通過する際にパラメーターを削除するだけで回避できてしまうため、 プロバイダー (OP) に対する UX 上のヒントとしてしか使えません。たとえば、 (RP) が次のようなリンクを表示したい場合です。 「こんにちは、Josh さん。あなたではありませんか? ここをクリックしてください。」 ただし、新たな認証が実際に行われたことを検証する目的で、これに頼るべきではありません。これに対処するには、クライアントが auth_time クレーム を使用して再認証が行われたことを検証する必要があります。この クレーム は、認証リクエストで prompt=login または max_age=0 パラメーターを指定すると、 に自動的に含まれます。 Authorization API の /authorize エンドポイントmax_age パラメーターを渡す必要があります。Auth0.js または Lock を使用している場合は、ライブラリーの適切なオプションでこのパラメーターを設定できます。 再認証をどのように実装するかは、具体的なユースケースによって異なります。機微な操作のための単純な再認証と、機微な操作のための step-up (つまり ) は区別してください。どちらも有効なセキュリティ対策です。前者ではエンドユーザーにパスワードの再入力を求め、後者ではそれに加えて、事前に設定された多要素認証の手段の利用も求めます。

prompt=login パラメータの制約

OIDC 仕様 では、再認証用の UI (通常はログイン画面) を表示させるために使える prompt=login パラメータが定義されています。
prompt任意: 認可サーバーがエンドユーザーに再認証と同意を求めるかどうかを指定する、スペース区切りの大文字と小文字を区別する ASCII 文字列値のリストです。定義されている値は次のとおりです。login認可サーバーは、エンドユーザーに再認証を求める必要があります。エンドユーザーを再認証できない場合は、通常 login_required エラーを返さなければなりません。
ただし、このパラメータを使って確実に再認証させようとすると問題があります。RP には、再認証が実際に行われたことを検証する手段がありません。なぜそうなるのかを理解するために、通信の流れを見てみましょう。RP からの認証リクエストのフローは次のとおりです。
AS による認証が成功すると、RP に ID トークンが渡されます:
JSON
AS から返される信頼できる ID ドキュメントには、最後のログインがいつ行われたかを検証できるクレームが含まれていません。これは、最初の認可リクエストがエンドユーザーのブラウザーを経由する 302 リダイレクトの形で送られる場合に問題になります。悪意のある第三者が RP に要求された再認証の手順を回避したい場合は、prompt=login パラメーターを削除するだけでよく、ID トークンに含まれるフィールドだけでは RP はその違いを見分けられません。 以下は、prompt=login パラメーターを使用した簡略化された implicit フローの図です。
強制再認証 OIDC Implicit フロー
なお、エンドユーザーがすることは prompt=login パラメーターを削除することだけで、再認証の手順はスキップできます。
簡略化された Implicit フローで prompt=login を削除
上記の最初のフローで返されるトークンは、2 番目のフローで返されるトークンと同一です。RP には、再認証が行われたことを検証するための仕様で定義された方法がないため、prompt=login によって実際に再認証が行われたと信頼することはできません。

max_age 認証リクエストパラメーター

prompt=login とは異なり、max_age 認証リクエストパラメーターは、指定した時間内に再認証が行われたことを RP が確実に確認できる仕組みを提供します。OIDC 仕様 には次のように記載されています。
max_age任意: 認証からの最大経過時間。エンドユーザーが最後に OP によって能動的に認証されてから、何秒まで許容するかを指定します。経過時間がこの値を超える場合、OP はエンドユーザーに対して能動的な再認証を試みなければなりません。 (max_age リクエストパラメーターは、OpenID 2.0 PAPE の max_auth_age リクエストパラメーターに対応します。) max_age を使用する場合、返される ID トークンには auth_time クレーム値が含まれていなければなりません。
定義の最後の文が最も重要です。RP が max_age を要求した場合、RP が受け取る ID トークンには auth_time クレームが含まれていなければなりません。つまり、max_age は次の 2 つの方法のいずれかで使用できます。
  • セッションの鮮度に最低要件を設けるため: アプリで、ユーザーに 1 日 1 回の再認証を求める要件がある場合、値を指定した max_age を渡すことで、より長時間の セッション内でもこれを強制できます。値は秒単位で定義します。
  • 即時に再認証を強制するため: アプリで、アクセス前にユーザーの再認証が必要な場合は、max_age パラメーターに 0 を指定すると、AS が新たなログインを強制します。
この要件は次のように説明されます。
OIDC re-authentication max_age flow
RP は、再認証が行われたかどうかを検証するために必要な情報を含むトークンを受け取る点に注意してください。これにより RP は、ID トークン内の auth_time クレームを参照して、要求した max_age パラメーターが満たされたかどうかを判断できます。このように、max_age=0 パラメーターは、prompt=login パラメーターを無効化しうるのと同種のクライアント改ざんの影響を受けません。
適切な auth_time を含む ID トークンを受け取っていることを検証する責任は、RP にのみあることに留意してください。この追加の検証は、max_age パラメーターを利用するアプリケーション作成者やフレームワーク側で対応する必要があります。

auth_time クレームを使用する

OIDC 仕様では、max_age パラメーターを使えば再認証が行われたことを確実に確認できますが、prompt=login にはその仕組みがありません。つまり、再認証を強制したい場合、あまり安全な選択肢があるとは言えません。
  • prompt=login: prompt パラメーターだけを含め、AS が実際に再認証を行ったかどうかは検証しません。
  • prompt=login & max_age=999999: 任意の max_age を指定して auth_time クレームが含まれるようにします。再認証が行われたことは検証できますが、パラメーターが煩雑になります。
  • max_age=0: max_age パラメーターだけを使って、実質的にログインプロンプトを強制します。なお、最近の仕様更新でこのパラメーターの意味がさらに明確化され、実質的に prompt=login と同じであることが示されました。これは、UX のためのパラメーターとセッション維持のためのパラメーターを混同してしまうため、現実的ではありません。
そこで Auth0 は、prompt=login リクエストパラメーターに応答する際、ID トークンに auth_time クレームを含めて返すことを選びました。つまり、prompt=login を使いつつ、再認証が行われたことも検証できます。

auth_time の検証例

再認証が実際に行われたことを確認するため、必ず検証を実装してください。適切な auth_time が返されていることを検証する必要があります。
次の例では、再認証を検証する方法を示すために passport-auth0-openidconnect モジュールを使用しています。1 つ目の方法 (最も簡単な方法) は、Auth0OidcStrategymax_age=0 オプションを追加することです。
JavaScript
戦略ですでに max_age パラメータの検証が行われるため、追加の検証手順は不要であることに注意してください。
JavaScript
同じ文脈で prompt=login を使用することもできますが、標準では ID トークンのレスポンスに auth_time を含めることが必須ではないため、検証は手動で行う必要があります。したがって、ストラテジーのコンストラクターは次のようになります。
JavaScript
max_age=0 とは異なり、クライアント側で auth_time パラメータを手動で検証する必要があります。詳しくは、auth_time クレームを使用するをご覧ください。
上記の例は、簡略化した概念実証です (直近 10 秒以内に認証されている必要があります) 。再認証が行われたことを検証するには、理想的には次の対応が必要です。
  1. 最初の認証リクエストを行った時刻を保存します。
  2. 認証レスポンスを受け取ったら、リクエストが送信された時刻を取得します。
  3. 元の認証リクエスト時刻と auth_time クレームを比較し、auth_time のタイムスタンプがそれより後であることを確認します。
Auth0 では、例で示した方法を本番システムで採用することは推奨していません。

既知の問題

Auth0 が保証できるのは、上流のとの間でやり取りが行われたことだけです。これは、ユーザーが実際にサードパーティーのアイデンティティプロバイダーにサインインした場合もあれば、すでにセッションがあり、再度サインインする必要がなかった場合もあります。いずれにしても、Auth0 と上流のアイデンティティプロバイダーとのやり取りの結果、auth_time は更新されます。 上流のアイデンティティプロバイダーで再認証を強制する機能は、すべてのプロバイダーがこれをサポートしているわけではないため、Auth0 ではサポートしていません。 以下の図は、フェデレーション接続で再認証を選択したユーザーのフロー例を示しています。
フェデレーション接続では再認証は強制されない図
この方法は、データベース接続を使用していることを前提としています。外部のアイデンティティプロバイダーは、再認証の強制をサポートしている場合もあれば、していない場合もあります。prompt=loginまたはprompt=consentは、一般に外部の (ソーシャル) アイデンティティプロバイダーにユーザーの再認証を促す手段ですが、Auth0 がこれを強制することはできません。
機密性の高い操作を防ぐために、ID トークンまたはauth_timeのクライアント側 (つまりブラウザー内) での検証に依存しないでください。

詳しくはこちら