コード交換のための証明キー(RFC 7636)

1. はじめに

RFC 7636 : コード交換のための証明キー(PKCE、「ピクシー」と発音)は、認可コードの傍受攻撃に対する対策に関する仕様です。

認可コードの傍受攻撃

この仕様は 2015 年 9 月に公開され、以下の変更が追加されました。

  1. 認可コードフローを使用する認可リクエストに code_challenge パラメータおよび code_challenge_method パラメータを追加。
  2. 認可リクエストに対応するトークンリクエストに code_verifier パラメータを追加。

この仕組みにより、認可サーバーはコードベリファイアを持たない悪意のあるアプリケーションからのトークンリクエストを拒否できるようになります。

2. PKCE 認可リクエスト

2.1 リクエストパラメータ

PKCE を使用する認可リクエストでは、code_challenge パラメータを必須とし、オプションで code_challenge_method パラメータを指定できます。

2.2 コードチャレンジ値

code_challenge パラメータの値は、コードベリファイアに対してコードチャレンジメソッド(計算ロジック)を適用することで求められます。

2.3 コードベリファイア値

コードベリファイアは、[A-Z] / [a-z] / [0-9] / "-" / "." / "_" / "~" の文字を使用するランダムな文字列であり、最小 43 文字、最大 128 文字の長さである必要があります。

PKCE 認可リクエスト

2.4 コードチャレンジメソッド

定義されているコードチャレンジメソッドは plainS256 です。それぞれのコードベリファイアをコードチャレンジに変換する計算ロジックは以下のとおりです。

メソッド ロジック
plain code_challenge = code_verifier
S256 code_challenge = BASE64URL-ENCODE(SHA256(ASCII(code_verifier)))

plain メソッドは入力を変更しないため、code_verifiercode_challenge の値は同じになります。

S256 メソッドは入力の SHA-256 ハッシュを計算し、そのハッシュ値を Base64-URL エンコードします。たとえば、code_verifier の値が dBjftJeZ4CVP-mB92K27uhbUJU1p1r_wW1gFWFOEjXk の場合、code_challenge の値は E9Melhoa2OwvFrEMTJguCHaoeK1t8URWbuGJSstw-cM になります。

S256 を使用する場合、クライアントアプリケーションは認可リクエストに code_challenge_method=S256 を含める必要があります。code_challenge_method パラメータを省略すると、認可サーバーはデフォルト値として plain を使用します。

3. PKCE 認可レスポンス

認可コードを生成した後、認可サーバーはそれをデータベースに保存し、リクエストに含まれていたコードチャレンジおよびコードチャレンジメソッドも記録します。

認可サーバーは、後にクライアントアプリケーションからのトークンリクエストを検証するために、保存されたコードチャレンジおよびコードチャレンジメソッドを使用します。

PKCE に特化した認可エンドポイントのレスポンスは特にありません。通常のレスポンスと同じです。

PKCE 認可レスポンス

4. PKCE トークンリクエスト

認可サーバーから認可コードを受け取った後、クライアントアプリケーションはトークンリクエストを行います。トークンリクエストには、認可コードに加えて、コードチャレンジを計算するために使用したコードベリファイアを含める必要があります。

コードベリファイアを指定するためのリクエストパラメータの名前は code_verifier です。

PKCE トークンリクエスト

5. PKCE トークンレスポンス

5.1 コードベリファイアの要求

PKCE をサポートする認可サーバーのトークンエンドポイントは、トークンリクエストに有効なコードベリファイアが含まれているかどうかを確認します。

このチェックは、grant_typeauthorization_code であり、トークンリクエストに含まれる認可コードがコードチャレンジと関連付けられている場合にのみ実行されます。

上記の条件を満たしているにもかかわらず、トークンリクエストに有効なコードベリファイアが含まれていない場合、そのリクエストは悪意のあるアプリケーションからのものと見なされ、認可サーバーはエラーレスポンスを返します。

5.2 コードベリファイアの検証

検証は 2 つのコードチャレンジの比較によって行われます。

1 つは 認可リクエスト に含まれ、データベースに保存されているもの。
もう 1 つは トークンリクエスト に含まれる code_verifier を使用して認可サーバーが計算したもの。

この 2 つが一致する場合、そのトークンリクエストは元の認可リクエストを行った正当なクライアントアプリケーションからのものと見なされます。それ以外の場合は、悪意のあるアプリケーションからのリクエストと見なされます。

5.3 アクセストークンの発行

トークンリクエストが検証されると、認可サーバーは通常どおりアクセストークンを発行します。

PKCE トークンレスポンス

6. Authlete を使用した PKCE の試行

6.1 準備

6.1.1 サインアップ

まだ Authlete アカウントをお持ちでない場合は、こちら から無料でサインアップできます。所要時間はわずか 5 分です。

authlete-signup

6.1.2 サービス ID およびクライアント ID

認可リクエストを作成するには、サービス ID および クライアント ID が必要です。

サービス ID および クライアント ID が必要な場合は、クイックセットアップガイド に従ってサービスとクライアントをセットアップしてください。

また、Authlete 管理コンソールサービス ID および クライアント ID を確認できます。

6.1.3 サービスおよびクライアント設定

以下のセクションでは、サービスとクライアント設定の方法について説明します。

表. サービス設定

タブ パラメータ
サービス設定 > エンドポイント > 基本設定 サポート可能な認可タイプ AUTHORIZATION_CODE を含める
サービス設定 > エンドポイント > 基本設定 サポート可能なレスポンスタイプ CODE を含める

エンドポイントの設定:

  1. サービス設定 > エンドポイント > 基本設定 に移動します。
  2. サポート可能な認可タイプ セクションで AUTHORIZATION_CODE を選択します。
  3. サポート可能なレスポンスタイプ セクションで CODE を選択します。
  4. 変更を保存 をクリックして変更を適用します。
pkce_1

表. クライアント設定

タブ パラメータ
クライアント設定 > 基本設定 > 一般 クライアントタイプ PUBLIC を選択
クライアント設定 > エンドポイント > 基本設定 サポート可能な認可タイプ AUTHORIZATION_CODE を含める
クライアント設定 > エンドポイント > 基本設定 サポート可能なレスポンスタイプ CODE を含める
クライアント設定 > エンドポイント > 基本設定 リダイレクトURI https://api.authlete.com/api/mock/redirection/service-api-key

クライアント基本設定の構成:

  1. クライアント設定 > 基本設定 > 一般 に移動します。
  2. クライアントタイプでは、公開 ラジオボタンを選択します。
  3. 変更を保存 をクリックして変更を適用します。
pkce_2

クライアントエンドポイントの構成:

  1. クライアント設定 > エンドポイント > 基本設定 に移動します。

  2. サポート可能な認可タイプ セクションで AUTHORIZATION_CODE を選択します。

  3. サポート可能なレスポンスタイプ セクションで CODE を選択します。

  4. リダイレクトURI の下で 追加 をクリックします。リダイレクトURLを次のように調整してください:

    • https://us.authlete.com/api/mock/redirection/service-id
  5. 変更を保存 をクリックして変更を適用します。

pkce_3

6.2 認可リクエスト

6.2.1 認可エンドポイントリクエスト

[OAuth2 Basic] で説明されているように、Authlete の認可 API は認可サーバーに代わって認可リクエストの検証を行います。

次の curl コマンドを使用して、認可サーバーから Authlete API へ認可リクエストを送信できます。<Service ID><Service Access Token><Client ID><Ticket> を適切な値に置き換えてください。

# Linux/Mac
curl -v -X POST "https://us.authlete.com/api/<Service ID e.g. 10738933707579>/auth/authorization" \
     -H "Authorization: Bearer <Service Access Token e.g. Xg6jVpJCvsaXvy2ks8R5WzjdMYlvQqOym3slDX0wNhQ>" \
     -d "ticket=5tqii9i_pUp1iteacZGUtdjikRnqGSrPwW7lqoH1Pcc" \
     -d "subject=john.s@example.com" \
     -d "response_type=code" \
     -d "client_id=<Client ID e.g.26478243745571>" \
     -d "redirect_uri=https://client.example.org/cb/example.com" \
     -d "code_challenge=E9Melhoa2OwvFrEMTJguCHaoeK1t8URWbuGJSstw-cM" \
     -d "code_challenge_method=S256"

# Windows (PowerShell)
curl.exe -v -X POST "https://us.authlete.com/api/<Service ID e.g. 10738933707579>/auth/authorization" `
      -H "Authorization: Bearer <Service Access Token e.g. Xg6jVpJCvsaXvy2ks8R5WzjdMYlvQqOym3slDX0wNhQ>" `
      -d "ticket=5tqii9i_pUp1iteacZGUtdjikRnqGSrPwW7lqoH1Pcc" `
      -d "subject=john.s@example.com" `
      -d "response_type=code" `
      -d "client_id=<Client ID e.g.26478243745571>" `
      -d "redirect_uri=https://client.example.org/cb/example.com" `
      -d "code_challenge=E9Melhoa2OwvFrEMTJguCHaoeK1t8URWbuGJSstw-cM" `
      -d "code_challenge_method=S256"

このリクエストには code_challenge および code_challenge_method パラメータが含まれています。PKCE を実装するには、これらのパラメータを含めてテストする必要があります。

また、Authlete 管理コンソールcode_challenge および code_challenge_method を設定することもできます。

6.2.2 認可レスポンス

リクエストが有効な場合、Authlete は次のようなレスポンスを返します。

{
    "action": "INTERACTION",
    "resultCode": "A004001",
    "resultMessage": "[A004001] Authlete has successfully issued a ticket to the service (API Key = 933860280) for the authorization request from the client (ID = 2800496004). [response_type=code, openid=false]",
    "ticket": "cElOaH9j4mS6AiIGR9oLqHlDn9jpvcNjqSgyRqfcmAE",
    "client": {...},
    "service": {...},
}

リソースオーナーが認証され、同意が得られた場合、認可サーバーは issue エンドポイントを呼び出して認可コードを取得します。

次の curl コマンドを使用して、トークンリクエストを送信できます。<Service ID><Service Access Token><Ticket> を適切な値に置き換えてください。

# Linux/Mac
curl -X POST "https://us.authlete.com/api/<Service ID e.g. 10738933707579>/auth/authorization/issue" \
     -H "Authorization: Bearer <Service Access Token e.g. Xg6jVpJCvsaXvy2ks8R5WzjdMYlvQqOym3slDX0wNhQ>" \
     -H "Content-Type: application/json" \
     -d '{ "ticket": "<Ticket e.g. cElOaH9j4mS6AiIGR9oLqHlDn9jpvcNjqSgyRqfcmAE>","subject": "testuser01"}'

# Windows
curl.exe -X POST "https://us.authlete.com/api/<Service ID e.g. 10738933707579>/auth/authorization/issue" `
     -H "Authorization: Bearer <Service Access Token e.g. Xg6jVpJCvsaXvy2ks8R5WzjdMYlvQqOym3slDX0wNhQ>" \
     -H "Content-Type: application/json" `
     -d '{ "ticket": "Ticket e.g. cElOaH9j4mS6AiIGR9oLqHlDn9jpvcNjqSgyRqfcmAE","subject": "testuser01"}'

リクエストが成功すると、Authlete は認可コードを発行します。

{
    "action": "LOCATION",
    "authorizationCode": "ILePyGjraVgeU_fzaQRfd0gv10pzxgcpHY_vHT2dsPI",
    "idToken": null,
    "jwtAccessToken": null,
    "responseContent": "https://client.example.org/cb/example.com?code=tJlGEKt8y0DLpjIA_jweywRtJs2fh83ZGNiwFRmIwYI&iss=https%3A%2F%2Fauthlete.com",
    "resultCode": "A040001",
    "resultMessage": "[A040001] The authorization request was processed successfully.",
    "ticketInfo": {
        "context": null
    }
}

6.3 トークンリクエスト

6.3.1 curl を使用したトークンリクエスト

認可サーバーがリダイレクトレスポンスをユーザーエージェントに送信し、ユーザーエージェントは次のような HTTP GET リクエストをクライアントに送信したと仮定します。

GET /cb/example.com?code=ILePyGjraVgeU_fzaQRfd0gv10pzxgcpHY_vHT2dsPI HTTP/1.1
Host: client.example.org

次の curl コマンドを実行して、トークンリクエストを送信できます。<Service ID><Service Access Token><Client ID><Code> を適切な値に置き換えてください。

curl -v -X POST https://us.authlete.com/api/<Service ID>/auth/token \
-H 'Content-Type: application/json' \
-H "Authorization: Bearer <Service Access Token>" \
-d '{ "parameters": "grant_type=authorization_code&code=<Code>&redirect_uri=https%3A%2F%2Fmy-client.example.com%2Fcb1&code_verifier=dBjftJeZ4CVP-mB92K27uhbUJU1p1r_wW1gFWFOEjXk", "clientId": "<Client ID>" }'

6.3.2 トークンレスポンス

指定した認可コードおよびコードベリファイアが有効である場合、次のような JSON 形式のアクセストークンが返されます。

{
   "resultMessage" : "[A050001] The token request (grant_type=authorization_code) was processed successfully.",
   "action" : "OK",
   "clientIdAliasUsed" : false,
   "subject" : "testuser01",
   "resultCode" : "A050001",
   "refreshTokenExpiresAt" : 1730552811449,
   "grantType" : "AUTHORIZATION_CODE",
   "accessToken" : "7FfwOnGjVHwxXhs2Wr67XV1-ZhQaoy3ctKcGkLyKxuY",
   "idToken" : "eyJhbGciOiJIUzI1NiJ9.eyJzdWIiOiJ0ZXN0dXNlcjAxIiwiYXVkIjpbIjEyODk4ODg0NTk2ODYzIl0sImlzcyI6Imh0dHBzOi8vYXV0aGxldGUuY29tIiwiZXhwIjoxNTU5MTA2ODE1LCJpYXQiOjE1NTkwMjA0MTUsIm5vbmNlIjoibi0wUzZfV3pBMk1qIn0.5uSFMTGnubyvtiExHc9l7HT9UsF8a_Qb0STtWzyclBk",
   "responseContent" : "{\"access_token\":\"7FfwOnGjVHwxXhs2Wr67XV1-ZhQaoy3ctKcGkLyKxuY\",\"refresh_token\":\"T1h7fJ6k55CyipDtXNPbzN8ta3FgAAf4QKjo36OVfIE\",\"scope\":\"openid\",\"id_token\":\"eyJhbGciOiJIUzI1NiJ9.eyJzdWIiOiJ0ZXN0dXNlcjAxIiwiYXVkIjpbIjEyODk4ODg0NTk2ODYzIl0sImlzcyI6Imh0dHBzOi8vYXV0aGxldGUuY29tIiwiZXhwIjoxNTU5MTA2ODE1LCJpYXQiOjE1NTkwMjA0MTUsIm5vbmNlIjoibi0wUzZfV3pBMk1qIn0.5uSFMTGnubyvtiExHc9l7HT9UsF8a_Qb0STtWzyclBk\",\"token_type\":\"Bearer\",\"expires_in\":86400}",
   "scopes" : [
      "openid"
   ],
   "accessTokenDuration" : 86400,
   "type" : "tokenResponse",
   "refreshToken" : "T1h7fJ6k55CyipDtXNPbzN8ta3FgAAf4QKjo36OVfIE",
   "accessTokenExpiresAt" : 1730552811449,
   "refreshTokenDuration" : 864000,
   "clientId" : 12898884596863
}

おめでとうございます! PKCE によって保護された認可コードフローを使用して、アクセストークンの取得に成功しました。

6.4 PKCE の設定

Authlete は PKCE のための設定項目を提供しています。PKCE はサービスまたはクライアントの設定で有効化できます。Authlete 管理コンソール にログインし、サービスを選択して 認可 タブを開くと、「認可コード交換用証明キー(PKCE)」という設定オプションが見つかります。

サービスの PKCE 設定

サービス設定で PKCE を有効にする方法:

  1. サービス設定 > エンドポイント > 認可 > 一般 に移動します。
  2. 認可コード交換用証明キー(PKCE) のオプションで、PKCEを必須にするコードチャレンジメソッドに対してS256を必須にする を有効化します。
  3. 変更を保存 をクリックして変更を適用します。
pkce_4

クライアントの PKCE 設定

クライアント設定で PKCE を有効にする方法:

  1. クライアント設定 > エンドポイント > 認可 > 一般 に移動します。
  2. 認可コード交換用証明キー(PKCE) のオプションで、PKCEを必須にするコードチャレンジメソッドに対してS256を必須にする を有効化します。
  3. 変更を保存 をクリックして変更を適用します。
pkce_5

認可コード交換用証明キー(PKCE)PKCEを必須にする が有効になっている場合、認可コードフローを使用する認可リクエストには code_challenge パラメータが必ず必要になります。デフォルトでは 無効 になっています。

セキュリティを強化するために、認可コード交換用証明キー(PKCE) を有効にしてください。これにより、code_challenge パラメータを含まない認可リクエストは認可サーバーによって拒否されるようになります。