Table of Contents
RFC 9470: OAuth 2.0 Step Up Authentication Challenge Protocol は、「リソースサーバーがクライアントに対して、現在のリクエストのアクセストークンに関連付けられた認証イベントが認証要件を満たしていないことを通知し、それを満たす方法を指定するメカニズムを導入する」 ものです(“Abstract” からの抜粋)。
最初のステップとして、クライアントアプリケーション (リライングパーティ)が アクセストークン を用いて 保護リソース のエンドポイントにアクセスします。
エンドポイントの実装は、リクエストからアクセストークンを抽出します。
次に、エンドポイントの実装は、アクセストークンに含まれる ユーザー認証 の情報(アクセストークンの発行時に 認可サーバー が実施した認証)を抽出します。
その後、エンドポイントの実装は、取得したユーザー認証情報がエンドポイントが要求する 認証要件 を満たしているかを確認します。
要件が満たされていない場合、エンドポイントの実装は、クライアントアプリケーションに対して認証要件の情報を含むエラーレスポンスを返します。
以下の図は、アクセストークンを用いたリクエストから、認証要件を伴うエラーレスポンスの返却までのフローを示しています。
クライアントアプリケーションが認証要件を含むエラーレスポンスを受け取った後、再度 認可リクエスト を送信します。この際、クライアントアプリケーションは、認可サーバーに対して認証要件を満たすアクセストークンの発行を依頼する必要があります。
OAuth 2.0 ステップアップ認証チャレンジプロトコルの仕様では、2種類の認証要件を前提としています。
ユーザー認証は、認可フローのどこかで実行されます。OAuth 2.0 および OpenID Connect は、ユーザー認証の詳細を定義していませんが、クライアントアプリケーションは、認可リクエスト内で ACR のリストを指定することで、ユーザー認証の基準を指定できます。
認可サーバーは、指定された ACR のいずれかを満たすユーザー認証を実施しようとします。しかし、acr クレームが「必須(essential)」として要求されていない限り、指定された ACR を満たす認証が行われなくてもエラーは発生しません。
acr_values リクエストパラメータACR のリストを指定する方法の一つは、acr_values リクエストパラメータを使用することです。このパラメータは、OpenID Connect Core 1.0 の セクション 3.1.2.1 で次のように定義されています。
acr_values:任意(OPTIONAL)。要求された認証コンテキストクラスリファレンス値。スペース区切りの文字列で、認可サーバーに対して認証リクエストの処理時に使用する acr 値を指定します。リストは優先順位の順序で並べられます。実施された認証のコンテキストクラスは acr クレームとして返されます。このパラメータの値は、スペース区切りの ACR のリストとして、以下のように認可リクエストに含めることができます。
https://as.example.com/authorize?acr_values=acr1+acr2+acr3&...
claims リクエストパラメータACR のリストを指定する 2 つ目の方法は、claims リクエストパラメータを使用することです。このパラメータは、OpenID Connect Core 1.0 の セクション 5.5 で定義されており、JSON オブジェクトを値として受け取ります。この JSON の構文はやや複雑です。
以下の JSON は、OpenID Connect Core 1.0 から抜粋した claims リクエストパラメータの値の例です。
{
"userinfo": {
"given_name": {"essential": true},
"nickname": null,
"email": {"essential": true},
"email_verified": {"essential": true},
"picture": null,
"http://example.info/claims/groups": null
},
"id_token": {
"auth_time": {"essential": true},
"acr": {"values": ["urn:mace:incommon:iap:silver"]}
}
}
この例では、ACR のリストに "urn:mace:incommon:iap:silver" という 1 つの要素が指定されています。この JSON を claims リクエストパラメータの値として認可リクエストに含めると、認可サーバーは "urn:mace:incommon:iap:silver" の基準を満たすユーザー認証を実施しようとします。
指定された ACR が満たされない場合にエラーを発生させるには、クライアントアプリケーションが acr クレームを 必須(essential) として要求する必要があります。次の JSON の例では、auth_time クレームと acr クレームが必須として要求されています。
{
"id_token": {
"auth_time": {
"essential": true
},
"acr": {
"values": ["urn:mace:incommon:iap:silver"],
"essential": true
}
}
}
default_acr_values クライアントメタデータ3 つ目の方法は、default_acr_values クライアントメタデータを使用することです。このメタデータは、OpenID Connect Dynamic Client Registration 1.0 の セクション 2 で次のように定義されています。
default_acr_values:任意(OPTIONAL)。デフォルトの認証コンテキストクラスリファレンス値。配列として指定され、OP(OpenID Provider)は、クライアントからのリクエスト処理時に、優先順位の順序でこれらの値を使用することが求められます。認証時に満たされたコンテキストクラスは、発行された ID トークンの acr クレーム値として返されます。認可リクエストで明示的に ACR のリストが指定されない場合、default_acr_values に設定された値が ACR のリストとして使用されます。
認可リクエストで acr クレームが必須として要求されている場合、指定された ACR が満たされないと認可サーバーはエラーレスポンスを返します。認可サーバーが “OpenID Connect Core Error Code unmet_authentication_requirements” をサポートしている場合、エラーレスポンスの error パラメータの値として unmet_authentication_requirements を使用します。
仕様ではこのエラーコードについて、次のように説明されています。
unmet_authentication_requirements:認可サーバーが、リライングパーティ(RP)の要求するユーザー認証要件を満たすことができません。このエラーコードは、OpenID Connect Core 1.0 の セクション 5.5.1.1 で指定されるように、acr クレームが必須として要求され、OP がその要件を満たせない場合に使用されるべきです。認可サーバーがこの仕様をサポートしていない場合は、どのエラーコードを使用するかは認可サーバーの実装に依存します。
OpenID Connect Core 1.0 では、acr クレームが必須として要求されない限り、指定された ACR を満たせなくてもエラーにはなりません。
しかし、OAuth 2.0 Step Up Authentication Challenge Protocol では、acr_values リクエストパラメータによる ACR の要求(通常 acr クレームは任意扱い)は 「必須として扱われるべき」 であり、指定された ACR が満たされない場合は unmet_authentication_requirements エラーを返すことを推奨しています。
この推奨は、OpenID Connect Core 1.0 からの以下の抜粋とわずかに矛盾します。
ユーザーがクレームの公開を許可しなかった場合や、クレームが存在しない場合であっても、特定のクレームの説明で特に指定されていない限り、認可サーバーはクレームが返されないことによってエラーを生成 してはならない。
しかし、実際の運用上は、このわずかな矛盾が大きな問題になることはないでしょう。
OpenID Connect Core 1.0 では、具体的な ACR 値の定義はされていません。そのため、ACR を利用するためには、実装ごとに ACR 値を定義する必要があります。以下の表は、実際の ACR 値の例を示しています。
| デプロイメント | ACR 値 |
|---|---|
| UK Open Banking | urn:openbanking:psd2:ca urn:openbanking:psd2:sca |
| AU CDR | urn:cds:au:cdr:2 urn:cds:au:cdr:3 |
| Open Banking Brasil | urn:brasil:openbanking:loa2 urn:brasil:openbanking:loa3 |
認可サーバーは、サポートしている ACR 値について、そのディスカバリードキュメントを通じて広告すべきです。OpenID Connect Discovery 1.0 では、acr_values_supported サーバーメタデータが次のように定義されています。
acr_values_supported:任意(OPTIONAL)。この OP がサポートする認証コンテキストクラスリファレンスのリストを含む JSON 配列。
OAuth 2.0 ステップアップ認証チャレンジプロトコルを利用する保護リソースのエンドポイントは、
提示されたアクセストークンが認証要件(ACR または Max Age のいずれか、または両方)を満たさない場合、エラーレスポンスを返します。
以下の図は、認証要件を満たさない場合のエラーレスポンスから、
認証要件を満たすアクセストークンを取得するための認可リクエストを送信するまでの流れを示しています。
エラーレスポンス内のパラメータについて、以下のセクションで説明します。
insufficient_user_authentication エラーコード認証要件が満たされていないことを示すために、
本仕様では insufficient_user_authentication というエラーコードを次のように定義しています。
insufficient_user_authentication:このエラーコードは、エラーレスポンスの WWW-Authenticate HTTP ヘッダー内に次のように含められます。
WWW-Authenticate: Bearer error="insufficient_user_authentication",...
acr_values パラメータアクセストークンが満たすべき ACR のリストを指定するために、
本仕様では acr_values パラメータを次のように定義しています。
acr_values:このパラメータは、エラーコード insufficient_user_authentication とともに次のように使用されます(仕様の抜粋)。
HTTP/1.1 401 Unauthorized WWW-Authenticate: Bearer error="insufficient_user_authentication", error_description="A different authentication level is required", acr_values="myACR"
クライアントアプリケーションが上記のエラーレスポンスを受信した場合、
認可リクエストの claims パラメータ内で acr クレームを 必須(essential) として指定し、
myACR を values に含める必要があります。
以下に、その認可リクエストの例を示します。
(改行は見やすくするためのもので、実際のリクエストには含まれません。)
https://as.example.com/authorize?claims={
"id_token": {
"acr": {
"essential": true,
"values": [ "myACR" ]
}
}
}&...
max_age パラメータアクセストークンが満たすべき最大認証時間 を指定するために、
本仕様では max_age パラメータを次のように定義しています。
max_age:このパラメータは、エラーコード insufficient_user_authentication とともに次のように使用されます(仕様の抜粋)。
HTTP/1.1 401 Unauthorized WWW-Authenticate: Bearer error="insufficient_user_authentication", error_description="More recent authentication is required", max_age="5"
クライアントアプリケーションが上記のエラーレスポンスを受信した場合、
prompt リクエストパラメータに login を指定した認可リクエストを送信する必要があります。
保護リソースのエンドポイントが、提示されたアクセストークンが認証要件を満たしているかどうかを確認できるようにするためには、
アクセストークンに、発行時に行われたユーザー認証の情報を含める必要があります。
OAuth 2.0 ステップアップ認証チャレンジプロトコルの仕様では、
以下の 2 つの属性をアクセストークンに持たせることを定義しています。
これらの属性は、JWT アクセストークンのペイロード部分や、
RFC 7662 に基づくトークンイントロスペクションのレスポンスに含めることができます。
| 属性 | 説明(仕様より抜粋) |
|---|---|
acr |
認証コンテキストクラスリファレンス。ユーザー認証の際に適用された認証コンテキストクラスを識別するための文字列。 |
auth_time |
ユーザー認証が行われた時刻。JSON の数値で表され、1970-01-01T00:00:00Z UTC からの秒数で指定される。 |
以下は、仕様から抜粋した JWT アクセストークンのペイロード部分の例です。
{
"active": true,
"client_id": "s6BhdRkqt3",
"scope": "purchase",
"sub": "someone@example.net",
"aud": "https://rs.example.com",
"iss": "https://as.example.net",
"exp": 1639528912,
"iat": 1618354090,
"auth_time": 1646340198,
"acr": "myACR"
}
Authlete は、バージョン 2.3 以降で OAuth 2.0 ステップアップ認証チャレンジプロトコルの仕様をサポートしています。
Authlete の /auth/authorization/issue API では、
acr リクエストパラメータと authTime リクエストパラメータを受け取ることができます。
これらの値は、発行される ID トークン の acr クレームおよび auth_time クレームの値として使用されます。
また、Authlete 2.3 以降では、アクセストークンにもこれらの情報がバインドされます。
以下のシェルコマンドのセットは、OAuth 2.0 認可コードフロー (RFC 6749 セクション 4.1) をシミュレートし、
ACR (acr) および認証時刻 (auth_time) を含むアクセストークンを発行するものです。
/auth/authorization API の呼び出し認可リクエストを parameters に含めて /auth/authorization API を呼び出し、
そのリクエストの情報を JSON 形式で取得します。
curl -v -X POST https://{api-cluster}.authlete.com/api/{serviceId}/auth/authorization \
-H 'Content-Type: application/json' \
-u 'Authorization: Bearer <Service Access Token>' \
--data-urlencode parameters="response_type=code&scope=openid&client_id=${CLIENT_ID}&redirect_uri=${REDIRECT_URI}&state=${STATE}&claims={\"id_token\":{\"acr\":{\"essential\":true,\"values\":[\"${ACR}\"]}}}" \
> authorization_response.json
/auth/authorization/issue API の呼び出し取得した ticket を用いて /auth/authorization/issue API を呼び出し、
認可コード、アクセストークン、ID トークンを発行します。
$ TICKET=`jq -r .ticket authorization_response.json`
$ AUTH_TIME=`date +%s`
$ curl -v -X POST https://{api-cluster}.authlete.com/api/{serviceId}/auth/authorization/issue \
-H 'Content-Type: application/json' \
-u 'Authorization: Bearer <Service Access Token>' \
-d ticket=$TICKET -d subject=$SUBJECT -d authTime=$AUTH_TIME -d acr=$ACR \
> authorization_issue_response.json
/auth/token API の呼び出し取得した認可コードを使用して /auth/token API を呼び出し、
アクセストークン(および必要に応じて ID トークン)を取得します。
$ AUTHORIZATION_CODE=`jq -r .authorizationCode authorization_issue_response.json`
$ curl -v -X POST https://{api-cluster}.authlete.com/api/{serviceId}/auth/token \
-H 'Content-Type: application/json' \
-u 'Authorization: Bearer <Service Access Token>' \
--data-urlencode parameters="client_id=${CLIENT_ID}&grant_type=authorization_code&code=${AUTHORIZATION_CODE}&redirect_uri=${REDIRECT_URI}" \
-d clientId=${CLIENT_ID} -d clientSecret=${CLIENT_SECRET} \
> token_response.json
認可エンドポイントの実装では、/auth/authorization/issue API を呼び出す代わりに、
/auth/authorization/fail API を呼び出して認可リクエストの失敗を通知することもできます。
/auth/authorization/fail API の呼び出しこの API では、reason リクエストパラメータを指定して、
エラーの理由を通知する必要があります(AuthorizationFailRequest 参照)。
認証要件が満たされない場合、
reason パラメータの値として <a href="https://authlete.github.io/authlete-java-common/com/authlete/common/dto/AuthorizationFailRequest.Reason.html#ACR_NOT_SATISFIED">ACR_NOT_SATISFIED</a> を指定すると、
Authlete 2.3 以降では、error 値が unmet_authentication_requirements となるエラーレスポンスを生成します。
curl -v -X POST https://{api-cluster}.authlete.com/api/{serviceId}/auth/authorization/fail \
-H 'Content-Type: application/json' \
-u 'Authorization: Bearer <Service Access Token>' \
-d ticket=$TICKET -d reason=ACR_NOT_SATISFIED \
> authorization_fail_response.json
この API のレスポンスには responseContent パラメータが含まれ、
この値は、エラーコードや追加情報を含むリダイレクト URL になります(AuthorizationFailResponse 参照)。
$ jq -r .responseContent authorization_fail_response.json
http://localhost:4000/api/mock/redirection/4803170471?error=unmet_authentication_requirements&error_description=%5BA060305%5D+The+authorization+request+requests+%27acr%27+as+essential%2C+but+the+authentication+performed+for+the+end-user+satisfies+none+of+the+requested+ACRs.&error_uri=https%3A%2F%2Fdocs.authlete.com%2F%23A060305&state=917bdc1ef38ba6f6c297b4e31ac84007&iss=https%3A%2F%2Fauthlete.com
URL に error_description パラメータが含まれていますが、
サービスの errorDescriptionOmitted 設定が true の場合は省略されます
(Service.isErrorDescriptionOmitted() 参照)。
Authlete では、アクセストークンの形式を設定可能です。
Service の accessTokenSignAlg プロパティが設定されている場合、
アクセストークンの形式は JWT となります
(詳細は JWT ベースのアクセストークンの有効化 を参照)。
Authlete 2.3 以降では、OAuth 2.0 ステップアップ認証チャレンジプロトコルの仕様に基づき、
JWT アクセストークンのペイロードに acr クレームと auth_time クレームを埋め込みます。
サービスが JWT アクセストークンを生成する設定になっている場合、
/auth/token API のレスポンスの jwtAccessToken パラメータに JWT アクセストークンが含まれます
(TokenResponse 参照)。
$ JWT_AT=`jq -r .jwtAccessToken token_response.json`
JWT アクセストークンのペイロード部分をデコードすると、
acr クレームおよび auth_time クレームが埋め込まれていることが確認できます。
$ echo $JWT_AT | ruby -paF\\. -rbase64 -e '$_=Base64.urlsafe_decode64 $F[1]' | jq .
出力例:
{
"sub": "taka",
"acr": "acr1",
"scope": "openid",
"auth_time": 1667321595,
"iss": "https://authlete.com",
"exp": 1667408271,
"iat": 1667321871,
"client_id": "5575687621",
"jti": "A1pURs-TMuyvueCoP2mSsntQX4-0IvYxlvectI4E2h8"
}
Authlete を利用する認可サーバーは、
RFC 7662 に準拠したトークンイントロスペクションエンドポイントを
/auth/introspection/standard API を用いて実装できます。
保護リソースのエンドポイントは、このトークンイントロスペクションエンドポイントを使用して、
アクセストークンの詳細情報を取得できます。
/auth/introspection/standard API の呼び出しこの API は、parameters にトークン情報を含めてリクエストを送信し、
イントロスペクション結果を JSON 形式で返します
(StandardIntrospectionResponse 参照)。
$ AT=`jq -r .accessToken token_response.json`
$ curl -v -X POST https://{api-cluster}.authlete.com/api/{serviceId}/auth/introspection/standard \
-H "Content-Type:application/json" \
-u 'Authorization: Bearer <Service Access Token>' \
--data-urlencode parameters="token=${AT}" \
> introspection_standard_response.json
レスポンスには responseContent パラメータが含まれます。
この値は、RFC 7662 に準拠した JSON 文字列です。
以下のコマンドを実行すると、レスポンスの responseContent の値を解析できます。
$ jq -r .responseContent introspection_standard_response.json | jq .
出力例:
{
"sub": "taka",
"acr": "acr1",
"scope": "openid",
"auth_time": 1667321595,
"iss": "https://authlete.com",
"active": true,
"token_type": "Bearer",
"exp": 1667408271,
"client_id": "5575687621"
}
Authlete は、RFC 7662 に準拠した標準のイントロスペクションエンドポイントとは別に、
独自の /auth/introspection API も提供しています。
この API を使用すると、より高度なトークン検証を行うことができます。
/auth/introspection API の利点/auth/introspection API は、標準のイントロスペクションエンドポイントと比較して、以下のような利点があります。
/auth/introspection API のリクエストパラメータこの API は、以下のリクエストパラメータを受け付けます
(IntrospectionRequest 参照)。
| パラメータ | カテゴリ | バージョン | 説明 |
|---|---|---|---|
token |
共通 | 1.1 | イントロスペクション対象のアクセストークン |
scopes |
共通 | 1.1 | アクセストークンがカバーすべきスコープ |
subject |
共通 | 1.1 | アクセストークンに関連付けられるべきユーザー識別子 |
acrValues |
ステップアップ認証 | 2.3 | アクセストークンが満たすべき ACR 値のリスト |
maxAge |
ステップアップ認証 | 2.3 | 許容される最大認証時間 (秒単位) |
acrValues リクエストパラメータを指定すると、
/auth/introspection API は、アクセストークンにバインドされた ACR 値が acrValues 配列内に含まれているかどうかを検証します。
指定された ACR を満たさない場合、本仕様に準拠したエラーレスポンスを生成します。
以下のコマンドを実行すると、acrValues を指定した ACR 検証を行うことができます。
$ curl -v -X POST https://{api-cluster}.authlete.com/api/{serviceId}/auth/introspection \
-H 'Content-Type:application/json' \
-u 'Authorization: Bearer <Service Access Token>' \
-d token=$AT -d acrValues="acrX acrY" \
> introspection_response-acrValues.json
レスポンスの responseContent パラメータには、
WWW-Authenticate HTTP ヘッダーの値として使用すべき文字列が含まれます
(IntrospectionResponse 参照)。
$ jq -r .responseContent introspection_response-acrValues.json
出力例:
Bearer error="insufficient_user_authentication",
error_description="[A341302] The authentication context class 'acr1' of the user authentication that the authorization server performed during the course of issuing the access token is insufficient. User authentication of an access token must satisfy one of [acrX acrY] to access the protected resource.",
error_uri="https://docs.authlete.com/#A341302",
acr_values="acrX acrY"
maxAge リクエストパラメータを指定すると、
/auth/introspection API は、アクセストークンの auth_time からの経過時間が maxAge を超えていないかどうかを検証します。
指定された maxAge を超えている場合、本仕様に準拠したエラーレスポンスを生成します。
以下のコマンドを実行すると、最大認証時間 を指定した認証検証を行うことができます。
$ curl -v -X POST https://{api-cluster}.authlete.com/api/{serviceId}/auth/introspection \
-H 'Content-Type:application/json' \
-u 'Authorization: Bearer <Service Access Token>' \
-d token=$AT -d maxAge=600 \
> introspection_response-maxAge.json
レスポンスの responseContent パラメータには、
WWW-Authenticate HTTP ヘッダーの値として使用すべき文字列が含まれます。
$ jq -r .responseContent introspection_response-maxAge.json
出力例:
Bearer error="insufficient_user_authentication",
error_description="[A340301] The time of the user authentication that the authorization server performed during the course of issuing the access token is too old, so re-authentication is needed. The maximum authentication age (max_age) required by the protected resource is 600.",
error_uri="https://docs.authlete.com/#A340301",
max_age="600"
Authlete 3.0 は 2024 年 11 月にリリースされました
(リリース発表 参照)。
このバージョンでは、OAuth 2.0 ステップアップ認証チャレンジプロトコルが正式にサポートされています。
ぜひ、最大限に活用してください!