OAuth 2.0の仕組みを図解!
認可フローを完全理解
OAuth 2.0は「Googleでログイン」等の外部認証の仕組みです。認可コードフローを中心に解説します。
こんな人向けの記事です
- OAuth 2.0の仕組みを理解したい人
- ソーシャルログインを実装したい人
- 認証と認可の違いを知りたい人
Step 1OAuthとは何か
OAuth 2.0は認可(Authorization)のためのプロトコルです。パスワードを渡さずに、第三者アプリにリソースへのアクセスを許可できます。
認証と認可の違い
# 認証(Authentication)= 「あなたは誰?」
# → ユーザーIDとパスワードで本人確認
# → 例: ログイン
# 認可(Authorization)= 「何を許可する?」
# → 第三者にリソースへのアクセス権を付与
# → 例: 「このアプリにGoogleカレンダーの読み取りを許可しますか?」
# OAuth 2.0 は「認可」のプロトコル
# OpenID Connect (OIDC) が OAuth 2.0 の上に認証を追加したもの身近な例
「Googleでログイン」「GitHubでログイン」ボタンは、OAuth 2.0 + OpenID Connect を使っています。Step 24つの登場人物
OAuth 2.0の登場人物
# 1. リソースオーナー(Resource Owner)
# → ユーザー自身。データの持ち主
# → 例: Googleアカウントを持つ田中さん
# 2. クライアント(Client)
# → ユーザーのデータにアクセスしたいアプリ
# → 例: カレンダー管理アプリ
# 3. 認可サーバー(Authorization Server)
# → アクセス許可を管理するサーバー
# → 例: Googleの認可サーバー
# 4. リソースサーバー(Resource Server)
# → ユーザーのデータを保持するサーバー
# → 例: Google Calendar API
# フロー概要:
# ユーザー → 「Googleでログイン」クリック
# → Google認可画面 → 「許可する」
# → アプリがアクセストークンを取得
# → トークンでGoogle APIにアクセスStep 3認可コードフロー
最も安全で推奨されるフローです。サーバーサイドアプリケーション向けです。
認可コードフロー(Authorization Code Flow)
# Step 1: ユーザーを認可エンドポイントにリダイレクト
# GET https://accounts.google.com/o/oauth2/v2/auth?
# response_type=code
# &client_id=YOUR_CLIENT_ID
# &redirect_uri=https://yourapp.com/callback
# &scope=openid email profile
# &state=ランダムな文字列(CSRF対策)
# Step 2: ユーザーが「許可」→ コールバックURLに認可コードが届く
# GET https://yourapp.com/callback?code=認可コード&state=ランダムな文字列
# Step 3: 認可コードをアクセストークンに交換(サーバー間通信)
# POST https://oauth2.googleapis.com/token
# {
# "code": "認可コード",
# "client_id": "YOUR_CLIENT_ID",
# "client_secret": "YOUR_CLIENT_SECRET",
# "redirect_uri": "https://yourapp.com/callback",
# "grant_type": "authorization_code"
# }
# Step 4: アクセストークンでAPIにアクセス
# GET https://www.googleapis.com/calendar/v3/calendars
# Authorization: Bearer アクセストークンclient_secretの管理
client_secretは絶対にフロントエンド(JavaScript)に含めないでください。サーバーサイドでのみ使用します。Step 4アクセストークンとリフレッシュトークン
トークンの仕組み
# アクセストークン
# - APIアクセスに使用する短命のトークン(通常1時間)
# - 期限切れになったら再取得が必要
# リフレッシュトークン
# - 新しいアクセストークンを取得するための長命のトークン
# - ユーザーの再ログインなしでトークンを更新
# トークン更新のフロー
# POST https://oauth2.googleapis.com/token
# {
# "refresh_token": "リフレッシュトークン",
# "client_id": "YOUR_CLIENT_ID",
# "client_secret": "YOUR_CLIENT_SECRET",
# "grant_type": "refresh_token"
# }
# レスポンス:
# {
# "access_token": "新しいアクセストークン",
# "expires_in": 3600,
# "token_type": "Bearer"
# }Step 5実装例(Python)
Python(Flask + requests-oauthlib)
# pip install Flask requests-oauthlib
from flask import Flask, redirect, request, session
from requests_oauthlib import OAuth2Session
app = Flask(__name__)
app.secret_key = "your-secret-key"
CLIENT_ID = "your-client-id"
CLIENT_SECRET = "your-client-secret"
AUTHORIZATION_URL = "https://accounts.google.com/o/oauth2/v2/auth"
TOKEN_URL = "https://oauth2.googleapis.com/token"
REDIRECT_URI = "http://localhost:5000/callback"
SCOPE = ["openid", "email", "profile"]
@app.route("/login")
def login():
oauth = OAuth2Session(CLIENT_ID, redirect_uri=REDIRECT_URI, scope=SCOPE)
auth_url, state = oauth.authorization_url(AUTHORIZATION_URL)
session["oauth_state"] = state
return redirect(auth_url)
@app.route("/callback")
def callback():
oauth = OAuth2Session(CLIENT_ID, state=session["oauth_state"],
redirect_uri=REDIRECT_URI)
token = oauth.fetch_token(TOKEN_URL, client_secret=CLIENT_SECRET,
authorization_response=request.url)
session["oauth_token"] = token
# ユーザー情報を取得
resp = oauth.get("https://www.googleapis.com/oauth2/v3/userinfo")
user_info = resp.json()
return f"ようこそ {user_info['name']} さん!"Step 6セキュリティの注意点
セキュリティ対策
# 1. state パラメータ(CSRF対策)
# 認可リクエストにランダムな state を含め、
# コールバックで一致を検証する
# 2. PKCE(Proof Key for Code Exchange)
# SPAやモバイルアプリで必須。
# code_verifier と code_challenge でトークン交換を保護
# 3. redirect_uri の厳密な検証
# 登録済みのURIと完全一致のみ許可(オープンリダイレクト防止)
# 4. トークンの安全な保管
# - サーバーサイド: 暗号化してDB保存
# - フロントエンド: httpOnly Cookie(localStorageは避ける)
# 5. スコープの最小化
# 必要最小限の権限のみ要求する
# NG: scope=全権限
# OK: scope=email profile(必要なものだけ)PKCE とは
SPA(シングルページアプリ)ではclient_secretを安全に保持できないため、PKCEを使って認可コードの横取りを防ぎます。現在はサーバーサイドアプリでもPKCEの使用が推奨されています。まとめ
- OAuth 2.0は認可(アクセス権の委譲)のプロトコル
- 認可コードフローが最も安全で推奨
- アクセストークン(短命)+ リフレッシュトークン(長命)
- state パラメータでCSRF対策、PKCEでコード横取り防止
- client_secretはサーバーサイドでのみ使用