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 ステップアップ認証チャレンジプロトコルが正式にサポートされています。
ぜひ、最大限に活用してください!