認可コード + PKCE フロー
認可コードフローに PKCE(Proof Key for Code Exchange)を追加したフロー。SPAやネイティブアプリなど、client_secret を安全に保管できない環境で使用します。現在最も推奨されるフローです。
PKCE の仕組み
code_verifier の生成
クライアントが暗号論的に安全な43〜128文字のランダム文字列を生成します。
code_verifier = dBjftJeZ4CVP-mB92K27uhbUJU1p1r_wW1gFWFOEjXkcode_challenge の計算
code_verifier の SHA-256 ハッシュを Base64URL エンコードします。
code_challenge = BASE64URL(SHA256(code_verifier))認可リクエストで送信
code_challenge のみを認可サーバーに送信します。code_verifier は送信しません。
GET /authorize?...&code_challenge=E9Melhoa2OwvFrEMTJguCHaoeK1t8URWbuGJSstw-cM&code_challenge_method=S256トークンリクエストで検証
トークン交換時に code_verifier を送信し、認可サーバーが SHA256(code_verifier) == code_challenge を検証します。
POST /token ... code_verifier=dBjftJeZ4CVP-mB92K27uhbUJU1p1r_wW1gFWFOEjXk通常の認可コードフローとの違い
| 項目 | 認可コード | 認可コード + PKCE |
|---|---|---|
| クライアント認証 | client_secret を使用 | code_verifier / code_challenge を使用 |
| 対象クライアント | 機密クライアント(サーバーサイド) | パブリッククライアント(SPA、ネイティブ) |
| 認可コード傍受対策 | client_secret で保護 | PKCE チャレンジで保護 |
| 認可リクエスト | 標準パラメータのみ | code_challenge, code_challenge_method を追加 |
| トークンリクエスト | client_secret を送信 | code_verifier を送信 |
シーケンス図
各ステップの詳細
クライアント → クライアント
クライアントが code_verifier(ランダム文字列)を生成し、そのSHA-256ハッシュから code_challenge を計算します。
リクエスト
GENERATE (ローカル処理)
code_verifier = dBjftJeZ4CVP-mB92K27uhbUJU1p1r_wW1gFWFOEjXk code_challenge = E9Melhoa2OwvFrEMTJguCHaoeK1t8URWbuGJSstw-cM code_challenge_method = S256
セキュリティノート
- !code_verifier は43〜128文字のランダム文字列
- !code_challenge = BASE64URL(SHA256(code_verifier))
- !S256メソッドを必ず使用する(plainは非推奨)
ブラウザ → 認可サーバー
認可リクエストに code_challenge と code_challenge_method を含めて送信します。code_verifier はクライアント側で安全に保持します。
リクエスト
GET /authorize
response_type=code&client_id=my-spa-app&redirect_uri=https://example.com/callback&scope=openid profile&state=random-csrf-token&code_challenge=E9Melhoa2OwvFrEMTJguCHaoeK1t8URWbuGJSstw-cM&code_challenge_method=S256
セキュリティノート
- !code_challenge のみ送信し、code_verifier は送信しない
- !認可サーバーが code_challenge を保存する
認可サーバー → ブラウザ
認可サーバーがユーザーにログイン画面と同意画面を表示します。通常の認可コードフローと同じです。
リクエスト
GET /login
レスポンス
POST /login
username=user&password=****&consent=approve
セキュリティノート
- !通常の認可コードフローと同じ認証プロセス
認可サーバー → クライアント
認可サーバーが認可コード付きでリダイレクトします。通常の認可コードフローと同じです。
リクエスト
GET https://example.com/callback
code=SplxlOBeZQQYbYS6WxSbIA&state=random-csrf-token
セキュリティノート
- !認可コード単体では不十分。code_verifier が必要
クライアント → 認可サーバー
クライアントが認可コードと一緒に code_verifier を送信します。認可サーバーは保存していた code_challenge と照合して検証します。
リクエスト
POST /token
Content-Type: application/x-www-form-urlencoded
grant_type=authorization_code&code=SplxlOBeZQQYbYS6WxSbIA&redirect_uri=https://example.com/callback&client_id=my-spa-app&code_verifier=dBjftJeZ4CVP-mB92K27uhbUJU1p1r_wW1gFWFOEjXk
セキュリティノート
- !client_secret の代わりに code_verifier で証明
- !認可サーバーは SHA256(code_verifier) == code_challenge を検証
- !認可コードを傍受されても code_verifier がなければトークンを取得できない
認可サーバー → クライアント
PKCE検証に成功すると、認可サーバーがアクセストークンを返します。
リクエスト
POST /token
レスポンス
200 /token
{ "access_token": "eyJhbGciOiJSUzI1NiIs...", "token_type": "Bearer", "expires_in": 3600, "scope": "openid profile" }
セキュリティノート
- !パブリッククライアントのためリフレッシュトークンは返さない場合が多い
クライアント → リソースサーバー
取得したアクセストークンでリソースサーバーにアクセスします。
リクエスト
GET /api/userinfo
Authorization: Bearer eyJhbGciOiJSUzI1NiIs...
セキュリティノート
- !通常の認可コードフローと同じリソースアクセス
- +SPA(シングルページアプリケーション)
- +モバイル/ネイティブアプリケーション
- +client_secret を安全に保管できないパブリッククライアント
- +現在最も推奨されるフロー(RFC 7636、OAuth 2.1)
- -サーバー間通信 → クライアントクレデンシャルフローを使用
- -入力デバイスがないIoT → デバイス認可フローを使用
セキュリティ上の考慮事項
- !code_challenge_method は必ず S256 を使用する
- !code_verifier は暗号論的に安全な乱数で生成する
- !code_verifier はリクエストごとに新規生成する
- !インプリシットフローの代わりにこのフローを使用すること
- !state パラメータも併用して CSRF を防止する