Financial-grade API (FAPI) Basics

はじめに

このドキュメントでは、Authlete を利用した Financial-grade API Security Profile 1.0 - Part 2: Advanced (以下 FAPI)対応のアイデンティティ・プロバイダーを構築する手順を通じて、同仕様が規定するセキュリティ条項の概要と Authlete の設定方法を紹介します。

手順の実施にあたっては、以下のチュートリアルの実施とナレッジベース記事の通読を行い、 OpenID Connect と Authlete に関する基本的な理解を有していることが必須です。

構成

本チュートリアルでは以下の構成を想定します。 なお、実サービスとして動作するのは、Authlete のコンソールと API だけです。

認可サーバー(OIDC アイデンティティプロバイダー)とリソースサーバーはいずれも実際には存在しませんが、 それぞれのサーバーがクライアント(OIDC リライングパーティ)から認可リクエストやトークンリクエスト、そしてトークンイントロスペクションリクエストを受信したときに、 どのような API リクエストを Authlete に行うかを、curl コマンドを用いて試行します。

チュートリアルにおける各要素の構成

各サービスの FQDN は以下の通りです。上述の通り認可サーバーとクライアントは存在しませんが、OAuth のフローを説明する上で FQDN が最低限必要となります。

サービス FQDN
Authlete API api.authlete.com
Authlete サービス管理者コンソール (Service Owner Console) so.authlete.com
Authlete クライアントアプリ開発者コンソール (Developer Console) cd.authlete.com
認可サーバー(OIDC アイデンティティプロバイダー) (Authorization Server) as.example.com
クライアント(OIDC リライングパーティ) (Client) client.example.org
リソースサーバー (Resource Server) N/A

FAPI を用いた API クライアントと API サーバーとの間のやりとり

本ドキュメントでは、FAPI に準拠した以下のトークン授受・利用フローに対応するための設定を行います。

FAPI 準拠のトークン授受・利用フロー

1. 認可リクエスト (Authorization Request)

クライアントはリクエストオブジェクトを用いた認可リクエストを作成・送信する必要があります。 リクエストオブジェクトは値渡し(request パラメーターを使用)もしくは参照渡し(request_uri パラメーターを使用)のいずれかとなります。本ドキュメントでは前者を用います。

また、response_type パラメーターの指定についても FAPI の規定に従う必要があります。 response_type として本ドキュメントでは code id_token を用います。

その他、claims の設定などについては後述します。

2. 認可レスポンス (Authorization Response)

本ドキュメントでは、認可リクエストに response_type=code id_token が指定されることから、フラグメントとして code を返却します。

3. トークンリクエスト (Token Request)

トークンリクエストでは、認可サーバーはクライアントを認証する方法として公開鍵方式を用いる必要があります。 本ドキュメントでは TLS 双方向認証を用います。

4. トークンレスポンス (Token Response)

認可サーバーは、トークンエンドポイントに提示されるクライアント証明書と、発行するアクセストークンとをバインド(ひもづけ)し、トークンレスポンスを返却します。

5. API リクエスト (API Request)

クライアントとリソースサーバーは、TLS 双方向認証を用いてクライアント認証を行った上で、アクセストークンを含む API リクエストを送受信します。 リソースサーバーはクライアント証明書とアクセストークンとのひもづけを検証の上、API レスポンスを返却します。

初期設定

新規 Authlete サービスとそのクライアントの追加

Authlete サービスの追加と FAPI の有効化

Authlete のサービス管理者コンソール https://so.authlete.com/ にログインし、右側の「サービス作成」をクリックします。 するとサービス作成のページが表示されます。「サービス名」と「トークン発行者識別子」を以下に従い入力し、「作成」をクリックします。 確認のダイアログが表示されるので「OK」をクリックします。

項目
サービス名 任意の値(例: FAPI Service
トークン発行者識別子 https://as.example.com

サービスを作成すると「APIキー」と「APIシークレット」が自動生成されます。 これらの値は、次にクライアントアプリ開発者コンソールにログインするときのログインID・パスワードとして用いるとともに、 認可サーバーが Authlete API にリクエストを行う際のクレデンシャルとなります。

項目
API キー 自動生成された値(例: 174381609020
API シークレット 自動生成された値(例: LszYEVDLM5Bu4lRjO9Vaj0tMSMVerWiPf_zcdy-vu4k

このサービスの FAPI サポートを有効化しましょう。 下方にある「編集」ボタンをクリックし、内容を編集可能にします。 「認可」タブの設定項目の中に「サポートするプロフィール群」があり、 「FAPI」チェックボックスにチェックが入っていない状態になっているはずです。 ここにチェックを入れて、下方にある「更新」ボタンをクリックします。 確認のダイアログが表示されるので「OK」をクリックします。

サービス設定の「サポートするプロフィール群」

次に、再度「編集」ボタンをクリックし、内容を編集可能にします。 「トークン」タブの設定項目の中に「サポートするスコープ」があり、 addressopenid などの既定スコープが表示されているはずです。 この項目の右側にある「スコープ作成」ボタンをクリックすると、スコープ作成のダイアログが表示されます。 ここでは以下の内容のスコープを作成します。

項目
スコープ名 payment
デフォルト設定 非デフォルト
説明 任意の値
スコープの属性群(「属性作成」をクリックして追加) 属性のキー: fapi / 属性値: rw

サービス設定の「スコープ作成」

クライアントの追加と基本設定

Authlete のクライアントアプリ開発者コンソール(https://cd.authlete.com/<API キー> 例: https://cd.authlete.com/174381609020)にアクセスし、 ログイン ID として先ほど作成したサービスの「API キー」を、 パスワードとして同じく「API シークレット」を用いてログインします。

ログイン後、右手にある「アプリ作成」をクリックすると、 アプリ作成のページが表示されます。まず「基本情報」タブでは、任意の「クライアント名」を入力し、 クライアントタイプとして 「CONFIDENTIAL」 を選択します。

項目
クライアント名 任意の値(例: FAPI Client
クライアントタイプ CONFIDENTIAL

次に、「基本情報」タブの隣にある「認可」タブをクリックし、「リダイレクト URI」セクションにある「リダイレクト URI 作成」をクリックして、 以下のリダイレクト URI を作成します。

項目
リダイレクト URI https://client.example.org/cb/example.com

指定後、ページ下方にある「作成」をクリックします。 確認のダイアログが表示されるので「OK」をクリックします。

これにより、サービスへのクライアント情報の登録が完了しました。

自動生成された「クライアント ID」は、 クライアントが認可サーバーにリクエストを行う際の client_id の値として用いられます。 なお、同時に「クライアントシークレット」も生成されていますが、 本チュートリアルでは使いません。 また、そのほか指定した各値が正しく設定されていることも確認しましょう。

項目
クライアント ID 自動生成された値(例: 591205987816490
クライアントタイプ CONFIDENTIAL
リダイレクト URI https://client.example.org/cb/example.com

初期設定後の動作確認

/auth/authorization API

上記の設定によって Authlete がどのように動作するかを、まず /auth/authorization API を用いて確認します。

非 FAPI スコープに関する認可リクエスト

認可サーバーが、FAPI-RW 対象のスコープが含まれない (scope=openid のみの) 認可リクエストを受信したと仮定し、 /auth/authorization API に対して以下のリクエストを送信します。

curl -s -X POST https://api.authlete.com/api/auth/authorization \
-u '174381609020:LszYEVDLM5Bu4lRjO9Vaj0tMSMVerWiPf_zcdy-vu4k' \
-H 'Content-Type: application/json' \
-d '{"parameters": "redirect_uri=https://client.example.org/cb/example.com&scope=openid&response_type=code&client_id=591205987816490&nonce=n-0S6_WzA2Mj"}' |jq

すると、以下のようなレスポンス(一部折り返しています)が返却されます。

{
  "type": "authorizationResponse",
  "resultCode": "A004001",
  "resultMessage": "[A004001] Authlete has successfully issued a ticket
   to the service (API Key = 174381609020) for the authorization request
   from the client (ID = 591205987816490). [response_type=code, openid=true]",
[...]

resultMessage の記述から、scope=openid のように、FAPI 対象ではないスコープの場合には、認可リクエストが Authlete に受け入れられたことがわかります。

それでは、FAPI の対象となるスコープの場合にはどのようになるか、次に試してみます。

FAPI スコープに関する認可リクエスト (1)

認可サーバーが、FAPI 対象のスコープ(payment)を含む認可リクエスト(scope=openid payment)を受信したと仮定し、 /auth/authorization API に対して以下のリクエストを送信します。

curl -s -X POST https://api.authlete.com/api/auth/authorization \
-u '174381609020:LszYEVDLM5Bu4lRjO9Vaj0tMSMVerWiPf_zcdy-vu4k' \
-H 'Content-Type: application/json' \
-d '{"parameters": "redirect_uri=https://client.example.org/cb/example.com&scope=openid+payment&response_type=code&client_id=591205987816490&nonce=n-0S6_WzA2Mj"}' |jq

すると、以下のようなエラーレスポンスが返却されます。

{
  "type": "authorizationResponse",
  "resultCode": "A150312",
  "resultMessage": "[A150312] The value of 'response_type' (code) is not allowed.",
[...]

resultMessage の内容を見ると、response_type=code は許可されていないことがわかります。

これは FAPI の 5.2. Advanced security provisions / 5.2.2. Authorization server

  • shall require
    1. the response_type value code id_token, or
    2. the response_type value code in conjunction with the response_mode value jwt;

とある通り、FAPI では response_type=code id_token か、もしくは response_mode=jwt を指定した上でresponse_type=codeの、どちらかを用いなくてはならないからです。 つまり response_type=code のみの指定は禁止されている(認可サーバーはそのようなレスポンスタイプを受け付けてはならない)のです。

FAPI スコープに関する認可リクエスト (2)

それでは、response_type=code ではなく response_type=code id_token とした認可リクエストの場合にはどうなるでしょうか。 /auth/authorization API に対して以下のリクエストを送信します。

curl -s -X POST https://api.authlete.com/api/auth/authorization \
-u '174381609020:LszYEVDLM5Bu4lRjO9Vaj0tMSMVerWiPf_zcdy-vu4k' \
-H 'Content-Type: application/json' \
-d '{"parameters": "redirect_uri=https://client.example.org/cb/example.com&scope=openid+payment&response_type=code+id_token&client_id=591205987816490&nonce=n-0S6_WzA2Mj"}' |jq

すると以下のような異なるエラーレスポンスが返却されます。

{
  "type": "authorizationResponse",
  "resultCode": "A150301",
  "resultMessage": "[A150301] A request object is required.",
[...]

resultMessage の記述から、この場合には、リクエストオブジェクトが必要だとわかります。

これは上述と同じく、FAPI の 5.2. Advanced security provisions / 5.2.2. Authorization server

  • shall only use the parameters included in the signed request object passed via the request or request_uri parameter;

とある通り、FAPI では、認可サーバーはクライアントに対して、リクエストオブジェクトを値渡し(request)ないし参照渡し(request_uri)にて認可リクエストに含めるよう、 要求しなくてはならないからです。

それでは次に、リクエストオブジェクトを用いた認可リクエストを準備してみましょう。

認可リクエストの FAPI 対応

本セクションでは、リクエストオブジェクトを用いた認可リクエストを作成します。

リクエストオブジェクトの設定

リクエストオブジェクトの署名鍵の設定

まず、クライアントがリクエストオブジェクトに署名するための鍵を準備します。

ここでは例として、 mkjwk を用いて、ES256 の鍵ペアを生成し、 2 種類(秘密鍵を含むもの・含まないもの)の鍵セットを生成します。 パラメーターは以下の通り選択・入力しています。

項目
タブ EC (楕円曲線)
曲線 P-256
鍵の用途 Signing
アルゴリズム ES256 (ECDSA using P-256 and SHA-256)
鍵の ID 任意の値(例: 1

mkjwk を用いた JWK セット生成

以下は生成された鍵セットの例です。

  • es256_keyset.txt (秘密鍵 "d" の行を含む)
{
  "keys": [
    {
      "kty": "EC",
      "d": "L6KxA-db4oh5NKYEpO6IulUDSRXP7fqNAmScu6fygIE",
      "use": "sig",
      "crv": "P-256",
      "kid": "1",
      "x": "icP8p_AigyTzwSpLRyv_bBQTSGu_NG7pMVXd-RAxwYE",
      "y": "06tC0MJeBlZNYlnY8g4bCA9wJ34XN-rWfWlmmlhf-F0",
      "alg": "ES256"
    }
  ]
}
  • es256_keyset_pub.txtes256_keyset.txt から秘密鍵 "d" の行を削除)
{
  "keys": [
    {
      "kty": "EC",
      "use": "sig",
      "crv": "P-256",
      "kid": "1",
      "x": "icP8p_AigyTzwSpLRyv_bBQTSGu_NG7pMVXd-RAxwYE",
      "y": "06tC0MJeBlZNYlnY8g4bCA9wJ34XN-rWfWlmmlhf-F0",
      "alg": "ES256"
    }
  ]
}

クライアント設定の追加

Authlete サービスに、クライアントから受信したリクエストオブジェクトの署名を検証させるためには、 そのクライアントの設定として公開鍵を登録し、さらに署名アルゴリズムを指定する必要があります。

Authlete のクライアントアプリ開発者コンソール(https://cd.authlete.com/<API キー> 例: https://cd.authlete.com/174381609020)にアクセスし、 各タブにおいて以下を設定します。

  • JWK セット」タブ
項目
JWK セットの内容 es256_keyset_pub.txt の内容をペースト

クライアント設定の「JWK セットの内容」

  • 認可」タブ
項目
リクエストオブジェクトの署名アルゴリズム ES256

クライアント設定の「リクエストオブジェクトの署名アルゴリズム」

上記の設定により、リクエストオブジェクトの署名検証の準備が整いました。

リクエストオブジェクトの生成

ここではリクエストオブジェクトを作成し、認可リクエストを組み立てます。

まず、以下のペイロードを準備します。client_id, nbf, exp の値は適宜変更します。

payload.txt

{
"redirect_uri":"https://client.example.org/cb/example.com",
"response_type":"code id_token",
"client_id":"<クライアント ID> 例: 591205987816490",
"scope":"openid payment",
"nbf": <現在時刻の Unix time> : 1613373232,
"exp": <nbf  3600 を加えた値> : 1613376832,
"aud":"https://as.example.com",
"nonce":"n-0S6_WzA2Mj"
}

秘密鍵を用いて Signed JWT を作成します。 以下は mkjose を用いて作成する例です。 各項目を入力・選択し、「生成する」ボタンを押下すると、「結果」の欄に Signed JWT が出力されます。

項目
ペイロード payload.txt の内容をペースト
署名アルゴリズム EC256 を選択
署名鍵 es256_keyset.txt の内容から、先頭 2 行("keys":, [)と末尾 2 行(], })を削除してペースト(JWK セットではなく JWK にする必要があるため)

mkjose を用いた Signed JWT の生成

また、step CLI を用いて作成する場合には以下の例のようになります。

cat payload.txt | \
step crypto jws sign --jwks=es256_keyset.txt --kid=1

以下は実行結果の例です。この文字列を、認可リクエストの request パラメーターの値として用います。

eyJhbGciOiJFUzI1NiIsImtpZCI6IjEifQ.ewoicmVkaXJlY3RfdXJpIjoiaHR0cHM6Ly9jbGllbnQuZXhhbXBsZS5vcmcvY2IvZXhhbXBsZS5jb20iLAoicmVzcG9uc2VfdHlwZSI6ImNvZGUgaWRfdG9rZW4iLAoiY2xpZW50X2lkIjoiNTkxMjA1OTg3ODE2NDkwIiwKInNjb3BlIjoib3BlbmlkIHBheW1lbnQiLAoiZXhwIjoxNTU0OTczMDAwMCwKImF1ZCI6Imh0dHBzOi8vYXMuZXhhbXBsZS5jb20iLAoibm9uY2UiOiJuLTBTNl9XekEyTWoiCn0K.q_MbfV5qN-gnB93JaQGVrEXu8WvhDuUzWx6DwC50J8AiQGjXDEpw9satUAMN18rrgnGNciiFztoEFJuJjrJoyA

動作確認

認可サーバーが認可リクエストをクライアントから受信したと仮定し、 Authlete の /auth/authorization API に対して以下のリクエストを送信します。

curl -s -X POST https://api.authlete.com/api/auth/authorization \
-u '174381609020:LszYEVDLM5Bu4lRjO9Vaj0tMSMVerWiPf_zcdy-vu4k' \
-H 'Content-Type: application/json' \
-d '{"parameters": "redirect_uri=https://client.example.org/cb/example.com&scope=openid+payment&response_type=code+id_token&client_id=591205987816490&nonce=n-0S6_WzA2Mj&request=eyJhbGciOiJFUzI1NiIsImtpZCI6IjEifQ.ewoicmVkaXJlY3RfdXJpIjoiaHR0cHM6Ly9jbGllbnQuZXhhbXBsZS5vcmcvY2IvZXhhbXBsZS5jb20iLAoicmVzcG9uc2VfdHlwZSI6ImNvZGUgaWRfdG9rZW4iLAoiY2xpZW50X2lkIjoiNTkxMjA1OTg3ODE2NDkwIiwKInNjb3BlIjoib3BlbmlkIHBheW1lbnQiLAoiZXhwIjoxNTU0OTczMDAwMCwKImF1ZCI6Imh0dHBzOi8vYXMuZXhhbXBsZS5jb20iLAoibm9uY2UiOiJuLTBTNl9XekEyTWoiCn0K.q_MbfV5qN-gnB93JaQGVrEXu8WvhDuUzWx6DwC50J8AiQGjXDEpw9satUAMN18rrgnGNciiFztoEFJuJjrJoyA"}' |jq

すると、以下のようなレスポンス(一部折り返しています)が返却されます。

{
  "type": "authorizationResponse",
  "resultCode": "A004001",
  "resultMessage": "[A004001] Authlete has successfully issued a ticket to the service
   (API Key = 174381609020) for the authorization request from the client
   (ID = 591205987816490). [response_type=code id_token, openid=true]",
[...]
  "ticket": "rjasCNvemUwamKP0G1h9Fh5Uo_3fgBfQsTF1PU4-GiE"
[...]

requestMessage の記述から、リクエストが受理されたことがわかります。

そして期待した通り、ticket が返却されています。 認可サーバーはこの ticket の値をログインセッションに格納し、 ユーザー認証と同意確認を行い、そして Authlete の /auth/authorization/issue API を呼び出し、 認可レスポンスの生成を要求することになります。

本ドキュメントの手順では、認可リクエストに response_type=code id_token と指定しています。 よって認可レスポンスは、認可コード code と ID トークン id_token を、 フラグメントとして含むものになります。

/auth/authorization/issue API

認可サーバーがユーザー認証と同意確認を行い、その結果決定したユーザー識別子(subject)が testuser01 であったと仮定し、 /auth/authorization/issue API にリクエストを送信してみましょう。 この subject と、上記の ticket が、API リクエストの引数となります。

curl -s -X POST https://api.authlete.com/api/auth/authorization/issue \
-u '174381609020:LszYEVDLM5Bu4lRjO9Vaj0tMSMVerWiPf_zcdy-vu4k' \
-H 'Content-Type: application/json' \
-d '{"subject":"testuser01","ticket":"rjasCNvemUwamKP0G1h9Fh5Uo_3fgBfQsTF1PU4-GiE"}' | jq

すると、以下のようなレスポンス(一部折り返しています)が返却されます。

{
  "type": "authorizationIssueResponse",
  "resultCode": "A151301",
  "resultMessage": "[A151301] The algorithm
   ('HS256' for 'id_token_signed_response_alg')
    to sign the ID token is not allowed.",
[...]

resultMessage の記述から、現在設定されている(Authlete の既定値である)HS256 が、ID トークンの署名アルゴリズムとして許可されていないことがわかります。 これは FAPI の 8.6. Algorithm considerations

  • shall use PS256 or ES256 algorithms;

とある通り、クライアントと認可サーバーは JWS アルゴリズムとして ES256 もしくは PS256 のどちらかを用いなければならないからです。 本ドキュメントでは、認可サーバーが ID トークンの署名アルゴリズムとして ES256 を用いるように、Authlete を設定してみます。

認可レスポンスの FAPI 対応

ID トークンの署名アルゴリズムの変更

ID トークンの署名鍵の追加

ES256 の鍵セットを生成し、ID トークンの署名に用いる鍵としてサービスに登録するとともに、 クライアント設定として ID トークンの署名アルゴリズムに ES256 を指定します。 手順は Authlete ナレッジベースの以下の記事を参照してください。

本ドキュメントでは、以下のパラメーターを選択・入力します。

項目
タブ EC (楕円曲線)
曲線 P-256
鍵の用途 Signing
アルゴリズム ES256 (ECDSA using P-256 and SHA-256)
鍵の ID 任意の値(例: 1

生成された鍵セット(“Keypair set”)を、まずサービスに登録します。 Authlete のサービス管理者コンソール https://so.authlete.com/ にログインし、 設定の「JWK Set (JWK セット)」にある「JWK Set Content (JWK セットの内容)」の項目に追加します。 また同じページの「ID Token Signature Key ID(ID トークン署名キー ID)」の項目に、この Keypair set の kid の値(上記の例では「1」)を入力します。

サービス設定の「JWK セットの内容」「ID トークン署名キー ID」

次に、Authlete のクライアントアプリ開発者コンソール(https://cd.authlete.com/<API キー> 例: https://cd.authlete.com/174381609020)にアクセスし、 ログイン ID として先ほど作成したサービスの「API キー」を、 パスワードとして同じく「API シークレット」を用いてログインします。

ログイン後、「ID トークン」タブにある「ID トークンの署名アルゴリズム」の項目にて、「ES256」を選択します。 この設定により Authlete は、このクライアントに対して ID トークンを発行する際の署名アルゴリズムとして ES256 を用いるようになります。

クライアント設定の「ID トークンの署名アルゴリズム」

動作確認

変更後、ticket 生成から再度動作確認を行います。 まず /auth/authorization API を実行します。

curl -s -X POST https://api.authlete.com/api/auth/authorization \
-u '174381609020:LszYEVDLM5Bu4lRjO9Vaj0tMSMVerWiPf_zcdy-vu4k' \
-H 'Content-Type: application/json' \
-d '{"parameters": "redirect_uri=https://client.example.org/cb/example.com&scope=openid+payment&response_type=code+id_token&client_id=591205987816490&nonce=n-0S6_WzA2Mj&request=eyJhbGciOiJFUzI1NiIsImtpZCI6IjEifQ.ewoicmVkaXJlY3RfdXJpIjoiaHR0cHM6Ly9jbGllbnQuZXhhbXBsZS5vcmcvY2IvZXhhbXBsZS5jb20iLAoicmVzcG9uc2VfdHlwZSI6ImNvZGUgaWRfdG9rZW4iLAoiY2xpZW50X2lkIjoiNTkxMjA1OTg3ODE2NDkwIiwKInNjb3BlIjoib3BlbmlkIHBheW1lbnQiLAoiZXhwIjoxNTU0OTczMDAwMCwKImF1ZCI6Imh0dHBzOi8vYXMuZXhhbXBsZS5jb20iLAoiY2xhaW1zIjp7CiAgImlkX3Rva2VuIjp7CiAgICAiYWNyIjp7CiAgICAgICJlc3NlbnRpYWwiOnRydWUsCiAgICAgICJ2YWx1ZXMiOlsidXJuOmV4YW1wbGU6cHNkMjpzY2EiXQogICAgfQogIH0KfSwKIm5vbmNlIjoibi0wUzZfV3pBMk1qIgp9Cg.b5rDSqaI3dh8n4A8hK4B5zSpnZNO_8--W-kTU03CNbCq1I_Vuf3w33ZVUhD0A-rla8cTPlZ25keQBncGWafzOA"}' \
| jq | grep ticket

この処理結果(一部折り返しています)として ticket が返却されます。

{
[...]
  "resultMessage": "[A004001] Authlete has successfully issued a ticket to the service
   (API Key = 174381609020) for the authorization request from the client
   (ID = 591205987816490). [response_type=code id_token, openid=true]",
  "ticket": "3TzdZO2t8qXaQXIEUA5LLN106uVk5fpwL8_UDGlcwUQ"
[...]

返却された ticketsubject の値を用いて、/auth/authorization/issue API にリクエストを行います。

curl -s -X POST https://api.authlete.com/api/auth/authorization/issue -u '174381609020:LszYEVDLM5Bu4lRjO9Vaj0tMSMVerWiPf_zcdy-vu4k' -H 'Content-Type: application/json' -d '{"subject":"testuser01","ticket":"3TzdZO2t8qXaQXIEUA5LLN106uVk5fpwL8_UDGlcwUQ"}' | jq

すると、以下のようなレスポンス(一部折り返しています)が返却されます。

{
  "type": "authorizationIssueResponse",
  "resultCode": "A040001",
  "resultMessage": "[A040001] The authorization request was processed successfully.",
  "accessTokenDuration": 0,
  "accessTokenExpiresAt": 0,
  "action": "LOCATION",
  "authorizationCode": "TSRAvPIp6V3RgPOs2O7FpPG1_7t6Xpc_kcIramz8gBQ",
  "idToken": "eyJraWQiOiIxIiwiYWxnIjoiRVMyNTYifQ.eyJzdWIiOiJ0ZXN0dXNlcjAxIiwiYXVkIjpbIjU5MTIwN
   Tk4NzgxNjQ5MCJdLCJjX2hhc2giOiJZQjloU01CWkJLdnFobnFaWWRWTXJnIiwiaXNzIjoiaHR0cHM6L
   y9hcy5leGFtcGxlLmNvbSIsImV4cCI6MTU3MjQxMDUyNiwiaWF0IjoxNTcyMzI0MTI2LCJub25jZSI6I
   m4tMFM2X1d6QTJNaiJ9.ZY5XK4TqAfcnLsMhkigNRpyM6CvwD7SdX-f9TQ18pwMUdh7eoGc6ijlfEnc4
   I3l0jYhlm22yuEeffV6XZhdL0A",
  "responseContent": "https://client.example.org/cb/example.com#
   code=TSRAvPIp6V3RgPOs2O7FpPG1_7t6Xpc_kcIramz8gBQ&
   id_token=eyJraWQiOiIxIiwiYWxnIjoiRVMyNTYifQ.eyJzdWIiOiJ0ZXN0dXNlcjAxIiwiYXVkIjpbIjU5MTIwN
   Tk4NzgxNjQ5MCJdLCJjX2hhc2giOiJZQjloU01CWkJLdnFobnFaWWRWTXJnIiwiaXNzIjoiaHR0cHM6L
   y9hcy5leGFtcGxlLmNvbSIsImV4cCI6MTU3MjQxMDUyNiwiaWF0IjoxNTcyMzI0MTI2LCJub25jZSI6I
   m4tMFM2X1d6QTJNaiJ9.ZY5XK4TqAfcnLsMhkigNRpyM6CvwD7SdX-f9TQ18pwMUdh7eoGc6ijlfEnc4
   I3l0jYhlm22yuEeffV6XZhdL0A"
}

resultMessage の記述から、認可リクエストの処理に成功したことがわかります。また idToken, authorizationCode にそれぞれ値が返却されています。 そして responseContent として、認可サーバーがクライアントに返却する HTTP レスポンスが生成されており、レスポンスのフラグメント部に ID トークンと認可コードが含まれていることがわかります。

この後クライアントは ID トークンを検証し、さらにその中に含まれる c_hash クレームを用いて認可コードを検証したのちに、 認可サーバーにトークンリクエストを送信することになります。そして認可サーバーはそのトークンリクエストを Authlete の /auth/token API に送信し、 アクセストークンを含むトークンレスポンスの生成を要求します。

/auth/token API

/auth/token API に対するリクエストは以下の通りです。

curl -s -X POST https://api.authlete.com/api/auth/token \
-u '174381609020:LszYEVDLM5Bu4lRjO9Vaj0tMSMVerWiPf_zcdy-vu4k' \
-H 'Content-Type: application/json' \
-d '{"clientId":"591205987816490","clientSecret":"e7iqzq7WE8Kg00yepYnpMTjvDnAnBlq5nfA9DDQLkiYkPQBV6Lr8sLhn7DhUJd17i0O6TwQ2hKFeDAYuU160Vg","parameters": "grant_type=authorization_code&redirect_uri=https://client.example.org/cb/example.com&code=TSRAvPIp6V3RgPOs2O7FpPG1_7t6Xpc_kcIramz8gBQ"}'|jq

すると、以下のようなレスポンス(一部折り返しています)が返却されます。

{
  "type": "tokenResponse",
  "resultCode": "A157301",
  "resultMessage": "[A157301] The client type of the client is 'confidential'
   but the client authentication method is 'none'.",
  "accessTokenDuration": 0,
  "accessTokenExpiresAt": 0,
  "action": "INVALID_CLIENT",
  "clientId": 591205987816490,
  "clientIdAliasUsed": false,
  "grantType": "AUTHORIZATION_CODE",
  "refreshTokenDuration": 0,
  "refreshTokenExpiresAt": 0,
  "responseContent": "{\"error_description\":\"[A157301] The client type
   of the client is 'confidential' but the client authentication method is 'none'.\",
   \"error\":\"invalid_client\",\"error_uri\":\"https://docs.authlete.com/#A157301\"}"
}

resultMessage の記述から、コンフィデンシャルクライアントのクライアント認証として、 Authlete の既定値である none は許可されていないことがわかります。

これは FAPI の 5.2 Read and write API security provisions / 5.2.2. Authorization server

  • shall authenticate the confidential client using one of the following methods (this overrides FAPI Security Profile 1.0 - Part 1: Baseline clause 5.2.2-4):

    1. tls_client_auth or self_signed_tls_client_auth as specified in section 2 of MTLS, or
    2. private_key_jwt as specified in section 9 of OIDC;

とある通り、FAPI では RFC 8705 が定義する Mutual TLS for OAuth Client Authentication、もしくは OpenID Connect Core 1.0 が定義する private_key_jwt のどちらかを用いなくてはならないからです。つまり none や、コンフィデンシャルクライアントの場合に広く使われている client_secret_basic は禁止されている(認可サーバーはそのようなクライアント認証を行ってはならない)のです。

本ドキュメントでは後者、かつ PKI Mutual-TLS Method (tls_client_auth) を用いるように Authlete を設定してみます。

トークンリクエストの FAPI 対応

TLS クライアント認証の設定

クライアント認証に「TLS クライアント認証」を用いるように、サービスおよびクライアントの設定を変更します。

サービス側の TLS クライアント認証設定

Authlete のサービス管理者コンソール https://so.authlete.com/ にログインし、 「認可」タブの「トークンエンドポイント」セクションにおいて以下を設定します。

項目
サポートするクライアント認証方式 TLS_CLIENT_AUTH

サービス設定の「サポートするクライアント認証方式」

クライアント側の TLS クライアント認証設定

Authlete のクライアントアプリ開発者コンソール(https://cd.authlete.com/<API キー> 例: https://cd.authlete.com/174381609020)にアクセスし、 「認可」タブの「トークンエンドポイント」セクションにおいて以下を設定します。

項目
サポートするクライアント認証方式 TLS_CLIENT_AUTH

クライアント設定の「サポートするクライアント認証方式」

項目
Subject Distinguished Name CN=client.example.org, O=Client, L=Chiyoda-ku, ST=Tokyo, C=JP

クライアント設定の「TLS クライアント認証のためのサブジェクト識別名」

以上の設定により、Authlete サービスは TLS 双方向認証によるクライアント認証に対応し、かつ上記のクライアントからのトークンリクエストについて、その認証方式を適用することになります。 また認証の際のクライアント識別名として CN=client.example.org, ... を用います。

自己署名証明書の生成

クライアントが自身の認証に用いる証明書を作成します。以下は openssl を用いて作成する例です。

  • 秘密鍵の生成
openssl genrsa 2048  > private_key_nopass.pem
Generating RSA private key, 2048 bit long modulus
...............+++
.......................+++
e is 65537 (0x10001)
  • CSR の作成
    • Subject DN として CN=client.example.org, O=Client, L=Chiyoda-ku, ST=Tokyo, C=JP を指定
$ openssl req -new -key private_key_nopass.pem -out server.csr
You are about to be asked to enter information that will be incorporated
into your certificate request.
What you are about to enter is what is called a Distinguished Name or a DN.
There are quite a few fields but you can leave some blank
For some fields there will be a default value,
If you enter '.', the field will be left blank.
-----
Country Name (2 letter code) []:JP
State or Province Name (full name) []:Tokyo
Locality Name (eg, city) []:Chiyoda-ku
Organization Name (eg, company) []:Client
Organizational Unit Name (eg, section) []:
Common Name (eg, fully qualified host name) []:client.example.org
Email Address []:

Please enter the following 'extra' attributes
to be sent with your certificate request
A challenge password []:
$ cat server.csr
-----BEGIN CERTIFICATE REQUEST-----
MIICpTCCAY0CAQAwYDELMAkGA1UEBhMCSlAxDjAMBgNVBAgMBVRva3lvMRMwEQYD
VQQHDApDaGl5b2RhLWt1MQ8wDQYDVQQKDAZDbGllbnQxGzAZBgNVBAMMEmNsaWVu
[...]
r4MUVOwPNWOM6UGYQZwjvtJ2rmKr8cQrbfvbcFiY4s6lLQGOz5yLzmO8GUdmfzUd
p5BW1iL+SpjS
-----END CERTIFICATE REQUEST-----
  • 証明書の作成
$ openssl x509 -days 365 -req -signkey private_key_nopass.pem -in server.csr -out server.crt
Signature ok
subject=/C=JP/ST=Tokyo/L=Chiyoda-ku/O=Client/CN=client.example.org
Getting Private key

本手順にて作成した証明書は以下の通りです。

server.crt

-----BEGIN CERTIFICATE-----
MIIDPDCCAiQCCQDWNMOIuzwDfzANBgkqhkiG9w0BAQUFADBgMQswCQYDVQQGEwJK
UDEOMAwGA1UECAwFVG9reW8xEzARBgNVBAcMCkNoaXlvZGEta3UxDzANBgNVBAoM
BkNsaWVudDEbMBkGA1UEAwwSY2xpZW50LmV4YW1wbGUub3JnMB4XDTE5MTAyODA3
MjczMFoXDTIwMTAyNzA3MjczMFowYDELMAkGA1UEBhMCSlAxDjAMBgNVBAgMBVRv
a3lvMRMwEQYDVQQHDApDaGl5b2RhLWt1MQ8wDQYDVQQKDAZDbGllbnQxGzAZBgNV
BAMMEmNsaWVudC5leGFtcGxlLm9yZzCCASIwDQYJKoZIhvcNAQEBBQADggEPADCC
AQoCggEBAK2Oyc+BV4N5pYcp47opUwsb2NaJq4X+d5Itq8whpFlZ9uCCHzF5TWSF
XrpYscOp95veGPF42eT1grfxYyvjFotE76caHhBLCkIbBh6Vf222IGMwwBbSZfO9
J3eURtEADBvsZ117HkPVdjYqvt3Pr4RxdR12zG1TcBAoTLGchyr8nBqRADFhUTCL
msYaz1ADiQ/xbJN7VUNQpKhzRWHCdYS03HpbGjYCtAbl9dJnH2EepNF0emGiSPFq
df6taToyCr7oZjM7ufmKPjiiEDbeSYTf6kbPNmmjtoPNNLeejHjP9p0IYx7l0Gkj
mx4kSMLp4vSDftrFgGfcxzaMmKBsosMCAwEAATANBgkqhkiG9w0BAQUFAAOCAQEA
qzdDYbntFLPBlbwAQlpwIjvmvwzvkQt6qgZ9Y0oMAf7pxq3i9q7W1bDol0UF4pIM
z3urEJCHO8w18JRlfOnOENkcLLLntrjOUXuNkaCDLrnv8pnp0yeTQHkSpsyMtJi9
R6r6JT9V57EJ/pWQBgKlN6qMiBkIvX7U2hEMmhZ00h/E5xMmiKbySBiJV9fBzDRf
mAy1p9YEgLsEMLnGjKHTok+hd0BLvcmXVejdUsKCg84F0zqtXEDXLCiKcpXCeeWv
lmmXxC5PH/GEMkSPiGSR7+b1i0sSotsq+M3hbdwabpJ6nQLLbKkFSGcsQ87yL+gr
So6zun26vAUJTu1o9CIjxw==
-----END CERTIFICATE-----

curl コマンドを用いてリクエストを行う際には、上記の内容を 1 行で表現したものを用います。

-----BEGIN CERTIFICATE-----\nMIIDPDCCAiQCCQDWNMOIuzwDfzANBgkqhkiG9w0BAQUFADBgMQswCQYDVQQGEwJK\nUDEOMAwGA1UECAwFVG9reW8xEzARBgNVBAcMCkNoaXlvZGEta3UxDzANBgNVBAoM\nBkNsaWVudDEbMBkGA1UEAwwSY2xpZW50LmV4YW1wbGUub3JnMB4XDTE5MTAyODA3\nMjczMFoXDTIwMTAyNzA3MjczMFowYDELMAkGA1UEBhMCSlAxDjAMBgNVBAgMBVRv\na3lvMRMwEQYDVQQHDApDaGl5b2RhLWt1MQ8wDQYDVQQKDAZDbGllbnQxGzAZBgNV\nBAMMEmNsaWVudC5leGFtcGxlLm9yZzCCASIwDQYJKoZIhvcNAQEBBQADggEPADCC\nAQoCggEBAK2Oyc+BV4N5pYcp47opUwsb2NaJq4X+d5Itq8whpFlZ9uCCHzF5TWSF\nXrpYscOp95veGPF42eT1grfxYyvjFotE76caHhBLCkIbBh6Vf222IGMwwBbSZfO9\nJ3eURtEADBvsZ117HkPVdjYqvt3Pr4RxdR12zG1TcBAoTLGchyr8nBqRADFhUTCL\nmsYaz1ADiQ/xbJN7VUNQpKhzRWHCdYS03HpbGjYCtAbl9dJnH2EepNF0emGiSPFq\ndf6taToyCr7oZjM7ufmKPjiiEDbeSYTf6kbPNmmjtoPNNLeejHjP9p0IYx7l0Gkj\nmx4kSMLp4vSDftrFgGfcxzaMmKBsosMCAwEAATANBgkqhkiG9w0BAQUFAAOCAQEA\nqzdDYbntFLPBlbwAQlpwIjvmvwzvkQt6qgZ9Y0oMAf7pxq3i9q7W1bDol0UF4pIM\nz3urEJCHO8w18JRlfOnOENkcLLLntrjOUXuNkaCDLrnv8pnp0yeTQHkSpsyMtJi9\nR6r6JT9V57EJ/pWQBgKlN6qMiBkIvX7U2hEMmhZ00h/E5xMmiKbySBiJV9fBzDRf\nmAy1p9YEgLsEMLnGjKHTok+hd0BLvcmXVejdUsKCg84F0zqtXEDXLCiKcpXCeeWv\nlmmXxC5PH/GEMkSPiGSR7+b1i0sSotsq+M3hbdwabpJ6nQLLbKkFSGcsQ87yL+gr\nSo6zun26vAUJTu1o9CIjxw==\n-----END CERTIFICATE-----\n

動作確認

サービス設定とクライアント設定をそれぞれ変更したのちに、以下の手順を実行します。

  1. /auth/authorization API を実行し、レスポンスから ticket の値を取得
  2. /auth/authorization/issue API を実行し、レスポンスから authorizationCode の値を取得
  3. 以下を変更の上、/auth/token API を実行 * clientCertificate パラメーターを追加し、値としてクライアント証明書を指定する * clientSecret パラメーターを削除する(TLS 双方向認証を用いるため不要となります)

以下は /auth/token API に送信するリクエストの例です。

curl -s -X POST https://api.authlete.com/api/auth/token \
-u '174381609020:LszYEVDLM5Bu4lRjO9Vaj0tMSMVerWiPf_zcdy-vu4k' \
-H 'Content-Type: application/json' \
-d '{"clientId":"591205987816490","parameters": "grant_type=authorization_code&redirect_uri=https://client.example.org/cb/example.com&code=HVIza0dGG9nDKGStAzMObYH9GkXME0aRSaLEcToHEI8","clientCertificate":"-----BEGIN CERTIFICATE-----\nMIIDPDCCAiQCCQDWNMOIuzwDfzANBgkqhkiG9w0BAQUFADBgMQswCQYDVQQGEwJK\nUDEOMAwGA1UECAwFVG9reW8xEzARBgNVBAcMCkNoaXlvZGEta3UxDzANBgNVBAoM\nBkNsaWVudDEbMBkGA1UEAwwSY2xpZW50LmV4YW1wbGUub3JnMB4XDTE5MTAyODA3\nMjczMFoXDTIwMTAyNzA3MjczMFowYDELMAkGA1UEBhMCSlAxDjAMBgNVBAgMBVRv\na3lvMRMwEQYDVQQHDApDaGl5b2RhLWt1MQ8wDQYDVQQKDAZDbGllbnQxGzAZBgNV\nBAMMEmNsaWVudC5leGFtcGxlLm9yZzCCASIwDQYJKoZIhvcNAQEBBQADggEPADCC\nAQoCggEBAK2Oyc+BV4N5pYcp47opUwsb2NaJq4X+d5Itq8whpFlZ9uCCHzF5TWSF\nXrpYscOp95veGPF42eT1grfxYyvjFotE76caHhBLCkIbBh6Vf222IGMwwBbSZfO9\nJ3eURtEADBvsZ117HkPVdjYqvt3Pr4RxdR12zG1TcBAoTLGchyr8nBqRADFhUTCL\nmsYaz1ADiQ/xbJN7VUNQpKhzRWHCdYS03HpbGjYCtAbl9dJnH2EepNF0emGiSPFq\ndf6taToyCr7oZjM7ufmKPjiiEDbeSYTf6kbPNmmjtoPNNLeejHjP9p0IYx7l0Gkj\nmx4kSMLp4vSDftrFgGfcxzaMmKBsosMCAwEAATANBgkqhkiG9w0BAQUFAAOCAQEA\nqzdDYbntFLPBlbwAQlpwIjvmvwzvkQt6qgZ9Y0oMAf7pxq3i9q7W1bDol0UF4pIM\nz3urEJCHO8w18JRlfOnOENkcLLLntrjOUXuNkaCDLrnv8pnp0yeTQHkSpsyMtJi9\nR6r6JT9V57EJ/pWQBgKlN6qMiBkIvX7U2hEMmhZ00h/E5xMmiKbySBiJV9fBzDRf\nmAy1p9YEgLsEMLnGjKHTok+hd0BLvcmXVejdUsKCg84F0zqtXEDXLCiKcpXCeeWv\nlmmXxC5PH/GEMkSPiGSR7+b1i0sSotsq+M3hbdwabpJ6nQLLbKkFSGcsQ87yL+gr\nSo6zun26vAUJTu1o9CIjxw==\n-----END CERTIFICATE-----\n"}' |jq

すると、以下のようなレスポンス(一部折り返しています)が返却されます。

{
  "type": "tokenResponse",
  "resultCode": "A152305",
  "resultMessage": "[A152305] The service and the client are not configured
   so that the required Holder of Key methods are performed.",
  "accessToken": "RCqjF4tlffJ7-n92sAEFQNIwrRm0syOUrBu0cNLAIJU",
  "accessTokenDuration": 0,
  "accessTokenExpiresAt": 0,
  "action": "BAD_REQUEST",
  "clientId": 591205987816490,
  "clientIdAliasUsed": false,
  "grantType": "AUTHORIZATION_CODE",
  "idToken": "eyJraWQiOiIxIiwiYWxnIjoiRVMyNTYifQ.eyJzdWIiOiJ0ZXN0dXNlcjAxIiwiYXVkIjpbIjU5MTIwN
  Tk4NzgxNjQ5MCJdLCJpc3MiOiJodHRwczovL2FzLmV4YW1wbGUuY29tIiwiZXhwIjoxNTcyNDEyMTcwL
  CJpYXQiOjE1NzIzMjU3NzAsIm5vbmNlIjoibi0wUzZfV3pBMk1qIn0.x4XmPTh698AbNEjCaNcD5k54q
  S249BSPkc9EkwZuUI17AL8z593GYTg3GVQQdhF9k0HYLRA17c3m39OxYDrx3g",
  "refreshToken": "jy5lN7TXZrAlIfgFbOaMMkzDtoJUu4prBtNa3HcoRRE",
  "refreshTokenDuration": 0,
  "refreshTokenExpiresAt": 0,
  "responseContent": "{\"error_description\":\"[A152305] The service and the client are
   not configured so that the required Holder of Key methods are performed.\",
   \"error\":\"invalid_request\",\"error_uri\":\"https://docs.authlete.com/#A152305\"}"
}

resultMessage の記述から、アクセストークンは発行されたものの、未だ設定が不足しているために、 FAPI 仕様上必須である 「Holder of Key」が行われなかったことがわかります。 すなわち、ここまでの手順だけでは、 TLS クライアント証明書をひもづけたアクセストークン発行の処理が行われていないということです。

トークンレスポンスの FAPI 対応

Holder of Key の設定

サービス側のアクセストークン設定

Authlete のサービス管理者コンソール https://so.authlete.com/ にログインし、 「トークン」タブの「アクセストークン」セクションにおいて以下を設定します。

項目
TLS クライアント証明書を紐付けたアクセストークンのサポート サポートする

サービス設定の「TLS クライアント証明書を紐付けたアクセストークンのサポート」

クライアント側のアクセストークン設定

Authlete のクライアントアプリ開発者コンソール(https://cd.authlete.com/<API キー> 例: https://cd.authlete.com/174381609020)にアクセスし、 「基本情報」タブにおいて以下を設定します。

項目
TLS クライアント証明書を紐付けたアクセストークンの使用 使用する

クライアント設定の「TLS クライアント証明書を紐付けたアクセストークンの使用」

以上により、FAPI 準拠の認可サーバーの設定が完了しました。

設定完了後の実行例

FAPI 準拠のトークン授受・利用フロー(Authlete を利用)

変更後、先と同様の手順により /auth/authorization API/auth/authorization/issue API/auth/token API を実行します。 さらに /auth/introspection API を実行して、最終的に API リクエストに用いられるアクセストークンが、クライアントの TLS クライアント証明書に紐づけられていることを確認します。

認可リクエスト

curl -s -X POST https://api.authlete.com/api/auth/authorization -u '174381609020:LszYEVDLM5Bu4lRjO9Vaj0tMSMVerWiPf_zcdy-vu4k' -H 'Content-Type: application/json' -d '{"parameters": "redirect_uri=https://client.example.org/cb/example.com&scope=openid+payment&response_type=code+id_token&client_id=591205987816490&nonce=n-0S6_WzA2Mj&request=eyJhbGciOiJFUzI1NiIsImtpZCI6IjEifQ.ewoicmVkaXJlY3RfdXJpIjoiaHR0cHM6Ly9jbGllbnQuZXhhbXBsZS5vcmcvY2IvZXhhbXBsZS5jb20iLAoicmVzcG9uc2VfdHlwZSI6ImNvZGUgaWRfdG9rZW4iLAoiY2xpZW50X2lkIjoiNTkxMjA1OTg3ODE2NDkwIiwKInNjb3BlIjoib3BlbmlkIHBheW1lbnQiLAoiZXhwIjoxNTU0OTczMDAwMCwKImF1ZCI6Imh0dHBzOi8vYXMuZXhhbXBsZS5jb20iLAoiY2xhaW1zIjp7CiAgImlkX3Rva2VuIjp7CiAgICAiYWNyIjp7CiAgICAgICJlc3NlbnRpYWwiOnRydWUsCiAgICAgICJ2YWx1ZXMiOlsidXJuOm1hY2U6aW5jb21tb246aWFwOnNpbHZlciJdCiAgICB9CiAgfQp9LAoibm9uY2UiOiJuLTBTNl9XekEyTWoiCn0K.50gewunAqCITD6p2kI52GDXdUgQP-EzLDjjjoDT9C4zY8YCKgLzN7sR2ZvkAQ_pimLpwFh2QYjjyPskvvtnC9g"}' |jq|grep ticket
  • レスポンス(一部折り返しています)
{
[...]
  "resultMessage": "[A004001] Authlete has successfully issued a ticket
   to the service (API Key = 174381609020) for the authorization request
  from the client (ID = 591205987816490). [response_type=code id_token, openid=true]",
  [...]
  "ticket": "b0JGD-ZkT8ElBGw2ck-T-t87Z033jXvhqC2omPT1bQ4"
  [...]

認可レスポンス

curl -s -X POST https://api.authlete.com/api/auth/authorization/issue -u '174381609020:LszYEVDLM5Bu4lRjO9Vaj0tMSMVerWiPf_zcdy-vu4k' -H 'Content-Type: application/json' -d '{"subject":"testuser01","ticket":"b0JGD-ZkT8ElBGw2ck-T-t87Z033jXvhqC2omPT1bQ4"}' | jq
  • レスポンス(一部折り返しています)
{
  "type": "authorizationIssueResponse",
  "resultCode": "A040001",
  "resultMessage": "[A040001] The authorization request was processed successfully.",
  "accessTokenDuration": 0,
  "accessTokenExpiresAt": 0,
  "action": "LOCATION",
  "authorizationCode": "DxiKC0cOc_46nzVjgr41RWBQtMDrAvc0BUbMJ_v7I70",
  "idToken": "eyJraWQiOiIxIiwiYWxnIjoiRVMyNTYifQ.eyJzdWIiOiJ0ZXN0dXNlcjAxIiwiYXVkIjpbIjU5MTIwN
  Tk4NzgxNjQ5MCJdLCJjX2hhc2giOiJqR2kyOElvYm5HcjNNQ3Y0UUVQRTNnIiwiaXNzIjoiaHR0cHM6L
  y9hcy5leGFtcGxlLmNvbSIsImV4cCI6MTU3MjQxMjY4MiwiaWF0IjoxNTcyMzI2MjgyLCJub25jZSI6I
  m4tMFM2X1d6QTJNaiJ9.1PFmc0gAsBWtLBriq3z9a4Tsi_ioEYlOqOYbicGEXWIS1WGX5ffGOyZNSzVB
  MamZbltZmSys0jlYmmYYLqgGsg",
  "responseContent": "https://client.example.org/cb/example.com#
  code=DxiKC0cOc_46nzVjgr41RWBQtMDrAvc0BUbMJ_v7I70&
  id_token=eyJraWQiOiIxIiwiYWxnIjoiRVMyNTYifQ.eyJzdWIiOiJ0ZXN0dXNlcjAxIiwiYXVkIjpbIjU5MTIwN
  Tk4NzgxNjQ5MCJdLCJjX2hhc2giOiJqR2kyOElvYm5HcjNNQ3Y0UUVQRTNnIiwiaXNzIjoiaHR0cHM6L
  y9hcy5leGFtcGxlLmNvbSIsImV4cCI6MTU3MjQxMjY4MiwiaWF0IjoxNTcyMzI2MjgyLCJub25jZSI6I
  m4tMFM2X1d6QTJNaiJ9.1PFmc0gAsBWtLBriq3z9a4Tsi_ioEYlOqOYbicGEXWIS1WGX5ffGOyZNSzVB
  MamZbltZmSys0jlYmmYYLqgGsg"
}

トークンリクエスト/レスポンス

curl -s -X POST https://api.authlete.com/api/auth/token \
-u '174381609020:LszYEVDLM5Bu4lRjO9Vaj0tMSMVerWiPf_zcdy-vu4k' \
-H 'Content-Type: application/json' \
-d '{"clientId":"591205987816490","parameters": "grant_type=authorization_code&redirect_uri=https://client.example.org/cb/example.com&code=DxiKC0cOc_46nzVjgr41RWBQtMDrAvc0BUbMJ_v7I70","clientCertificate":"-----BEGIN CERTIFICATE-----\nMIIDPDCCAiQCCQDWNMOIuzwDfzANBgkqhkiG9w0BAQUFADBgMQswCQYDVQQGEwJK\nUDEOMAwGA1UECAwFVG9reW8xEzARBgNVBAcMCkNoaXlvZGEta3UxDzANBgNVBAoM\nBkNsaWVudDEbMBkGA1UEAwwSY2xpZW50LmV4YW1wbGUub3JnMB4XDTE5MTAyODA3\nMjczMFoXDTIwMTAyNzA3MjczMFowYDELMAkGA1UEBhMCSlAxDjAMBgNVBAgMBVRv\na3lvMRMwEQYDVQQHDApDaGl5b2RhLWt1MQ8wDQYDVQQKDAZDbGllbnQxGzAZBgNV\nBAMMEmNsaWVudC5leGFtcGxlLm9yZzCCASIwDQYJKoZIhvcNAQEBBQADggEPADCC\nAQoCggEBAK2Oyc+BV4N5pYcp47opUwsb2NaJq4X+d5Itq8whpFlZ9uCCHzF5TWSF\nXrpYscOp95veGPF42eT1grfxYyvjFotE76caHhBLCkIbBh6Vf222IGMwwBbSZfO9\nJ3eURtEADBvsZ117HkPVdjYqvt3Pr4RxdR12zG1TcBAoTLGchyr8nBqRADFhUTCL\nmsYaz1ADiQ/xbJN7VUNQpKhzRWHCdYS03HpbGjYCtAbl9dJnH2EepNF0emGiSPFq\ndf6taToyCr7oZjM7ufmKPjiiEDbeSYTf6kbPNmmjtoPNNLeejHjP9p0IYx7l0Gkj\nmx4kSMLp4vSDftrFgGfcxzaMmKBsosMCAwEAATANBgkqhkiG9w0BAQUFAAOCAQEA\nqzdDYbntFLPBlbwAQlpwIjvmvwzvkQt6qgZ9Y0oMAf7pxq3i9q7W1bDol0UF4pIM\nz3urEJCHO8w18JRlfOnOENkcLLLntrjOUXuNkaCDLrnv8pnp0yeTQHkSpsyMtJi9\nR6r6JT9V57EJ/pWQBgKlN6qMiBkIvX7U2hEMmhZ00h/E5xMmiKbySBiJV9fBzDRf\nmAy1p9YEgLsEMLnGjKHTok+hd0BLvcmXVejdUsKCg84F0zqtXEDXLCiKcpXCeeWv\nlmmXxC5PH/GEMkSPiGSR7+b1i0sSotsq+M3hbdwabpJ6nQLLbKkFSGcsQ87yL+gr\nSo6zun26vAUJTu1o9CIjxw==\n-----END CERTIFICATE-----\n"}' |jq
  • レスポンス(一部折り返しています)
{
  "type": "tokenResponse",
  "resultCode": "A050001",
  "resultMessage": "[A050001] The token request (grant_type=authorization_code)
   was processed successfully.",
  "accessToken": "SUtEVc3Tj3D3xOdysQtssQxe9egAhI4fimexNVMjRyU",
  "accessTokenDuration": 86400,
  "accessTokenExpiresAt": 1572412769390,
  "action": "OK",
  "clientId": 591205987816490,
  "clientIdAliasUsed": false,
  "grantType": "AUTHORIZATION_CODE",
  "idToken": "eyJraWQiOiIxIiwiYWxnIjoiRVMyNTYifQ.eyJzdWIiOiJ0ZXN0dXNlcjAxIiwiYXVkIjpbIjU5MTIwN
  Tk4NzgxNjQ5MCJdLCJpc3MiOiJodHRwczovL2FzLmV4YW1wbGUuY29tIiwiZXhwIjoxNTcyNDEyNzY5L
  CJpYXQiOjE1NzIzMjYzNjksIm5vbmNlIjoibi0wUzZfV3pBMk1qIn0.9EQojck-Cf2hnKAZWR164kr21
  o5lPKehvIHyViZgRg4CY_ZGmnyFooG4FCwlZxu-QOTtaDCffCsuCdz4GqknTA",
  "refreshToken": "tXZjYfoK35I-djg9V3n6s58zsrVqRIzTNMXKIS_wkj8",
  "refreshTokenDuration": 864000,
  "refreshTokenExpiresAt": 1573190369390,
  "responseContent": "{\"access_token\":\"SUtEVc3Tj3D3xOdysQtssQxe9egAhI4fimexNVMjRyU\",
  \"refresh_token\":\"tXZjYfoK35I-djg9V3n6s58zsrVqRIzTNMXKIS_wkj8\",\"scope\":\"openid payment\",
  \"id_token\":\"eyJraWQiOiIxIiwiYWxnIjoiRVMyNTYifQ.eyJzdWIiOiJ0ZXN0dXNlcjAxIiwiYXVkIjpbIjU5MTIwN
  Tk4NzgxNjQ5MCJdLCJpc3MiOiJodHRwczovL2FzLmV4YW1wbGUuY29tIiwiZXhwIjoxNTcyNDEyNzY5L
  CJpYXQiOjE1NzIzMjYzNjksIm5vbmNlIjoibi0wUzZfV3pBMk1qIn0.9EQojck-Cf2hnKAZWR164kr21
  o5lPKehvIHyViZgRg4CY_ZGmnyFooG4FCwlZxu-QOTtaDCffCsuCdz4GqknTA\",
  \"token_type\":\"Bearer\",\"expires_in\":86400}",
  "scopes": [
    "openid",
    "payment"
  ],
  "subject": "testuser01"
}

API リクエスト

上記手順の結果、アクセストークンとして SUtEVc3Tj3D3xOdysQtssQxe9egAhI4fimexNVMjRyU が生成されています。 この値をリソースサーバーがクライアントから受信したと仮定し、/auth/introspection API を用いて、 このアクセストークンの有効性と、関連する情報の取得を試みます。 この際に、リソースサーバーがクライアントから同時に受け取るであろう、クライアント証明書も併せて API に送信します。

/auth/introspection API

curl -s -X POST https://api.authlete.com/api/auth/introspection \
-u '174381609020:LszYEVDLM5Bu4lRjO9Vaj0tMSMVerWiPf_zcdy-vu4k' \
-H 'Content-Type: application/json' \
-d '{"token":"SUtEVc3Tj3D3xOdysQtssQxe9egAhI4fimexNVMjRyU","clientCertificate":"-----BEGIN CERTIFICATE-----\nMIIDPDCCAiQCCQDWNMOIuzwDfzANBgkqhkiG9w0BAQUFADBgMQswCQYDVQQGEwJK\nUDEOMAwGA1UECAwFVG9reW8xEzARBgNVBAcMCkNoaXlvZGEta3UxDzANBgNVBAoM\nBkNsaWVudDEbMBkGA1UEAwwSY2xpZW50LmV4YW1wbGUub3JnMB4XDTE5MTAyODA3\nMjczMFoXDTIwMTAyNzA3MjczMFowYDELMAkGA1UEBhMCSlAxDjAMBgNVBAgMBVRv\na3lvMRMwEQYDVQQHDApDaGl5b2RhLWt1MQ8wDQYDVQQKDAZDbGllbnQxGzAZBgNV\nBAMMEmNsaWVudC5leGFtcGxlLm9yZzCCASIwDQYJKoZIhvcNAQEBBQADggEPADCC\nAQoCggEBAK2Oyc+BV4N5pYcp47opUwsb2NaJq4X+d5Itq8whpFlZ9uCCHzF5TWSF\nXrpYscOp95veGPF42eT1grfxYyvjFotE76caHhBLCkIbBh6Vf222IGMwwBbSZfO9\nJ3eURtEADBvsZ117HkPVdjYqvt3Pr4RxdR12zG1TcBAoTLGchyr8nBqRADFhUTCL\nmsYaz1ADiQ/xbJN7VUNQpKhzRWHCdYS03HpbGjYCtAbl9dJnH2EepNF0emGiSPFq\ndf6taToyCr7oZjM7ufmKPjiiEDbeSYTf6kbPNmmjtoPNNLeejHjP9p0IYx7l0Gkj\nmx4kSMLp4vSDftrFgGfcxzaMmKBsosMCAwEAATANBgkqhkiG9w0BAQUFAAOCAQEA\nqzdDYbntFLPBlbwAQlpwIjvmvwzvkQt6qgZ9Y0oMAf7pxq3i9q7W1bDol0UF4pIM\nz3urEJCHO8w18JRlfOnOENkcLLLntrjOUXuNkaCDLrnv8pnp0yeTQHkSpsyMtJi9\nR6r6JT9V57EJ/pWQBgKlN6qMiBkIvX7U2hEMmhZ00h/E5xMmiKbySBiJV9fBzDRf\nmAy1p9YEgLsEMLnGjKHTok+hd0BLvcmXVejdUsKCg84F0zqtXEDXLCiKcpXCeeWv\nlmmXxC5PH/GEMkSPiGSR7+b1i0sSotsq+M3hbdwabpJ6nQLLbKkFSGcsQ87yL+gr\nSo6zun26vAUJTu1o9CIjxw==\n-----END CERTIFICATE-----\n"}'|jq
  • レスポンス(一部折り返しています)
{
  "type": "introspectionResponse",
  "resultCode": "A056001",
  "resultMessage": "[A056001] The access token is valid.",
  "action": "OK",
  "certificateThumbprint": "cBNP0zNH0fkcIQdVHdB8GDQAbaZyIjKXB0EVRTByJMU",
  "clientId": 591205987816490,
  "clientIdAliasUsed": false,
  "existent": true,
  "expiresAt": 1572412769000,
  "refreshable": true,
  "responseContent": "Bearer error=\"invalid_request\"",
  "scopes": [
    "openid",
    "payment"
  ],
  "subject": "testuser01",
  "sufficient": true,
  "usable": true
}

これによりリソースサーバーは、クライアントから受信したアクセストークンの有効性を確認し、そのトークンにひもづいている情報を取得することができました。

まとめ

本チュートリアルでは、FAPI に準拠したトークン授受・使用に対応した認可サーバーを構築するための、Authlete の設定を行いました。