メインコンテンツへ移動
SEMentor
セキュリティ 約11分

認証・認可の仕組み

セッション認証・JWT・OAuth 2.0・OpenID Connect の違いと使い分けを体系的に理解する

認証(Authentication)と認可(Authorization)

混同されやすい2つの概念を最初に整理します。

概念問い
認証(Authentication / AuthN)あなたは誰ですか?ログイン、パスワード確認
認可(Authorization / AuthZ)あなたは何をしてよいですか?管理者のみ削除可能

認証なしに認可は成立せず、認可なしでは認証後の権限制御ができません。

セッション認証

ブラウザ向け Web アプリの伝統的な方式です。

1. ユーザーがID/PW送信
2. サーバーがID/PWを検証
3. サーバーがセッションIDを生成してDBに保存
4. レスポンスのSet-CookieでセッションIDをブラウザに渡す
5. 以降のリクエストではCookieのセッションIDでユーザーを識別
# セッション認証の実装イメージ(Flask)
from flask import session
import secrets

@app.route('/login', methods=['POST'])
def login():
    user = verify_credentials(request.form['email'], request.form['password'])
    if user:
        session_id = secrets.token_hex(32)
        db.sessions.set(session_id, user.id, ex=86400)  # 24時間
        response.set_cookie('session_id', session_id,
                           httponly=True, secure=True, samesite='Strict')
        return redirect('/')
    return 'Invalid credentials', 401

特徴:

  • サーバーがセッション状態を保持(ステートフル)
  • スケールアウト時にセッション共有が必要(Redis 等)
  • ブラウザが自動でCookieを送るためCSRF対策が必要

JWT(JSON Web Token)

ステートレスな認証トークン。サーバー側にセッションを保存せず、トークン自体に情報を持たせます。

JWTの構造

eyJhbGciOiJSUzI1NiIsInR5cCI6IkpXVCJ9  ← Header(Base64)
.
eyJzdWIiOiIxMjM0NTY3ODkwIiwibmFtZSI6Ikpob24gRG9lIiwiaWF0IjoxNTE2MjM5MDIyfQ  ← Payload(Base64)
.
SflKxwRJSMeKKF2QT4fwpMeJf36POk6yJV_adQssw5c  ← Signature(HMAC/RSA)

Payload の例:

{
  "sub": "user_123",
  "name": "田中太郎",
  "role": "admin",
  "iat": 1716000000,
  "exp": 1716086400
}
# JWTの生成と検証(PyJWT)
import jwt
from datetime import datetime, timedelta

SECRET_KEY = "your-secret-key"  # 実際は環境変数から取得

def create_token(user_id: str, role: str) -> str:
    payload = {
        "sub": user_id,
        "role": role,
        "iat": datetime.utcnow(),
        "exp": datetime.utcnow() + timedelta(hours=1),
    }
    return jwt.encode(payload, SECRET_KEY, algorithm="HS256")

def verify_token(token: str) -> dict:
    try:
        return jwt.decode(token, SECRET_KEY, algorithms=["HS256"])
    except jwt.ExpiredSignatureError:
        raise ValueError("Token expired")
    except jwt.InvalidTokenError:
        raise ValueError("Invalid token")

JWTの注意点

❌ NG: ペイロードに機密情報を入れる
   → Base64はエンコードであって暗号化ではない。誰でも復号して読める

❌ NG: アルゴリズムを "none" に設定する
   → 署名なしでトークンが偽造できる

❌ NG: JWTをLocalStorageに保存する
   → XSS攻撃でJSからアクセス可能。HttpOnlyクッキーに保存する方が安全

✅ 推奨: 有効期限を短く(アクセストークン: 15分〜1時間)
✅ 推奨: リフレッシュトークンと組み合わせる
✅ 推奨: 本番では RS256(非対称鍵)を使う

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

アクセストークン(短命: 15分〜1時間)
→ API認証に使う。有効期限が短いので漏洩リスクが低い

リフレッシュトークン(長命: 7日〜30日)
→ 新しいアクセストークンを取得するためだけに使う
→ DBに保存し、使用済みにしてローテーション(RTR: Refresh Token Rotation)

OAuth 2.0

認可の委譲フレームワーク」です。「Googleでログイン」「GitHubでログイン」の裏で動いています。

登場人物

役割説明
Resource Ownerリソースの所有者ユーザー
Clientリソースにアクセスしたいアプリあなたの作ったアプリ
Authorization Server認証・認可を行うサーバーGoogle, GitHub, Cognito
Resource Server保護されたリソースを提供Google APIs, GitHub API

Authorization Code フロー(推奨)

1. ユーザーが「Googleでログイン」をクリック
2. ClientがGoogle認可サーバーへリダイレクト
   /?response_type=code&client_id=xxx&redirect_uri=xxx&scope=email

3. ユーザーがGoogleでログインして許可
4. Google → Clientへ認可コード(code)をリダイレクトで返す
   /callback?code=AUTH_CODE

5. Client(バックエンド)がcodeをアクセストークンと交換
   POST /token { code, client_id, client_secret, ... }
   ← { access_token, refresh_token, expires_in }

6. ClientがアクセストークンでGoogle APIを呼び出す
   GET /userinfo   Authorization: Bearer ACCESS_TOKEN
# Pythonでのシンプルな実装例(requests-oauthlib)
from requests_oauthlib import OAuth2Session

GOOGLE_CLIENT_ID = "..."
GOOGLE_CLIENT_SECRET = "..."
REDIRECT_URI = "https://app.example.com/callback"
SCOPE = ["openid", "email", "profile"]

# Step 1: 認可URLを生成
oauth = OAuth2Session(GOOGLE_CLIENT_ID, scope=SCOPE, redirect_uri=REDIRECT_URI)
auth_url, state = oauth.authorization_url(
    "https://accounts.google.com/o/oauth2/auth"
)

# Step 2: コードをトークンと交換
token = oauth.fetch_token(
    "https://oauth2.googleapis.com/token",
    client_secret=GOOGLE_CLIENT_SECRET,
    authorization_response=callback_url,
)

# Step 3: ユーザー情報を取得
userinfo = oauth.get("https://www.googleapis.com/oauth2/v3/userinfo").json()

OpenID Connect(OIDC)

OAuth 2.0 は認可のプロトコルです。認証(ユーザーが誰か)を扱うために OAuth 2.0 を拡張したのが OpenID Connect です。

OAuth 2.0:  「このユーザーの代わりにGitHubリポジトリを読んでいい」(認可)
OIDC:       「このユーザーはGoogle認証済みの user@example.com だ」(認証)

OIDC では OAuth 2.0 のアクセストークンに加えて ID Token(JWT形式) が発行されます。

// IDトークンのペイロード例
{
  "iss": "https://accounts.google.com",   // 発行者
  "sub": "110169484474386276334",         // ユーザーの一意ID
  "email": "user@example.com",
  "email_verified": true,
  "name": "田中太郎",
  "picture": "https://lh3.googleusercontent.com/...",
  "iat": 1716000000,
  "exp": 1716003600
}

認証方式の選択基準

ブラウザ向けWebアプリ(自社サービス)
→ セッション認証 or OIDC(SSO)

スマホアプリ / SPA からのAPI呼び出し
→ JWT(アクセストークン + リフレッシュトークン)

「〇〇でログイン」(ソーシャルログイン)
→ OAuth 2.0 + OIDC

B2B SaaS(企業SSO)
→ SAML 2.0 or OIDC

マイクロサービス間の通信
→ mTLS または JWT(サービスアカウント)

多要素認証(MFA)

知識要素(Something you know): パスワード、PIN
所持要素(Something you have): スマホアプリ(TOTP: Google Authenticator)
生体要素(Something you are): 指紋、顔認識

TOTPの仕組み:
1. サーバーとユーザーが共有秘密鍵を持つ(QRコードで設定)
2. 現在時刻(30秒単位)と秘密鍵から6桁コードを生成
3. コードはサーバー側でも同じ計算で検証
→ 秘密鍵なしでは予測不可能

まとめ

方式ステートフルスケール主な用途
セッションRedis必要従来型Webアプリ
JWT❌(ステートレス)しやすいSPA/モバイルAPI
OAuth 2.0--サードパーティ認可
OIDC--SSO・ソーシャルログイン

認証・認可は絶対に自作しないのが原則です。実績ある認証ライブラリやマネージドサービス(Auth0、Firebase Auth、AWS Cognito)を活用しましょう。


ログイン後の権限制御を詳しく学ぶには、認可と権限設計もあわせて確認してください。

このレッスンは未完了です。

次のレッスンへ