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 クライアント認証」の設定については以下の記事をご参照ください。


動作のしくみ


“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 クライアント証明書を紐付けたアクセストークンのサポート
サポートする
サービス設定の「TLS クライアント証明書を紐付けたアクセストークンのサポート」


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


Authlete のクライアントアプリ開発者コンソールにアクセスし、 「基本情報」タブにおいて以下を設定します。
項目
TLS クライアント証明書を紐付けたアクセストークンの使用
使用する
クライアント設定の「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-----
MIIDPDCCAiQCCQDfkemtGUyxAjANBgkqhkiG9w0BAQsFADBgMQswCQYDVQQGEwJK
UDEOMAwGA1UECAwFVG9reW8xEzARBgNVBAcMCkNoaXlvZGEta3UxDzANBgNVBAoM
BkNsaWVudDEbMBkGA1UEAwwSY2xpZW50LmV4YW1wbGUub3JnMB4XDTIwMDUxMTEx
MDcyM1oXDTIxMDUxMTExMDcyM1owYDELMAkGA1UEBhMCSlAxDjAMBgNVBAgMBVRv
a3lvMRMwEQYDVQQHDApDaGl5b2RhLWt1MQ8wDQYDVQQKDAZDbGllbnQxGzAZBgNV
BAMMEmNsaWVudC5leGFtcGxlLm9yZzCCASIwDQYJKoZIhvcNAQEBBQADggEPADCC
AQoCggEBAMu7V2QyQk4iLPry0OsykR/8WO2aNOBNoVZYDexF1TsFv9s2S4PEDFEt
1BZrEmBe5HWpGb1iuDGG6wjAanEkea8AIUgslMsOOB0rQnbJA3nI5wktjCG2VzWo
zrmxAAaBM7ixaLPcJT1K0FSu4fzse3X9gtA2rtNGwk1JEMNIBpFghxw7zzUZBduS
lkVPPcQ3gPGVutiWfNPNPAqb4eMKwtuTPXbZSJ4RO9xGKMIuoaBWO9xPS6rNy+Kr
vOckNEsR+8PBYh9vzbmF0mDlk+BMd9sOGyylYBARFdqukMJnSbVcRvr+Gcaf+QSg
FRHkVGWXHXtMRI+MsCvOXrhmlJhxbdMCAwEAATANBgkqhkiG9w0BAQsFAAOCAQEA
JfEyXTEGdjchre2dFCa/WLdH5PZBqB+E0Umm+/I+puA+aUpkRaOfmIUPNaCZ1Lgg
mOo8BGuEG0oXpOSBoRkxf46d1bxJk5YSe+TgSxS6YX/mE6Lt1Aj7HjojlCuNFGS6
vzyEugQSsOjuoIPS32PvqmZ+80jzVNUsMD76xGl32ZsVeJmDnogVPZ4NlolQcmH2
bYjK00uPXaUU30IVu+UTrEBhe30My/aW31NbT2I7Ct52wwu67pyP3QOKmuLxMd6N
+KkxfIY23zla4herq9vWZejgfBfkAwOapeIIHGYtY7IFxm6nqufb4h71vjeoouOt
31J7bEcAU9WRP1Pr/X4hLA==
-----END CERTIFICATE-----",'\
'"parameters"':\
'"redirect_uri=https://client.example.org/cb/example.com'\
'&grant_type=authorization_code'\
'&client_id=...'\
'&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.
eyJzdWIiOiJ0ZXN0dXNlcjAxIiwic2NvcGUiOm51bGwsImlzcyI6Imh0dHBzOi8
vYXMuZXhhbXBsZS5jb20iLCJjbmYiOnsieDV0I1MyNTYiOiJPSURfU2MyeVJlVE
R4OVFTN2YxU01Vek54c2g3a2hKWW1hSXdxWHc4WXV3In0sImV4cCI6MTU5MTkzO
TMyNiwiaWF0IjoxNTkxODUyOTI2LCJjbGllbnRfaWQiOiIxNzU2NjE2MDYwMzc2
NiIsImp0aSI6InN6dDVnV0ZTb3ZGSVU1Ti13amRMeUZELUVpUzNyZEcyS1IzMTF
CMkY2RmMifQ.
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-----
MIIDPDCCAiQCCQDfkemtGUyxAjANBgkqhkiG9w0BAQsFADBgMQswCQYDVQQGEwJK
UDEOMAwGA1UECAwFVG9reW8xEzARBgNVBAcMCkNoaXlvZGEta3UxDzANBgNVBAoM
BkNsaWVudDEbMBkGA1UEAwwSY2xpZW50LmV4YW1wbGUub3JnMB4XDTIwMDUxMTEx
MDcyM1oXDTIxMDUxMTExMDcyM1owYDELMAkGA1UEBhMCSlAxDjAMBgNVBAgMBVRv
a3lvMRMwEQYDVQQHDApDaGl5b2RhLWt1MQ8wDQYDVQQKDAZDbGllbnQxGzAZBgNV
BAMMEmNsaWVudC5leGFtcGxlLm9yZzCCASIwDQYJKoZIhvcNAQEBBQADggEPADCC
AQoCggEBAMu7V2QyQk4iLPry0OsykR/8WO2aNOBNoVZYDexF1TsFv9s2S4PEDFEt
1BZrEmBe5HWpGb1iuDGG6wjAanEkea8AIUgslMsOOB0rQnbJA3nI5wktjCG2VzWo
zrmxAAaBM7ixaLPcJT1K0FSu4fzse3X9gtA2rtNGwk1JEMNIBpFghxw7zzUZBduS
lkVPPcQ3gPGVutiWfNPNPAqb4eMKwtuTPXbZSJ4RO9xGKMIuoaBWO9xPS6rNy+Kr
vOckNEsR+8PBYh9vzbmF0mDlk+BMd9sOGyylYBARFdqukMJnSbVcRvr+Gcaf+QSg
FRHkVGWXHXtMRI+MsCvOXrhmlJhxbdMCAwEAATANBgkqhkiG9w0BAQsFAAOCAQEA
JfEyXTEGdjchre2dFCa/WLdH5PZBqB+E0Umm+/I+puA+aUpkRaOfmIUPNaCZ1Lgg
mOo8BGuEG0oXpOSBoRkxf46d1bxJk5YSe+TgSxS6YX/mE6Lt1Aj7HjojlCuNFGS6
vzyEugQSsOjuoIPS32PvqmZ+80jzVNUsMD76xGl32ZsVeJmDnogVPZ4NlolQcmH2
bYjK00uPXaUU30IVu+UTrEBhe30My/aW31NbT2I7Ct52wwu67pyP3QOKmuLxMd6N
+KkxfIY23zla4herq9vWZejgfBfkAwOapeIIHGYtY7IFxm6nqufb4h71vjeoouOt
31J7bEcAU9WRP1Pr/X4hLA==
-----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\"}"
}
How did we do with this article?