メインコンテンツへスキップ
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 からの認証リクエストのフローは次のとおりです。
https://mydomain.auth0.com/authorize?
client_id=abcd1234
&redirect_uri= https://mydomain.com/callback
&scope=openid profile
&response_type=id_token
&prompt=login
AS による認証が成功すると、RP に IDトークン が送られます:
JSON
{
  "nickname": "user",
  "name": "user@mydomain.auth0.com",
  "updated_at": "2019-04-01T14:43:03.445Z",
  "iss": "https://jcain0.auth0.com/",
  "sub": "auth0|l33t",
  "aud": "abcd1234",
  "iat": 1554129793,
  "exp": 1554165793
}
AS から返される信頼できる ID 文書には、最後のログインがいつ行われたかを検証できるクレームがありません。これは、最初の認可リクエストがエンドユーザーのブラウザーを経由する 302 リダイレクトの形で送られる場合に問題となります。悪意のある第三者が RP によって要求された再認証ステップを回避したい場合、prompt=login パラメーターを削除するだけで済み、RP は IDトークン に含まれるフィールドだけではその違いを判別できません。 以下は、prompt=login パラメーターを使用する簡略化された implicit flow の図です。
OIDC Implicit Flow で再認証を強制する
エンドユーザーは prompt=login パラメーターを削除するだけで、再認証ステップをスキップできる点に注意してください。
簡略化された Implicit Flow で prompt=login を削除
上記の 1 つ目のフローで返されるトークンは、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 を要求した場合、返される 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 モジュールを使用して、再認証を検証する方法を示します。最初の方法 (最も簡単な方法でもあります) は、Auth0OidcStrategymax_age=0 オプションを追加することです。
JavaScript
var strategy = new Auth0OidcStrategy(
  {
    domain: process.env.AUTH0_DOMAIN,
    clientID: process.env.AUTH0_CLIENT_ID,
    clientSecret: process.env.AUTH0_CLIENT_SECRET,
    callbackURL: process.env.AUTH0_CALLBACK_URL || 'http://localhost:5000/callback',
    max_age: 0
  },
  function(req, issuer, audience, profile, accessToken, refreshToken, params, cb) {
    // 追加の検証は不要です!
    return cb(null, profile);
  });
戦略が max_age パラメーターの検証をすでに行うため、追加の検証手順は不要であることに注意してください。
JavaScript
// https://openid.net/specs/openid-connect-basic-1_0.html#IDTokenValidation - チェック 8.
if (meta.params.max_age && (!jwtClaims.auth_time || ((meta.timestamp - meta.params.max_age) > jwtClaims.auth_time))) {
  return self.error(new Error('auth_time in id_token not included or too old'));
}
同じコンテキストで prompt=login を使用することもできますが、標準では IDトークンのレスポンスに auth_time を含めることが必須ではないため、検証は手動で行う必要があります。したがって、strategy コンストラクターは次のようになります。
JavaScript
var strategy = new Auth0OidcStrategy(
  {
    domain: process.env.AUTH0_DOMAIN,
    clientID: process.env.AUTH0_CLIENT_ID,
    clientSecret: process.env.AUTH0_CLIENT_SECRET,
    callbackURL: process.env.AUTH0_CALLBACK_URL || 'http://localhost:5000/callback',
    prompt: 'login'
  },
  function(req, issuer, audience, profile, accessToken, refreshToken, params, cb) {
    const tenSecondsAgo = (Date.now() / 1000) - 10;
    if (isNaN(profile.auth_time) || profile.auth_time < tenSecondsAgo) {
      return cb('prompt=login requested, but auth_time is greater than 10 seconds old', null);
    }

    return cb(null, profile);
  });
max_age=0 とは異なり、クライアントは auth_time パラメーターを自分で検証する必要があります。詳細については、auth_time クレームを使用するを参照してください。
上記の例は簡略化した概念実証であり、過去 10 秒以内に認証されていることを前提としています。再認証が行われたことを検証するには、理想的には次の対応が必要です。
  1. 初回の認証リクエストが行われた時刻を保存します。
  2. 認証レスポンスを受け取ったら、リクエストが送信された時刻を取得します。
  3. 元の認証リクエスト時刻と auth_time クレームを比較し、auth_time がそれより後のタイムスタンプであることを確認します。
Auth0 は、この例で使用している方法を本番システムで採用することを推奨していません。

既知の問題

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

詳細情報