FAPI 2.0 Security Profile における認可コードフロー

概要

本ドキュメントでは、FAPI 2.0 Security Profile で規定される認可コードフローの概要について解説します。また、それに準拠するためのサービス・クライアントの設定方法についても解説します。


はじめに

FAPI 2.0 Security Profile で規定される認可コードフローは以下のようになります。
Authorization Code Flow in FAPI 2.0 Security Profile

1. Pushed Authorization リクエスト

最初に、クライアントは認可サーバーの pushed authorization request エンドポイントに対してリクエストを送信します。これにより、送信された認可リクエストパラメーター群が認可サーバーに登録されます。FAPI 2.0 Security Profile の要請により、認可リクエストパラメーターにはいくつかの要求事項が課されます (response_type=code 等)。また、クライアント認証方法についても mutual TLS authentication または private_key_jwt のいずれかを用いることが要求されます。本ドキュメントではクライアント認証方法として private_key_jwt を用いることに注意してください。

2. Pushed Authorization レスポンス

リクエストが正常に処理された場合、pushed authorization request エンドポイントはリクエスト URI (request_uri) を含むレスポンスを返却します。

3. 認可リクエスト

クライアントはリクエスト URI (request_uri) を含む認可リクエストを認可サーバーの認可エンドポイントに送信します。このステップはユーザーエージェントを通じて行われます。 

4. 認可レスポンス

リクエストが正常に処理された場合、認可エンドポイントから認可コードを含むレスポンスが返却されます。これを受けて、ユーザーエージェントはクライアントのリダイレクト URI へリダイレクトされます。

5. トークンリクエスト

クライアントは認可コードを含めてトークンエンドポイントへリクエストを送信します。FAPI 2.0 Security Profile による要請から、トークンエンドポイントは mutual TLS または DPoP によって送信者制限されたアクセストークンを発行する必要があります。本ドキュメントでは送信者制限のメカニズムとして DPoP を利用することにします。また、pushed authorization request エンドポイントと同様にトークンエンドポイントでも private_key_jwt によってクライアント認証を行うことに注意してください。

6. トークンレスポンス

リクエストが正常に処理された場合、トークンエンドポイントはアクセストークンを含むレスポンスを返却します。

7. API リクエスト

クライアントは発行されたアクセストークンとその所有者証明 (本ドキュメントの場合、DPoP proof JWT) を利用して、リソースエンドポイントへアクセスします。

FAPI 2.0 Security Profile スコープ

Authlete 2.3 以降、FAPI 2.0 Security Profile のために新たなスコープ属性が導入されました。属性の定義は以下の通りです。
属性のキー
属性の値
fapi2
baseline

以後、本ドキュメントでは、このスコープ属性に紐づけられたスコープを FAPI 2.0 Security Profile スコープと呼ぶことにします。以下は、FAPI 2.0 Security Profile スコープの一例です。
FAPI 2.0 Security Profile スコープの一例.

サービスを FAPI 2.0 Security Profile 準拠に設定する方法

サービスを FAPI 2.0 Security Profile 準拠にするためには、以下のよう設定にしてください。
プロパティ
説明
サポートする認可種別
AUTHORIZATION_CODE 含める。
サポートする応答種別
CODE を含める。
サポートするプロフィール群
FAPI 含める。
iss レスポンスパラメーター
含めるを選択。
トークンエンドポイントの URI
認可サーバーのトークンエンドポイントの URI を設定。
サポートするクライアント認証方式
PRIVATE_KEY_JWT を選択。
nbf クレーム
必須を選択。
Audience Validation
Perform を選択。
サポートするスコープ
FAPI 2.0 Security Profile スコープを作成。

クライアントを FAPI 2.0 Security Profile 準拠に設定する方法

クライアントを FAPI 2.0 Security Profile 準拠にするためには、以下のよう設定にしてください。
Property
Description
クライアントタイプ
CONFIDENTIAL を選択。
認可種別
AUTHORIZATION_CODE 含める。
応答種別
CODE を含める。
リダイレクト URI
リダイレクト URI を少なくとも一つ作成。
クライアント認証方式
PRIVATE_KEY_JWT を選択。
アサーション署名アルゴリズム
NONE 以外を選択。
ID トークン署名アルゴリズム
注意:この設定は署名された ID トークンが発行される場合のみ必要。

NONE 以外を選択。
ID トークン暗号化アルゴリズム
注意:この設定は暗号化された ID トークンが発行される場合のみ必要。

RSA1_5 以外を選択。
JWK セットの内容
クライアントアサーションの署名を検証する際に必要な公開鍵を含む JWK セットを設定。

API コールテスト

このセクションでは、FAPI 2.0 Security Profile で規定される認可コードフローにおいて、認可サーバーから Authlete API に対して送信される API コールを実際にシミュレートします。

1. /pushed_auth_req API

FAPI 2.0 Security Profile で規定される認可コードフローにおいて、クライアントが認可サーバーの pushed authorization request エンドポイントに対して正常なリクエストを送信したとします。FAPI 2.0 Security Profile, Requirements for Clients における要求事項により、クライアントから認可サーバーへのリクエストは以下のようになります。

POST /par HTTP/1.1
Host: server.example.com
Content-Type: application/x-www-form-urlencoded

client_assertion_type=urn%3Aietf%3Aparams%3Aoauth%3Aclient-assertion-type%3Ajwt-bearer
&client_assertion=eyJraWQiOiJjbGllbnQtYXNzZXJ0aW9uLWtleSIsImFsZyI6IkVTMjU2In0.eyJqdGkiOiIyZTQ3MzhkMjNjOWM5YjYyZGNjZCIsInN1YiI6IjEzNDQ2NzM2NDE4IiwiaXNzIjoiMTM0NDY3MzY0MTgiLCJhdWQiOiJodHRwczovL2F1dGhsZXRlLmNvbSIsImlhdCI6MTY1OTk1MDIwNywiZXhwIjoxNjU5OTUzMjA3fQ.VdPzRZ42v62Q2gUfZ-r6aGUL27pY4y5uUkJZRWU9taDHN8z7GtqyyhmmDLfmSuGKglvhmuQvQuv6nJeuQZk5wQ
&client_id=13446736418
&response_type=code
&redirect_uri=https%3A%2F%2Fclient.example.org%2Fcb
&scope=myscope
&code_challenge=E9Melhoa2OwvFrEMTJguCHaoeK1t8URWbuGJSstw-cM
&code_challenge_method=S256

scope パラメーターに myscope (FAPI 2.0 Security Profile スコープ) が指定されていることに注意してください。また、pushed authorization request エンドポイントにおけるクライアント認証方式は private_key_jwt であることに注意してください。

認可サーバーがクライアントからリクエストを受けた後、認可サーバーは Authlete の /pushed_auth_req API をコールします。以下の curl コマンドは、認可サーバーから Authlete の /pushed_auth_req API に対するリクエストをシミュレートしたものです。

curl -s -X POST https://api.authlete.com/api/pushed_auth_req \
-u '20699248885:VR6lv6BbPx04p7D7vcx-7YM6OrHVLUQitahPyma6API' \
-H 'Content-type: application/json' \
-d '{"parameters":"client_assertion_type=urn%3Aietf%3Aparams%3Aoauth%3Aclient-assertion-type%3Ajwt-bearer&client_assertion=eyJraWQiOiJjbGllbnQtYXNzZXJ0aW9uLWtleSIsImFsZyI6IkVTMjU2In0.eyJqdGkiOiJmNDE2ODVlZWY0ZWYzOGJjMzkwNiIsInN1YiI6IjEzNDQ2NzM2NDE4IiwiaXNzIjoiMTM0NDY3MzY0MTgiLCJhdWQiOiJodHRwczovL2F1dGhsZXRlLmNvbSIsImlhdCI6MTY2MDE0Mjc2NCwiZXhwIjoxNjYwMTQ1NzY0fQ.vagMXCXtrM9cai6ZID6Y3SyCw6FixAY2MGWkzx2-inwKcxpP1KHDX9iSiDiK75vF0cItwxGAALtcdfyoxWaw3A&client_id=13446736418&response_type=code&redirect_uri=https%3A%2F%2Fclient.example.com%2Fcb&scope=myscope&code_challenge=E9Melhoa2OwvFrEMTJguCHaoeK1t8URWbuGJSstw-cM&code_challenge_method=S256"}'

/pushed_auth_req API からの正常レスポンスは以下のようになります。

{
  "type": "pushedAuthReqResponse",
  "resultCode": "A245001",
  "resultMessage": "[A245001] Successfully registered a request object for client (13446736418), URI is urn:ietf:params:oauth:request_uri:QbGjdHSfZi_m6W_ldrqjZhmYjQo3QcQOd3Hx2RcGODg.",
  "action": "CREATED",
  "requestUri": "urn:ietf:params:oauth:request_uri:QbGjdHSfZi_m6W_ldrqjZhmYjQo3QcQOd3Hx2RcGODg",
  "responseContent": "{\"expires_in\":600,\"request_uri\":\"urn:ietf:params:oauth:request_uri:QbGjdHSfZi_m6W_ldrqjZhmYjQo3QcQOd3Hx2RcGODg\"}"
}

2. /auth/authorization API

pushed authorization request エンドポイントからリクエスト URI が発行された後、クライアントは認可サーバーの認可エンドポイントに対して当該リクエスト URI を含めた認可リクエストを送信します。 以下はリクエストの例です。

GET /authorization?client_id=13446736418&request_uri=urn%3Aietf%3Aparams%3Aoauth%3Arequest_uri%3AQbGjdHSfZi_m6W_ldrqjZhmYjQo3QcQOd3Hx2RcGODg HTTP/1.1
Host: server.example.com

クライアントから認可リクエストを受けた後、認可サーバーは Authlete の /auth/authorization API をコールします。以下の curl コマンドは、認可サーバーから Authlete の /auth/authorization API に対するリクエストをシミュレートしたものです。

curl -s -X POST https://api.authlete.com/api/auth/authorization \
-u '20699248885:VR6lv6BbPx04p7D7vcx-7YM6OrHVLUQitahPyma6API' \
-H 'Content-type: application/json' \
-d '{"parameters":"client_id=13446736418&request_uri=urn%3Aietf%3Aparams%3Aoauth%3Arequest_uri%3AQbGjdHSfZi_m6W_ldrqjZhmYjQo3QcQOd3Hx2RcGODg"}'

/auth/authorization API からの正常レスポンスは以下のようになります。

{
  "type": "authorizationResponse",
  "resultCode": "A004001",
  "resultMessage": "[A004001] Authlete has successfully issued a ticket to the service (API Key = 20699248885) for the authorization request from the client (ID = 13446736418). [response_type=code, openid=false]",
  ...
  "ticket": "CBKnPeMOf9xfv0sV-srVzbZv_dtFh01Zc0VkQ2nQKFg"
}

3. /auth/authorization/issue API

認可サーバーが /auth/authorization API から正常レスポンスを受けた後、エンドユーザーはブラウザ上でクライアントを認可/拒否し、その結果が認可サーバーに伝達されます。それを受けて、認可サーバーは Authlete の /auth/authorization/issue API をコールします。以下の curl コマンドは、認可サーバーから Authlete の /auth/authorization/issue API に対するリクエストをシミュレートしたものです。

curl -s -X POST https://api.authlete.com/api/auth/authorization/issue \
-u '20699248885:VR6lv6BbPx04p7D7vcx-7YM6OrHVLUQitahPyma6API' \
-H 'Content-type: application/json' \
-d '{"ticket":"CBKnPeMOf9xfv0sV-srVzbZv_dtFh01Zc0VkQ2nQKFg","subject":"john","result":"AUTHORIZED"}'

/auth/authorization/issue API からの正常レスポンスは以下のようになります。

{
  "type": "authorizationIssueResponse",
  "resultCode": "A040001",
  "resultMessage": "[A040001] The authorization request was processed successfully.",
  "authorizationCode": "xY1Vd-rjb-Yj84sXGgkr424VR9JbVGQw2wgVbudSXGU",
  ...
}

4. /auth/token API

認可エンドポイントから認可コードを含むレスポンスを受けた後、クライアントは認可サーバーのトークンエンドポイントに対して以下のようなリクエストを送ります。

POST /token HTTP/1.1
Host: server.example.com
Content-Type: application/x-www-form-urlencoded
DPoP: eyJ0eXAiOiJkcG9wK2p3dCIsImFsZyI6IkVTMjU2IiwiandrIjp7Imt0eSI6IkVDIiwidXNlIjoic2lnIiwiY3J2IjoiUC0yNTYiLCJ4IjoiRk4xYk81cnBVY0ZNeU9ncE9iQ1BTaGZkcldJTFN1RVBBTmZKUWl4cU16ayIsInkiOiJYY3RYc0xXY1YzMkJMVVZCMDhqVjJ4d2VFU2FTY0xULVNKUEl6TlVoSHRvIiwiYWxnIjoiRVMyNTYifX0.eyJqdGkiOiI0OWE1YWU0ZTA5NTZiNDJkMjEwMyIsImh0bSI6IlBPU1QiLCJodHUiOiJodHRwczovL3NlcnZlci5leGFtcGxlLmNvbS90b2tlbiIsImlhdCI6MTY2MDE0Mjk2N30.codEOIUYuytFacobXDn4-Jx7xr6oRaw83lorgPbOS1y5pMyDCfUf08SA9tCUWMMUctgGdt7D9wf7hDCip6lgTA

client_assertion_type=urn%3Aietf%3Aparams%3Aoauth%3Aclient-assertion-type%3Ajwt-bearer
&client_assertion=eyJraWQiOiJjbGllbnQtYXNzZXJ0aW9uLWtleSIsImFsZyI6IkVTMjU2In0.eyJqdGkiOiJmYzRkMDRlMTI0MTRjNzU5OGNlNyIsInN1YiI6IjEzNDQ2NzM2NDE4IiwiaXNzIjoiMTM0NDY3MzY0MTgiLCJhdWQiOiJodHRwczovL2F1dGhsZXRlLmNvbSIsImlhdCI6MTY2MDE0Mjk2NCwiZXhwIjoxNjYwMTQ1OTY0fQ.-Gn4X3d_ymujjhSh8ZLWdtOzq2-TcgvwGzU-NEjVDaJSLN8kpOVHguOytSDXqJW5QJcsH8J_bkTaVZV7Nyl2IA
&client_id=13446736418
&code=xY1Vd-rjb-Yj84sXGgkr424VR9JbVGQw2wgVbudSXGU
&redirect_uri=https%3A%2F%2Fclient.example.org%2Fcb
&grant_type=authorization_code
&code_verifier=dBjftJeZ4CVP-mB92K27uhbUJU1p1r_wW1gFWFOEjXk

クライアントが
  • private_key_jwt によって認証される
  • DPoP proof JWT をリクエストに含めなければならない
ことに注意してください。

その後、認可サーバーは Authlete の /auth/token API をコールします。以下の curl コマンドは、認可サーバーから Authlete の /auth/token API に対するリクエストをシミュレートしたものです。

curl -s -X POST https://api.authlete.com/auth/token \
-u '20699248885:VR6lv6BbPx04p7D7vcx-7YM6OrHVLUQitahPyma6API' \
-H 'Content-type: application/json' \
-d {"parameters":"client_assertion_type=urn:ietf:params:oauth:client-assertion-type:jwt-bearer&client_assertion=eyJraWQiOiJjbGllbnQtYXNzZXJ0aW9uLWtleSIsImFsZyI6IkVTMjU2In0.eyJqdGkiOiJmYzRkMDRlMTI0MTRjNzU5OGNlNyIsInN1YiI6IjEzNDQ2NzM2NDE4IiwiaXNzIjoiMTM0NDY3MzY0MTgiLCJhdWQiOiJodHRwczovL2F1dGhsZXRlLmNvbSIsImlhdCI6MTY2MDE0Mjk2NCwiZXhwIjoxNjYwMTQ1OTY0fQ.-Gn4X3d_ymujjhSh8ZLWdtOzq2-TcgvwGzU-NEjVDaJSLN8kpOVHguOytSDXqJW5QJcsH8J_bkTaVZV7Nyl2IA&client_id=13446736418&code=xY1Vd-rjb-Yj84sXGgkr424VR9JbVGQw2wgVbudSXGU&redirect_uri=https%3A%2F%2Fclient.example.org%2Fcb&grant_type=authorization_code&code_verifier=dBjftJeZ4CVP-mB92K27uhbUJU1p1r_wW1gFWFOEjXk","dpop":"eyJ0eXAiOiJkcG9wK2p3dCIsImFsZyI6IkVTMjU2IiwiandrIjp7Imt0eSI6IkVDIiwidXNlIjoic2lnIiwiY3J2IjoiUC0yNTYiLCJ4IjoiRk4xYk81cnBVY0ZNeU9ncE9iQ1BTaGZkcldJTFN1RVBBTmZKUWl4cU16ayIsInkiOiJYY3RYc0xXY1YzMkJMVVZCMDhqVjJ4d2VFU2FTY0xULVNKUEl6TlVoSHRvIiwiYWxnIjoiRVMyNTYifX0.eyJqdGkiOiI0OWE1YWU0ZTA5NTZiNDJkMjEwMyIsImh0bSI6IlBPU1QiLCJodHUiOiJodHRwczovL3NlcnZlci5leGFtcGxlLmNvbS90b2tlbiIsImlhdCI6MTY2MDE0Mjk2N30.codEOIUYuytFacobXDn4-Jx7xr6oRaw83lorgPbOS1y5pMyDCfUf08SA9tCUWMMUctgGdt7D9wf7hDCip6lgTA"}'

/auth/token API からの正常レスポンスは以下のようになります。

{
  "type": "tokenResponse",
  "resultCode": "A050001",
  "resultMessage": "[A050001] The token request (grant_type=authorization_code) was processed successfully.",
  "accessToken": "7i9xPkbkKCmcmkZIvSGci5PKf4HO9TUAefwsABMrbTQ",
  ...
}

5. /auth/introspection API

以上のステップを経て、認可サーバーはクライアントに対してアクセストークンを発行します。クライアントはそのアクセストークンを利用して、リソースサーバーのエンドポイントにアクセスします。以下はリクエストの例です。

GET /api/sample?client_id=13446736418&request_uri=urn%3Aietf%3Aparams%3Aoauth%3Arequest_uri%3AQbGjdHSfZi_m6W_ldrqjZhmYjQo3QcQOd3Hx2RcGODg HTTP/1.1
Authorization: DPoP 7i9xPkbkKCmcmkZIvSGci5PKf4HO9TUAefwsABMrbTQ
DPoP: eyJ0eXAiOiJkcG9wK2p3dCIsImFsZyI6IkVTMjU2IiwiandrIjp7Imt0eSI6IkVDIiwidXNlIjoic2lnIiwiY3J2IjoiUC0yNTYiLCJ4IjoiRk4xYk81cnBVY0ZNeU9ncE9iQ1BTaGZkcldJTFN1RVBBTmZKUWl4cU16ayIsInkiOiJYY3RYc0xXY1YzMkJMVVZCMDhqVjJ4d2VFU2FTY0xULVNKUEl6TlVoSHRvIiwiYWxnIjoiRVMyNTYifX0.eyJqdGkiOiI5YWMwYTRjYWM1ODA4MmZlYzQxOSIsImh0bSI6IkdFVCIsImh0dSI6Imh0dHBzOi8vcmVzb3VyY2UuZXhhbXBsZS5jb20vYXBpL3NhbXBsZSIsImlhdCI6MTY2MDE0MzAzMiwiYXRoIjoiaDdacFQ2VnJCU3JpaGdYSDA1ZE5tM3VvQ0x5TmVXYWNfZHFkOUhOOUktbyJ9.avJW2u8Gasm2wpXi4XlxJ2Z4A4WsgXm5jFscbN893vRzcxqZU4AwK0J5Tosk4T1d9WmL5wnZKnAEy2Gb9L2_pQ
Host: resource.example.com

DPoP リクエストヘッダーに DPoP proof JWT が含まれていることに注意してください。

リソースエンドポイントはクライアントからのリクエストを受けた後、Authlete の /auth/introspection API をコールし、アクセストークンの検証を行います。以下の curl コマンドは、リソースサーバーから Authlete の /auth/introspection API に対するリクエストをシミュレートしたものです。

curl -s -X POST https://api.authlete.com/api/auth/introspection \
-u '20699248885:VR6lv6BbPx04p7D7vcx-7YM6OrHVLUQitahPyma6API' \
-H 'Content-type: application/json' \
-d {"token":"7i9xPkbkKCmcmkZIvSGci5PKf4HO9TUAefwsABMrbTQ","dpop":"eyJ0eXAiOiJkcG9wK2p3dCIsImFsZyI6IkVTMjU2IiwiandrIjp7Imt0eSI6IkVDIiwidXNlIjoic2lnIiwiY3J2IjoiUC0yNTYiLCJ4IjoiRk4xYk81cnBVY0ZNeU9ncE9iQ1BTaGZkcldJTFN1RVBBTmZKUWl4cU16ayIsInkiOiJYY3RYc0xXY1YzMkJMVVZCMDhqVjJ4d2VFU2FTY0xULVNKUEl6TlVoSHRvIiwiYWxnIjoiRVMyNTYifX0.eyJqdGkiOiI5NmYzNDc4OWFjNWFmZDNkMzhhNiIsImh0bSI6IkdFVCIsImh0dSI6Imh0dHBzOi8vcmVzb3VyY2UuZXhhbXBsZS5jb20vYXBpL3NhbXBsZSIsImlhdCI6MTY1OTk1MDUyNywiYXRoIjoidmdDa3JDWEhIYTQ3VVpVazdJNVk4QmFKdWxWek9KMFhEMVNzZGdWdHVKSSJ9.ysI4w__vdqVPOjjVhUidhHoletEOmvSMJD6TdqUIM49KIGcjxjgVpaVA-D5yKnEs_4aRGkt_tsRiSsHUNam9KA","htm":"GET","htu":"https://resource.example.com/api/sample"}'

/auth/introspection API からの正常レスポンスは以下のようになります。

{
  "type": "introspectionResponse",
  "resultCode": "A056001",
  "resultMessage": "[A056001] The access token is valid.",
  "action": "OK",
  ...
}
How did we do with this article?