Skip to content

필사 모드: OAuth 2.0完全攻略 — 認証と認可のすべて

日本語
0%
정확도 0%
💡 왼쪽 원문을 읽으면서 오른쪽에 따라 써보세요. Tab 키로 힌트를 받을 수 있습니다.
원문 렌더가 준비되기 전까지 텍스트 가이드로 표시합니다.

はじめに

「Googleでログイン」ボタンを押すと何が起こるでしょうか?たった1回のクリックの裏では、**OAuth 2.0**という精巧なプロトコルが動いています。

OAuthを理解すれば、ソーシャルログイン、API認証、マイクロサービス間通信、さらにはAnthropicのClaude API認証ポリシーまで明確になります。

核心概念:認証 vs 認可

認証(Authentication): 「あなたは誰?」

→ ユーザーが本人であることを証明

→ ID/パスワード、生体認証、OTP

認可(Authorization): 「何ができる?」

→ 認証されたユーザーに権限を付与

→ このアプリがあなたのGoogle Calendarを読んでもいい?

OAuth 2.0 = 認可(Authorization)フレームワーク

OpenID Connect = OAuth 2.0の上に認証を追加したもの

OAuth 2.0の4つの役割

┌──────────────┐

│ Resource Owner│ ← ユーザー

└──────┬───────┘

│ 「このアプリに私のカレンダーへのアクセスを許可」

┌──────────────┐ ┌──────────────────┐

│ Client │────▶│ Authorization │

│ (私たちのアプリ)│◀────│ Server │

└──────┬───────┘ │ (Google OAuth) │

│ └──────────────────┘

│ Access Token

┌──────────────┐

│ Resource │

│ Server │ ← Google Calendar API

└──────────────┘

Grant Types(認可方式)

1. Authorization Code(最も重要!)

Webアプリで最も多く使われる方式です。

[1] ユーザー → アプリ: 「Googleでログイン」クリック

[2] アプリ → Google: リダイレクト(client_id, redirect_uri, scope)

[3] ユーザー → Google: ログイン + 「許可」クリック

[4] Google → アプリ: redirect_uri?code=AUTH_CODE

[5] アプリ → Google: AUTH_CODE + client_secret → Access Token

[6] アプリ → Google API: Access Tokenでデータ要求

Step 2: Authorization要求URL生成

auth_url = "https://accounts.google.com/o/oauth2/v2/auth?" + urllib.parse.urlencode({

"client_id": "YOUR_CLIENT_ID.apps.googleusercontent.com",

"redirect_uri": "https://yourapp.com/callback",

"response_type": "code",

"scope": "openid email profile https://www.googleapis.com/auth/calendar.readonly",

"state": "random_csrf_token_abc123", # CSRF防止!

"access_type": "offline", # Refresh Tokenも受け取る

})

→ ユーザーをこのURLにリダイレクト

Step 5: Authorization Code → Access Token交換

token_response = requests.post("https://oauth2.googleapis.com/token", data={

"code": "AUTH_CODE_FROM_CALLBACK",

"client_id": "YOUR_CLIENT_ID",

"client_secret": "YOUR_CLIENT_SECRET", # サーバーサイドのみ!

"redirect_uri": "https://yourapp.com/callback",

"grant_type": "authorization_code",

})

tokens = token_response.json()

{

"access_token": "ya29.xxx...", ← API呼び出し用(1時間)

"refresh_token": "1//xxx...", ← 更新用(永続)

"id_token": "eyJhbGci...", ← ユーザー情報(OIDC)

"expires_in": 3600,

"token_type": "Bearer"

}

Step 6: API呼び出し

calendar = requests.get(

"https://www.googleapis.com/calendar/v3/calendars/primary/events",

headers={"Authorization": f"Bearer {tokens['access_token']}"}

)

**なぜこんなに複雑なのか?**

- Authorization CodeはブラウザURLに露出しますが、**1回限り**で単独では使えません

- Access Token交換は**サーバー間通信**(client_secretを保護)

- フロントエンドにSecretが絶対に露出しません

2. Authorization Code + PKCE(モバイル/SPA必須!)

モバイルアプリやSPA(Reactなど)にはclient_secretを安全に保存する場所がありません。

Clientがcode_verifierを生成(秘密の値)

code_verifier = secrets.token_urlsafe(64)

"dBjftJeZ4CVP-mB92K27uhbUJU1p1r_wW1gFWFOEjXk..."

code_challenge = SHA256(code_verifier) → Base64URL

code_challenge = base64.urlsafe_b64encode(

hashlib.sha256(code_verifier.encode()).digest()

).rstrip(b'=').decode()

Authorization要求にchallenge含める

auth_url = f"...&code_challenge={code_challenge}&code_challenge_method=S256"

Token交換時にverifierを提出(Secretの代わり!)

token_response = requests.post("https://oauth2.googleapis.com/token", data={

"code": auth_code,

"client_id": "YOUR_CLIENT_ID",

"code_verifier": code_verifier, # Secretの代わりにこれを検証!

"redirect_uri": "...",

"grant_type": "authorization_code",

})

**PKCEの核心**: Auth Codeを傍受されてもcode_verifierがなければTokenを取得できません。

3. Client Credentials(サーバー間通信)

ユーザーの介入なしにサーバー → サーバー直接認証

例: バックエンド → Google Cloud API

token = requests.post("https://oauth2.googleapis.com/token", data={

"client_id": "SERVICE_CLIENT_ID",

"client_secret": "SERVICE_CLIENT_SECRET",

"grant_type": "client_credentials",

"scope": "https://www.googleapis.com/auth/cloud-platform",

})

4. Refresh Token(更新)

Access Token期限切れ(1時間)→ Refresh Tokenで更新

new_tokens = requests.post("https://oauth2.googleapis.com/token", data={

"refresh_token": "1//xxx...",

"client_id": "YOUR_CLIENT_ID",

"client_secret": "YOUR_CLIENT_SECRET",

"grant_type": "refresh_token",

})

新しいAccess Tokenが発行される(Refresh Tokenは維持)

Token種類の比較

| Token | 有効期間 | 用途 | 保存場所 |

| ------------------ | ------------------ | ---------------------- | ---------------------------------- |

| Authorization Code | 数秒 | Token交換用(1回限り) | URLパラメータ(即時消滅) |

| Access Token | 1時間 | API呼び出し | メモリ(ブラウザ)/ DB(サーバー) |

| Refresh Token | 永続(取消し可能) | Access Token更新 | サーバーDB(安全に!) |

| ID Token (OIDC) | 1時間 | ユーザー情報確認 | メモリ |

JWT(JSON Web Token)の構造

Access TokenとID Tokenは通常JWT形式です:

eyJhbGciOiJSUzI1NiJ9.eyJzdWIiOiIxMjM0NTY3ODkwIiwibmFtZSI6Ik...

├── Header ──────────┤├── Payload ─────────────────────────────...

jwt = "eyJhbGciOiJSUzI1NiJ9.eyJzdWIiOiIxMjM0NTY3ODkwIn0.signature"

Header

header = json.loads(base64.urlsafe_b64decode(jwt.split('.')[0] + '=='))

{"alg": "RS256", "typ": "JWT"}

Payload

payload = json.loads(base64.urlsafe_b64decode(jwt.split('.')[1] + '=='))

{"sub": "1234567890", "name": "Youngju Kim", "iat": 1709420400}

Signature = RS256(header + "." + payload, private_key)

→ サーバーのpublic keyで検証(改ざん不可)

OpenID Connect (OIDC) — OAuth + 認証

OAuth 2.0だけでは「このユーザーが誰か」は分からない

→ OIDCがid_tokenを追加してユーザー情報を提供

scopeに "openid" を追加 → id_tokenが発行される

scopeに "profile email" を追加 → 名前、メールが含まれる

セキュリティチェックリスト

すべき: stateパラメータでCSRFを防止

すべき: PKCEを使用(SPA/モバイルは必須)

すべき: redirect_uriを正確にマッチ(ワイルドカード不可)

すべき: Refresh Tokenはサーバーにのみ保存

すべき: Access TokenはAuthorizationヘッダーでのみ送信

すべき: HTTPS必須(Token盗取防止)

してはいけない: Access TokenをURLパラメータに入れない

してはいけない: client_secretをフロントエンドに露出しない

してはいけない: TokenをlocalStorageに保存しない(XSS脆弱)

実務例:Anthropic OAuthの問題

最近AnthropicがOAuthポリシーを変更したのもこの文脈です:

以前: Claude Proサブスク → OAuth Token → サードパーティアプリ(OpenClawなど)

変更: OAuth TokenはClaude.ai / Claude Codeでのみ使用可能

理由: Tokenアービトラージ防止(サブスク料 より API Token価値が高い)

代替: Anthropic API Key(従量課金)を使用

**Q1.** OAuth 2.0における認証(Authentication)と認可(Authorization)の違いは?

||認証: ユーザーが誰であるかを確認。認可: 認証されたユーザーに特定リソースへのアクセス権限を付与。OAuth 2.0は認可フレームワークであり、OIDCが認証を追加||

**Q2.** Authorization Code GrantでCodeが1回限りである理由は?

||CodeはブラウザURLに露出するため盗取リスクがある。1回限りでclient_secretなしではToken交換不可のため、単独では無価値||

**Q3.** PKCEにおけるcode_verifierとcode_challengeの関係は?

||code_challenge = SHA256(code_verifier)のBase64URLエンコーディング。認可リクエスト時にchallengeを送り、トークン交換時にverifierを送ってサーバーが検証||

**Q4.** Refresh TokenをlocalStorageに保存してはいけない理由は?

||XSS攻撃でJavaScriptがlocalStorageを読み取れる。Refresh Tokenは長期有効なため、盗取時に持続的なアクセスが可能。httpOnlyクッキーまたはサーバーDBに保存すべき||

**Q5.** Client Credentials Grantはどのような状況で使われるか?

||ユーザーの介入なしにサーバー間の直接認証。例: バックエンドサービスが別のAPIサーバーにアクセスする時。client_id + client_secretのみでToken発行||

**Q6.** stateパラメータがない場合、どのような攻撃が可能か?

||CSRF攻撃 — 攻撃者が自身のアカウントで発行したAuthorization Codeを被害者のコールバックURLに送り、被害者を攻撃者のアカウントにログインさせることができる||

**Q7.** JWTのSignatureは何を保証するか?

||トークンの内容(Header + Payload)が改ざんされていないことを保証。サーバーのPrivate Keyで署名し、Public Keyで検証。ただし暗号化ではない — PayloadはBase64デコードで誰でも読める||

クイズ

OAuth 2.0の動作原理からAuthorization Code、PKCE、Refresh Token、OpenID

Connectまで。なぜGoogleログインボタン1つにこれほど複雑なプロトコルが必要なのか、コードとシーケンスで完全に解剖します。

1. Authorization Code(最も重要!) Webアプリで最も多く使われる方式です。 なぜこんなに複雑なのか?

Authorization CodeはブラウザURLに露出しますが、1回限りで単独では使えません Access

Token交換はサーバー間通信(client_secretを保護) フロントエンドにSecretが絶対に露出しません 2.

Authorization Code + PKCE(モバイル/SPA必須!)

モバイルアプリやSPA(Reactなど)にはclient_secretを安全に保存する場所がありません。

Access TokenとID Tokenは通常JWT形式です:

最近AnthropicがOAuthポリシーを変更したのもこの文脈です: Q1. OAuth

2.0における認証(Authentication)と認可(Authorization)の違いは? Q2. Authorization Code

GrantでCodeが1回限りである理由は? Q3. PKCEにおけるcode_verifierとcode_challengeの関係は? Q4.

Refresh TokenをlocalStorageに保存してはいけない理由は? Q5. Client Credentials

Grantはどのような状況で使われるか? Q6.

현재 단락 (1/140)

「Googleでログイン」ボタンを押すと何が起こるでしょうか?たった1回のクリックの裏では、**OAuth 2.0**という精巧なプロトコルが動いています。

작성 글자: 0원문 글자: 6,851작성 단락: 0/140