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

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

概要

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

FAPI 2.0 Security Profile の機能は Authlete 2.3 以降で利用可能です。

はじめに

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

fapi2-baseline-diagram
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 sp

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

fapi2-sp-scope-attributes-ja
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 クレーム 必須 を選択。
オーディエンス確認 実行する を選択。
アクセストークン署名アルゴリズム 注意:この設定は JWT 形式のアクセストークンを発される場合のみ必要。PS256ES256EdDSA のいずれかを選択。
サポートするスコープ FAPI 2.0 Security Profile スコープを作成。
JWK セットの内容 注意:この設定は “JWK セットの内容” を利用する場合のみ必要。必要な JWK (e.g. JWT アクセストークンの署名用鍵) を含む JWK セットを設定。
JWK セットエンドポイントの URI 注意:この設定は “JWK セットエンドポイントの URI” を利用する場合のみ必要。必要な JWK (e.g. JWT アクセストークンの署名用鍵) を含む JWK セットを指す URI を設定。https で始まる URI を設定すること。

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

クライアントを FAPI 2.0 Security Profile 準拠にするためには、以下のよう設定にしてください。

プロパティ 説明
クライアントタイプ CONFIDENTIAL を選択。
認可種別 AUTHORIZATION_CODE 含める。
応答種別 CODE を含める。
リダイレクト URI リダイレクト URI を少なくとも一つ作成。
クライアント認証方式 PRIVATE_KEY_JWT を選択。
アサーション署名アルゴリズム PS256ES256EdDSA のいずれかを選択。
ID トークン署名アルゴリズム 注意:この設定は署名された ID トークンが発行される場合のみ必要。PS256ES256EdDSA のいずれかを選択。
ID トークン暗号化アルゴリズム 注意:この設定は暗号化された ID トークンが発行される場合のみ必要。RSA1_5 以外を選択。
JWK セットの内容 注意:この設定は “JWK セットの内容” を利用する場合のみ必要。必要な JWK (e.g. クライアントアサーションの署名検証用鍵) を含む JWK セットを設定。
JWK セットの URI 注意:この設定は “JWK セットの URI” を利用する場合のみ必要。必要な JWK (e.g. クライアントアサーションの署名検証用鍵) を含む JWK セットを指す URI を設定。https で始まる URI を設定すること。

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:ietf:params:oauth:client-assertion-type:jwt-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: eyJ0eXAiOiJkcG9wK2p3dCIsImFsZyI6IkVTMjU2IiwiandrIjp7Imt0eSI6IkVDIiwidXNlIjoic2lnIiwiY3J2IjoiUC0yNTYiLCJ4IjoiRk4xYk81cnBVY0ZNeU9ncE9iQ1BTaGZkcldJ
TFN1RVBBTmZKUWl4cU16ayIsInkiOiJYY3RYc0xXY1YzMkJMVVZCMDhqVjJ4d2VFU2FTY0xULVNKUEl6TlVoSHRvIiwiYWxnIjoiRVMyNTYifX0.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 をリクエストに含めなければならない (DPoP proof JWT は PS256、ES256、EdDSA のいずれかで署名されている必要がある)

ことに注意してください。
その後、認可サーバーは 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=h...

/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 7i9xPkbkKCmcmkZIvSGci5PKf4HO9TUAefwsABMrbTQDPoP: 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",
  ...
}