OAuth 2.0

OAuth 2.0 の主要なフロー(特に安全に使うべき Authorization Code + PKCE)を中心に、図(テキスト図)で手順を示し、実装時に注意すべき点を箇条書きでまとめます。

要点(先に結論)

  • ユーザー認可を安全に行うには「Authorization Code フロー + PKCE」を使う(公開クライアント=ブラウザ/ネイティブ向け)。
  • サーバー間認証(マシン対マシン)は「Client Credentials」を使う。
  • トークン取り扱い(保存・伝搬)とリダイレクト検証、TLS、スコープ設計が最重要。
  • 認証(ユーザー識別)が目的なら OpenID Connect(OIDC)を併用する。
  1. Authorization Code フロー(推奨:PKCE付き) — テキスト図 クライアント = アプリ(例:SPA / ネイティブ / サーバーサイド) AS = Authorization Server(認可サーバ) RS = Resource Server(API)

ユーザーエージェント(ブラウザ)を介した典型フロー(PKCEあり):

  1. クライアント生成: code_verifier と code_challenge = HASH(code_verifier)
  2. ブラウザをリダイレクト → AS の認可エンドポイント GET /authorize?response_type=code&client_id=…&redirect_uri=…&scope=…&state=…&code_challenge=…&code_challenge_method=S256
  3. ユーザーが認可(ログイン、同意)
  4. AS がブラウザを redirect_uri?code=AUTH_CODE&state=… へリダイレクト
  5. クライアントが AUTH_CODE を受け取り、トークンエンドポイントへ POST(バックエンド): POST /token { grant_type=authorization_code, code=AUTH_CODE, redirect_uri=…, code_verifier=… }
  6. AS が code_verifier を検証 → アクセストークン(access_token) と(必要なら)リフレッシュトークン を返す
  7. クライアントが access_token を用いて RS に API リクエスト: Authorization: Bearer ACCESS_TOKEN

簡易ASCII図: User Browser Client Authorization Server Resource Server | | | | |—(1) open auth-> | | | |—(2) /authorize->|—- redirect —> | | | <—(4) redirect with code ————-| | | |—(5) POST /token-> | | | | <-(6) access_token–| | | |—(7) API call w/ token ———————> | | | | |

  1. Client Credentials フロー(マシン対マシン)
  • クライアント(機械)が直接トークンを取得: POST /token { grant_type=client_credentials, client_id, client_secret, scope }
  • AS は access_token を返す。ユーザーコンテキストは無し。
  1. その他のフロー(注意)
  • Implicit フロー:クライアント側で直接トークンを受け取る方式。現在はセキュリティ上非推奨(PKCE 登場以降)。
  • Resource Owner Password Credentials:ユーザー名/パスワードを直接クライアントに預ける方式。基本非推奨。
  • OpenID Connect:認証が必要なら Authorization Code + OIDC (id_token) を使う。
  1. 実装で注意すべき点(チェックリスト)
  • TLS(HTTPS)必須
    • 認可エンドポイント・トークンエンドポイント・API は常に HTTPS のみ。
  • PKCE の採用(公開クライアント用)
    • code_verifier を使い、S256 を推奨。必須にする。
  • state パラメータで CSRF 対策
    • 生成・検証を必ず行い、一度のみ有効にする。
  • redirect_uri を厳密に検証
    • ワイルドカード禁止。登録済みの完全一致のみ受け入れる。
  • クライアント認証
    • 機密クライアント(サーバー)には client_secret または mTLS を必須にする。
  • トークンの保管方法
    • ブラウザ SPA は localStorage に保存しない(XSS に弱い)。
    • 可能なら httpOnly, Secure, SameSite=strict クッキーを使う(ただし CSRF 対策が必要)。
    • ネイティブアプリは OS の安全なストア(Keychain, Keystore)。
  • アクセストークンの寿命を短く、リフレッシュは限定的に
    • 短命アクセストークン + リフレッシュトークン回転(rotation)を推奨。
    • リフレッシュトークンの再利用検出(reuse detection)を実装する。
  • スコープの最小化
    • 必要最小限の権限のみ付与。スコープで細かく分離する。
  • トークンの検証(Resource Server)
    • JWT: iss, aud, exp, nbf, iat をチェックし、署名(kid)を検証する。
    • 署名鍵は公開鍵(JWKs)で取得してキャッシュ→ローテーション対応。
    • オペレーションはトークンインスペクション(introspection)やJWT検証を用いる。
  • Revocation(取り消し)と Logout
    • リフレッシュトークンやアクセストークンの取り消しエンドポイントを用意する。
  • ロギングと監査
    • 異常なトークン発行やリフレッシュの試行をログし、アラート化する。
  • エラーハンドリングをユーザーフレンドリーに
    • 失敗時はユーザーに適切な再認証導線を示す。
  • フロントエンドでの CORS と Cookie の扱い
    • Cookie を使う場合は SameSite/HttpOnly を正しく設定、CORS の設定は最小権限で。
  • セッション固定攻撃対策
    • 新しい認可で既存セッションが乗っ取られないように、セッション ID を再生成するなど。
  • リダイレクトURI の開発/本番の分離
    • 登録済み URI を厳密に管理。誤ってワイルドカードや短縮URIを使わない。
  1. セキュリティ攻撃と対策(代表例)
  • CSRF(認可リクエストの偽造)
    • 対策:state パラメータ、SameSite cookie、CSRF トークン
  • リダイレクト先にトークンが漏れる(referrer / history)
    • 対策:認可コードフローを使いトークンを URL に含めない。Referrer-Policy ヘッダ。
  • コードインターセプト(authorization code interception)
    • 対策:PKCE を使用し、code_verifier で検証。
  • XSS によるトークン窃取
    • 対策:XSS を防ぐ(CSP, 入力検証等)。トークンを JS が読めない httpOnly cookie に保存。
  • リフレッシュトークン盗難
    • 対策:回転、再利用検出、最小権限、短寿命。
  1. 運用の注意
  • 定期的に公開鍵や設定(.well-known/openid-configuration)を確認しキャッシュポリシーを設定する。
  • 同意画面の文言は明確に(ユーザが何を許可するか分かるように)。
  • 監査ログと異常検知(不審な IP、短時間に大量のトークン発行など)。
  • ライブラリ利用:成熟した OAuth/OIDC ライブラリを使う(自前でプロトコル実装しない)。
  • 規格のバージョン差違いに注意(RFC と実装の差)。
  1. 実装例(簡潔なベストプラクティス)
  • Web サーバー(Confidential Client)
    • Authorization Code (no PKCE necessary but allowed) → サーバーで code を交換 → access/refresh を安全にサーバー側で保管(DB、暗号化)。
    • クライアントはセッション cookie を使ってユーザーセッションを管理。API 呼び出しはサーバー側が access_token を付与して行う(BFF パターン)。
  • SPA(Public Client)
    • Authorization Code + PKCE を使用。access_token はメモリか短期 cookie に保存、長期保存を避ける。できれば BFF(Backend For Frontend)を使ってブラウザにトークンを渡さない方法を検討。
  1. 推奨ライブラリ/仕様参照
  • RFC 6749 OAuth 2.0(仕様)
  • RFC 8252(ネイティブアプリ向け)
  • OAuth 2.0 for Browser-Based Apps(IETF draft 推奨事項)
  • OpenID Foundation の OIDC
  • 実装ライブラリの例:
    • サーバー:oauth2-proxy, Keycloak, Auth0, Okta, Hydra(ORY)
    • クライアント:OAuth client libraries(各言語の公式・実績あるライブラリ)

最後に簡単な実践チェックリスト(最小限)

  • HTTPS を全エンドポイントで有効にしている
  • Authorization Code + PKCE を採用している(公開クライアント)
  • state を使っている(検証している)
  • redirect_uri を完全一致で検証している
  • トークンの保存方法を安全に設計している(httpOnly cookie / OS keystore)
  • アクセストークンを短時間にしている、リフレッシュトークンは回転している
  • トークンの署名・検証(iss,aud,exp)をしている
  • エラーログ・監査・異常検出を導入している

必要なら図をより見やすい形式(Mermaid や SVG)で作成しますが、表示可能か教えてください。あるいは、あなたの環境(SPA / ネイティブ / サーバーサイド / BFF)を教えてもらえれば、そのケースに合わせた詳細手順と実装サンプル(要求パラメータ、サーバー実装スニペット、CURL コマンド例)を出します。