TLS クライアント証明書を結びつけたアクセストークンの発行

TLS クライアント証明書を結びつけたアクセストークンの発行

はじめに

本記事では、“RFC 8705 OAuth 2.0 Mutual-TLS Client Authentication and Certificate-Bound Access Tokens” にて規定されている “Mutual-TLS certificate-bound access tokens” を利用するための、Authlete の設定手順を説明します。

“Mutual-TLS certificate-bound access tokens” は、「TLS クライアント認証」によるクライアント認証とは独立して動作します。「TLS クライアント認証」の設定については以下の記事をご参照ください。

動作のしくみ

mtls-certificate-bound-access-token_ja

“Mutual-TLS certificate-bound access tokens” の設定が有効化されている場合、Authlete の API は次の処理を行います。

  • /auth/token API

    • クライアント証明書 (clientCertificate) をリクエストパラメーターとして受け取り、発行するアクセストークンに結びつけます。
    • JWT 形式のアクセストークン が有効化されている場合には、発行するアクセストークンに、クライアント証明書の情報(サムプリント)を含めます。
  • /auth/introspection API

    • アクセストークン (token) とクライアント証明書 (clientCertificate) をリクエストパラメーターとして受け取り、両者が結びついているかを確認します。
  • /auth/introspection/standard API

    • アクセストークン (token) を、リクエストパラメーターのひとつ (parameters) に含まれる値として受け取り、そのトークンに結びついているクライアント証明書の情報(サムプリント)を返却します。

これにより、相互 TLS によってクライアント証明書を提示できたクライアントのみが、そのアクセストークンの所持を証明し、API リクエストに利用できる(言い換えれば、他のクライアントが利用できない)ようになります。


設定

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

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

項目
TLS クライアント証明書を紐付けたアクセストークンのサポート サポートする
fapi-service-certboundtoken
サービス設定の「TLS クライアント証明書を紐付けたアクセストークンのサポート」

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

Authlete のクライアントアプリ開発者コンソール にアクセスし、 「基本情報」タブにおいて以下を設定します。

項目
TLS クライアント証明書を紐付けたアクセストークンの使用 使用する
fapi-client-certboundtoken
クライアント設定の「TLS クライアント証明書を紐付けたアクセストークンの使用」

以上により、“Mutual-TLS certificate-bound access tokens” が利用可能となります。


実行例

以下は設定後の、各 API の実行例です。(読みやすさのため折り返しています)

/auth/token API

リクエスト

認可サーバーはクライアントとの相互 TLS 接続から得られたクライアント証明書を “clientCertificate” の値として指定し、Authlete にリクエストを送信します。

$ curl -s -X POST https://api.authlete.com/api/auth/token \
 -u ...:... \
 -H 'Content-type: application/json' \
 -d '{'\
    '"clientCertificate":"-----BEGIN CERTIFICATE-----\n
MIIDPDCCAiQCCQDfkemtGUyxAjANBgkqhkiG9w0BAQsFADBgMQswCQYDVQQGEwJK\n
UDEOMAwGA1UECAwFVG9reW8xEzARBgNVBAcMCkNoaXlvZGEta3UxDzANBgNVBAoM\n
BkNsaWVudDEbMBkGA1UEAwwSY2xpZW50LmV4YW1wbGUub3JnMB4XDTIwMDUxMTEx\n
MDcyM1oXDTIxMDUxMTExMDcyM1owYDELMAkGA1UEBhMCSlAxDjAMBgNVBAgMBVRv\n
a3lvMRMwEQYDVQQHDApDaGl5b2RhLWt1MQ8wDQYDVQQKDAZDbGllbnQxGzAZBgNV\n
BAMMEmNsaWVudC5leGFtcGxlLm9yZzCCASIwDQYJKoZIhvcNAQEBBQADggEPADCC\n
AQoCggEBAMu7V2QyQk4iLPry0OsykR/8WO2aNOBNoVZYDexF1TsFv9s2S4PEDFEt\n
1BZrEmBe5HWpGb1iuDGG6wjAanEkea8AIUgslMsOOB0rQnbJA3nI5wktjCG2VzWo\n
zrmxAAaBM7ixaLPcJT1K0FSu4fzse3X9gtA2rtNGwk1JEMNIBpFghxw7zzUZBduS\n
lkVPPcQ3gPGVutiWfNPNPAqb4eMKwtuTPXbZSJ4RO9xGKMIuoaBWO9xPS6rNy+Kr\n
vOckNEsR+8PBYh9vzbmF0mDlk+BMd9sOGyylYBARFdqukMJnSbVcRvr+Gcaf+QSg\n
FRHkVGWXHXtMRI+MsCvOXrhmlJhxbdMCAwEAATANBgkqhkiG9w0BAQsFAAOCAQEA\n
JfEyXTEGdjchre2dFCa/WLdH5PZBqB+E0Umm+/I+puA+aUpkRaOfmIUPNaCZ1Lgg\n
mOo8BGuEG0oXpOSBoRkxf46d1bxJk5YSe+TgSxS6YX/mE6Lt1Aj7HjojlCuNFGS6\n
vzyEugQSsOjuoIPS32PvqmZ+80jzVNUsMD76xGl32ZsVeJmDnogVPZ4NlolQcmH2\n
bYjK00uPXaUU30IVu+UTrEBhe30My/aW31NbT2I7Ct52wwu67pyP3QOKmuLxMd6N\n
+KkxfIY23zla4herq9vWZejgfBfkAwOapeIIHGYtY7IFxm6nqufb4h71vjeoouOt\n
31J7bEcAU9WRP1Pr/X4hLA==\n-----END CERTIFICATE-----\n",'\
'"parameters"':\
'"redirect_uri=https://client.example.org/cb/example.com'\
'&client_id=...'\
'&grant_type=authorization_code'\
'&code=smA...Gp4"'\
'}'

レスポンス

クライアント証明書に結びつけられたアクセストークンが発行されます。

{
  "type": "tokenResponse",
  "resultCode": "A050001",
  "resultMessage": "[A050001] The token request (grant_type=authorization_code) was processed successfully.",
  "accessToken": "AHu...MMs"
,  "accessTokenDuration": 86400,
  "accessTokenExpiresAt": 1591935416474,
  "action": "OK",
  "clientId": 17566160603766,
  "clientIdAliasUsed": false,
  "grantType": "AUTHORIZATION_CODE",
  "refreshToken": "_zkZeqy6pLdFerpwqJPh0S0gEZsSj9EbmwVsN92WNsE",
  "refreshTokenDuration": 864000,
  "refreshTokenExpiresAt": 1592713016474,
  "responseContent": "{\"access_token\":\"AHu...MMs\",
    \"refresh_token\":\"_zk...NsE\",
    \"scope\":null,
    \"token_type\":\"Bearer\",
    \"expires_in\":86400}",
  "subject": "testuser01"
}

JWT 形式のアクセストークンの場合には、以下のようなアクセストークンになります。

eyJraWQiOiIxIiwiYWxnIjoiRVMyNTYifQ.
eyJzdWIiOiJ0ZXN0dXNlcjAxIiwic2NvcGUiOm51bGwsImlzcyI6Imh0dHBzOi8v
YXMuZXhhbXBsZS5jb20iLCJjbmYiOnsieDV0I1MyNTYiOiJPSURfU2MyeVJlVER4
OVFTN2YxU01Vek54c2g3a2hKWW1hSXdxWHc4WXV3In0sImV4cCI6MTU5MTkzOTMy
NiwiaWF0IjoxNTkxODUyOTI2LCJjbGllbnRfaWQiOiIxNzU2NjE2MDYwMzc2NiIs
Imp0aSI6InN6dDVnV0ZTb3ZGSVU1Ti13amRMeUZELUVpUzNyZEcyS1IzMTFCMkY2
RmMifQ.
Pw3IntOrXDqW3Z5eoGSeeJaPG5n1aKEVokgbUh_BJpqzuZ6F6GDDLhj98XBvjii
x9oAmaTpy39ijl1vcq3r1BA

このアクセストークンのペイロード部には、以下のように、証明書のサムプリントが含まれます。

{
   "sub": "testuser01",
   "scope": null,
   "iss": "https://as.example.com",
   "cnf": {
       "x5t#S256": "OID\_Sc2yReTDx9QS7f1SMUzNxsh7khJYmaIwqXw8Yuw"
   },
   "exp": 1591939326,
   "iat": 1591852926,
   "client_id": "17566160603766",
   "jti": "szt5gWFSovFIU5N-wjdLyFD-EiS3rdG2KR311B2F6Fc"
}

/auth/introspection API

リクエスト

リソースサーバーはクライアントとの相互 TLS 接続から得られたクライアント証明書を “clientCertificate” の値として指定し、Authlete にリクエストを送信します。

curl -s -X POST https://api.authlete.com/api/auth/introspection \
-u ...:... \
-H 'Content-type: application/json' \
-d '{
  "token": "AHu...MMs",
  "clientCertificate": "-----BEGIN CERTIFICATE-----\n
  MIIDPDCCAiQCCQDfkemtGUyxAjANBgkqhkiG9w0BAQsFADBgMQswCQYDVQQGEwJK\n
  UDEOMAwGA1UECAwFVG9reW8xEzARBgNVBAcMCkNoaXlvZGEta3UxDzANBgNVBAoM\n
  BkNsaWVudDEbMBkGA1UEAwwSY2xpZW50LmV4YW1wbGUub3JnMB4XDTIwMDUxMTEx\n
  MDcyM1oXDTIxMDUxMTExMDcyM1owYDELMAkGA1UEBhMCSlAxDjAMBgNVBAgMBVRv\n
  a3lvMRMwEQYDVQQHDApDaGl5b2RhLWt1MQ8wDQYDVQQKDAZDbGllbnQxGzAZBgNV\n
  BAMMEmNsaWVudC5leGFtcGxlLm9yZzCCASIwDQYJKoZIhvcNAQEBBQADggEPADCC\n
  AQoCggEBAMu7V2QyQk4iLPry0OsykR/8WO2aNOBNoVZYDexF1TsFv9s2S4PEDFEt\n
  1BZrEmBe5HWpGb1iuDGG6wjAanEkea8AIUgslMsOOB0rQnbJA3nI5wktjCG2VzWo\n
  zrmxAAaBM7ixaLPcJT1K0FSu4fzse3X9gtA2rtNGwk1JEMNIBpFghxw7zzUZBduS\n
  lkVPPcQ3gPGVutiWfNPNPAqb4eMKwtuTPXbZSJ4RO9xGKMIuoaBWO9xPS6rNy+Kr\n
  vOckNEsR+8PBYh9vzbmF0mDlk+BMd9sOGyylYBARFdqukMJnSbVcRvr+Gcaf+QSg\n
  FRHkVGWXHXtMRI+MsCvOXrhmlJhxbdMCAwEAATANBgkqhkiG9w0BAQsFAAOCAQEA\n
  JfEyXTEGdjchre2dFCa/WLdH5PZBqB+E0Umm+/I+puA+aUpkRaOfmIUPNaCZ1Lgg\n
  mOo8BGuEG0oXpOSBoRkxf46d1bxJk5YSe+TgSxS6YX/mE6Lt1Aj7HjojlCuNFGS6\n
  vzyEugQSsOjuoIPS32PvqmZ+80jzVNUsMD76xGl32ZsVeJmDnogVPZ4NlolQcmH2\n
  bYjK00uPXaUU30IVu+UTrEBhe30My/aW31NbT2I7Ct52wwu67pyP3QOKmuLxMd6N\n
  +KkxfIY23zla4herq9vWZejgfBfkAwOapeIIHGYtY7IFxm6nqufb4h71vjeoouOt\n
  31J7bEcAU9WRP1Pr/X4hLA==\n-----END CERTIFICATE-----"
}'
}'

レスポンス

アクセストークン (token) とクライアント証明書 (clientCertificate) の結びつきが確認できた場合には、以下のようなレスポンスが返却されます。このレスポンスには、クライアント証明書のサムプリント(certificateThumbprint) が含まれています。

{
    "type": "introspectionResponse",
    "resultCode": "A056001",
    "resultMessage": "[A056001] The access token is valid.",
    "action": "OK",
    "certificateThumbprint": "OID_Sc2yReTDx9QS7f1SMUzNxsh7khJYmaIwqXw8Yuw",
    "clientId": ...,
    "clientIdAliasUsed": false,
    "existent": true,
    "expiresAt": 1591936228000,
    "refreshable": true,
    "responseContent": "Bearer error=\"invalid_request\"",
    "subject": "testuser01",
    "sufficient": true,
    "usable": true
}

/auth/introspection/standard API

リクエスト

リソースサーバーは認可サーバーのトークンイントロスペクションエンドポイントに、対象のアクセストークンを送信します。認可サーバーはそのリクエスト内容を Authlete に転送します。

$ curl -s -X POST https://api.authlete.com/api/auth/introspection/standard \
-u ...:... \
-H 'Content-type: application/json' \
-d '{ "parameters":"token=AHu...MMs
" }'

レスポンス

アクセストークンに結びつくクライアント証明書が確認できた場合には、Authlete はその証明書のサムプリントを含む responseContent を返却します。認可サーバーはこの responseContent をリソースサーバーに、トークンイントロスペクションエンドポイントからのレスポンスとして返却します。

{
    "type": "standardIntrospectionResponse",
    "resultCode": "A145001",
    "resultMessage": "[A145001] Introspection was performed successfully (type=access_token, active=true).",
    "action": "OK",
    "responseContent": {
        "sub": "testuser01",
        "scope": null,
        "iss": "https://as.example.com",
        "active": true,
        "cnf": {
            "x5t#S256": "OID_Sc2yReTDx9QS7f1SMUzNxsh7khJYmaIwqXw8Yuw"
        },
        "token_type": "Bearer",
        "exp": 1591936228,
        "client_id": "17566160603766"
    }
}