OAuth 2.0 入門 - 認可フレームワークの仕組みとフロー

OAuth 2.0は、サードパーティアプリケーションがユーザーのリソースに対して限定的なアクセス権を取得するための認可フレームワーク。RFC 6749で定義されている。

認可と認証の違い

OAuth 2.0を理解する上で、認可(Authorization)認証(Authentication) の区別は重要。

概念目的答える問いOAuth 2.0での扱い
認証(Authentication)「誰であるか」を確認するAre you who you claim to be?OAuth 2.0自体は認証の仕組みを持たない
認可(Authorization)「何ができるか」を許可するWhat are you allowed to do?OAuth 2.0の主要な目的

OAuth 2.0は認可のためのフレームワークであり、認証プロトコルではない。認証が必要な場合はOAuth 2.0の上に構築されたOpenID Connect(OIDC)を使用する。

登場人物(ロール)

OAuth 2.0には4つの主要なロールが定義されている。

ロール説明具体例
リソースオーナー(Resource Owner)保護されたリソースへのアクセスを許可できるエンティティ。通常はエンドユーザーGoogleアカウントの所有者
クライアント(Client)リソースオーナーの代理としてリソースにアクセスするアプリケーションサードパーティのWebアプリ、モバイルアプリ
認可サーバー(Authorization Server)リソースオーナーを認証し、アクセストークンを発行するサーバーGoogle OAuth、Auth0、Keycloak
リソースサーバー(Resource Server)保護されたリソースをホストし、アクセストークンを検証してリクエストに応答するサーバーGoogle Calendar API、GitHub API

認可サーバーとリソースサーバーは同一のサーバーである場合もある。

クライアントの種類

OAuth 2.0ではクライアントを機密性の観点で2つに分類する。

種類説明
コンフィデンシャルクライアントクライアントシークレットを安全に保持できるサーバーサイドWebアプリ
パブリッククライアントクライアントシークレットを安全に保持できないSPA、モバイルアプリ、デスクトップアプリ

パブリッククライアントではクライアントシークレットに依存したセキュリティは確保できないため、PKCEなどの追加対策が必要になる。

認可グラント種別

認可グラント(Authorization Grant)は、クライアントがアクセストークンを取得するための方法。用途に応じて複数の種別が定義されている。

グラント種別用途クライアント種別RFC
認可コード(Authorization Code)サーバーサイドWebアプリコンフィデンシャルRFC 6749
認可コード + PKCESPA、モバイルアプリパブリックRFC 7636
クライアントクレデンシャル(Client Credentials)マシン間通信(M2M)コンフィデンシャルRFC 6749
デバイスコード(Device Authorization)入力制限のあるデバイス(TV、IoT等)パブリックRFC 8628
インプリシット(Implicit)SPA(旧方式)パブリックRFC 6749
リソースオーナーパスワード(ROPC)レガシー移行用両方RFC 6749

注意: インプリシットグラントとリソースオーナーパスワードクレデンシャル(ROPC)は、セキュリティ上の問題からOAuth 2.1ドラフトで廃止予定。新規実装では使用しないこと。インプリシットグラントはトークンがURLフラグメントに露出するリスクがあり、代わりに認可コード + PKCEを使用する。

認可コードフロー

最も一般的なOAuth 2.0フロー。サーバーサイドWebアプリケーション向け。

sequenceDiagram
    participant User as リソースオーナー<br/>(ブラウザ)
    participant Client as クライアント<br/>(Webアプリ)
    participant AuthServer as 認可サーバー
    participant Resource as リソースサーバー

    User->>Client: 1. サービスにアクセス
    Client->>User: 2. 認可サーバーへリダイレクト
    User->>AuthServer: 3. 認可リクエスト<br/>(client_id, redirect_uri, scope, state)
    AuthServer->>User: 4. ログイン画面表示
    User->>AuthServer: 5. 認証 + 同意
    AuthServer->>User: 6. 認可コードとともにリダイレクト
    User->>Client: 7. 認可コード + state
    Client->>AuthServer: 8. トークンリクエスト<br/>(認可コード + client_secret)
    AuthServer->>Client: 9. アクセストークン + リフレッシュトークン
    Client->>Resource: 10. APIリクエスト<br/>(Authorization: Bearer トークン)
    Resource->>Client: 11. リソース応答

認可リクエストのパラメータ

GET /authorize?
  response_type=code
  &client_id=CLIENT_ID
  &redirect_uri=https://example.com/callback
  &scope=read:user
  &state=xyz123
パラメータ必須説明
response_typecode を指定
client_id事前に認可サーバーに登録したクライアント識別子
redirect_uri認可後のリダイレクト先(事前登録と一致が必要)
scope要求するアクセス範囲
stateCSRF対策のためのランダム文字列。レスポンスで返却され、クライアント側で検証する

トークンリクエスト

POST /token HTTP/1.1
Host: auth.example.com
Content-Type: application/x-www-form-urlencoded

grant_type=authorization_code
&code=AUTHORIZATION_CODE
&redirect_uri=https://example.com/callback
&client_id=CLIENT_ID
&client_secret=CLIENT_SECRET

トークンレスポンス

{
  "access_token": "eyJhbGciOiJSUzI1NiIs...",
  "token_type": "Bearer",
  "expires_in": 3600,
  "refresh_token": "dGhpcyBpcyBhIHJlZnJlc2g...",
  "scope": "read:user"
}

PKCE(Proof Key for Code Exchange)

PKCE(ピクシー)は、認可コードフローをパブリッククライアントでも安全に利用するための拡張仕様(RFC 7636)。認可コードの横取り攻撃(Authorization Code Interception Attack)を防ぐ。

PKCEの仕組み

sequenceDiagram
    participant Client as クライアント
    participant AuthServer as 認可サーバー

    Note over Client: code_verifier を生成<br/>(43〜128文字のランダム文字列)
    Note over Client: code_challenge を計算<br/>(SHA256ハッシュのBase64URL)
    Client->>AuthServer: 認可リクエスト<br/>(+ code_challenge, code_challenge_method)
    AuthServer-->>Client: 認可コード
    Client->>AuthServer: トークンリクエスト<br/>(認可コード + code_verifier)
    Note over AuthServer: code_verifierを検証<br/>(ハッシュがcode_challengeと一致するか)
    AuthServer-->>Client: アクセストークン

code_verifier と code_challenge

code_verifier  = ランダムな43〜128文字の文字列([A-Z] / [a-z] / [0-9] / "-" / "." / "_" / "~")
code_challenge = BASE64URL(SHA256(code_verifier))
パラメータ送信タイミング説明
code_challenge認可リクエスト時code_verifierのSHA256ハッシュ(Base64URL)
code_challenge_method認可リクエスト時S256(推奨)または plain
code_verifierトークンリクエスト時元のランダム文字列

PKCEにより、認可コードを傍受した攻撃者がトークンを取得しようとしても、code_verifier を知らないためトークン交換に失敗する。

PKCE付き認可リクエスト例

GET /authorize?
  response_type=code
  &client_id=CLIENT_ID
  &redirect_uri=https://example.com/callback
  &scope=read:user
  &state=xyz123
  &code_challenge=E9Melhoa2OwvFrEMTJguCHaoeK1t8URWbuGJSstw-cM
  &code_challenge_method=S256

ベストプラクティス: OAuth 2.1ドラフトでは、コンフィデンシャルクライアントを含むすべてのクライアントでPKCEの使用が必須とされている。

クライアントクレデンシャルフロー

ユーザーが介在しない、マシン間通信(M2M)向けのフロー。クライアント自身の資格情報でトークンを取得する。

sequenceDiagram
    participant Client as クライアント<br/>(バックエンドサービス)
    participant AuthServer as 認可サーバー
    participant Resource as リソースサーバー

    Client->>AuthServer: トークンリクエスト<br/>(client_id + client_secret)
    AuthServer->>Client: アクセストークン
    Client->>Resource: APIリクエスト<br/>(Bearer トークン)
    Resource->>Client: リソース応答
POST /token HTTP/1.1
Host: auth.example.com
Content-Type: application/x-www-form-urlencoded

grant_type=client_credentials
&client_id=CLIENT_ID
&client_secret=CLIENT_SECRET
&scope=api:read

デバイスコードフロー

スマートTVやIoTデバイスなど、ブラウザや十分な入力手段を持たないデバイス向けのフロー(RFC 8628)。

sequenceDiagram
    participant Device as デバイス
    participant AuthServer as 認可サーバー
    participant User as ユーザー<br/>(スマホ/PC)

    Device->>AuthServer: デバイス認可リクエスト<br/>(client_id, scope)
    AuthServer->>Device: device_code, user_code,<br/>verification_uri
    Device->>User: 「https://example.com/device に<br/>コード ABC-123 を入力してください」
    User->>AuthServer: verification_uriにアクセスし<br/>user_codeを入力 + 認証
    loop ポーリング
        Device->>AuthServer: トークンリクエスト<br/>(device_code)
        AuthServer->>Device: authorization_pending / トークン
    end

アクセストークンとリフレッシュトークン

アクセストークン

リソースサーバーへのアクセスに使用するトークン。

特性説明
有効期限短い(数分〜数時間が一般的)
用途APIリクエストのAuthorizationヘッダーに付与
形式不透明文字列(Opaque Token)またはJWT
送信方法Authorization: Bearer <token>
GET /api/user/profile HTTP/1.1
Host: api.example.com
Authorization: Bearer eyJhbGciOiJSUzI1NiIs...

リフレッシュトークン

アクセストークンの有効期限切れ時に、新しいアクセストークンを取得するためのトークン。

特性説明
有効期限長い(数日〜数週間、または無期限)
用途アクセストークンの再取得
保管場所サーバーサイドに安全に保管(ブラウザのlocalStorageは避ける)
送信先認可サーバーのトークンエンドポイントのみ

トークンリフレッシュフロー

sequenceDiagram
    participant Client as クライアント
    participant AuthServer as 認可サーバー
    participant Resource as リソースサーバー

    Client->>Resource: APIリクエスト(期限切れトークン)
    Resource->>Client: 401 Unauthorized
    Client->>AuthServer: リフレッシュリクエスト<br/>(grant_type=refresh_token)
    AuthServer->>Client: 新しいアクセストークン<br/>(+ 新しいリフレッシュトークン)
    Client->>Resource: APIリクエスト(新しいトークン)
    Resource->>Client: リソース応答
POST /token HTTP/1.1
Host: auth.example.com
Content-Type: application/x-www-form-urlencoded

grant_type=refresh_token
&refresh_token=dGhpcyBpcyBhIHJlZnJlc2g...
&client_id=CLIENT_ID

トークンの比較

項目アクセストークンリフレッシュトークン
有効期限短い(分〜時間)長い(日〜週)
送信先リソースサーバー認可サーバーのみ
漏洩時の影響限定的(短期間で失効)重大(新しいトークンを無制限に取得可能)
保管要件メモリ内が望ましいサーバーサイドに暗号化保管

スコープ

スコープは、アクセストークンに付与される権限の範囲を定義する仕組み。クライアントが必要最小限の権限のみを要求できるようにする(最小権限の原則)。

スコープの設計例

# GitHub API
scope=repo user:email read:org

# Google API
scope=https://www.googleapis.com/auth/calendar.readonly
      https://www.googleapis.com/auth/drive.file

# 独自API
scope=read:users write:posts delete:comments
サービススコープ形式
GitHubリソース:操作repo, user:email, read:org
GoogleURL形式https://www.googleapis.com/auth/calendar.readonly
Microsoftリソース.操作User.Read, Mail.Send
Slackリソース:操作channels:read, chat:write

同意画面(Consent Screen)

ユーザーがクライアントに権限を付与する際、認可サーバーは要求されたスコープの一覧を同意画面として表示する。ユーザーはスコープの内容を確認し、アクセスを許可または拒否できる。

セキュリティ上の注意点

必須対策

対策説明
HTTPS通信の強制トークンの漏洩を防ぐため、すべての通信をTLSで暗号化する
stateパラメータの使用CSRF攻撃を防ぐため、認可リクエストにランダムなstate値を含め、コールバックで検証する
PKCEの使用認可コード横取り攻撃を防止する。パブリッククライアントでは必須
redirect_uriの厳密な一致オープンリダイレクト攻撃を防ぐため、事前登録されたURIと完全一致で検証する
トークンの安全な保管アクセストークンはメモリ内、リフレッシュトークンはサーバーサイドに保管する

避けるべきプラクティス

アンチパターンリスク代替策
localStorageにトークンを保存XSS攻撃でトークンが漏洩するHttpOnly + Secure + SameSite属性のCookieを使用
インプリシットグラントの使用トークンがURLフラグメントに露出する認可コード + PKCEを使用
スコープの過剰な要求侵害時の影響範囲が拡大する必要最小限のスコープを要求する
トークンの長期有効期限漏洩時のリスクが増大する短い有効期限 + リフレッシュトークンで更新する
クライアントシークレットのハードコードソースコードからシークレットが漏洩する環境変数やシークレットマネージャーを使用する

トークンの失効(Revocation)

不要になったトークンは、トークン失効エンドポイント(RFC 7009)で明示的に無効化する。

POST /revoke HTTP/1.1
Host: auth.example.com
Content-Type: application/x-www-form-urlencoded

token=ACCESS_TOKEN_OR_REFRESH_TOKEN
&token_type_hint=refresh_token
&client_id=CLIENT_ID

OpenID Connect(OIDC)との関係

OpenID Connect(OIDC)は、OAuth 2.0の上に認証レイヤーを追加した仕様。OAuth 2.0が「何ができるか(認可)」を扱うのに対し、OIDCは「誰であるか(認証)」を扱う。

項目OAuth 2.0OpenID Connect
目的認可(リソースへのアクセス許可)認証(ユーザーの身元確認)
取得するトークンアクセストークンアクセストークン + IDトークン
ユーザー情報標準化されていないUserInfoエンドポイントで標準化
スコープサービス固有openid, profile, email 等が標準定義

OIDCでは認可リクエストのスコープに openid を含めることで、IDトークン(JWT形式)が返される。IDトークンにはユーザーの識別子(subクレーム)などが含まれる。

GET /authorize?
  response_type=code
  &client_id=CLIENT_ID
  &scope=openid profile email
  &redirect_uri=https://example.com/callback
  &state=xyz123

SSO(シングルサインオン)やOIDCの詳細については SSO(シングルサインオン)入門 も参照。

主要サービスのOAuth 2.0エンドポイント

サービス認可エンドポイントトークンエンドポイント
Googlehttps://accounts.google.com/o/oauth2/v2/authhttps://oauth2.googleapis.com/token
GitHubhttps://github.com/login/oauth/authorizehttps://github.com/login/oauth/access_token
Microsofthttps://login.microsoftonline.com/{tenant}/oauth2/v2.0/authorizehttps://login.microsoftonline.com/{tenant}/oauth2/v2.0/token

参考リンク