1.

OAuth 2.0 認可フロー完全ガイド(Authorization Code / PKCE / Refresh

編集
この記事の要点
  • OAuth 2.0 は 第三者アプリにユーザーの認可だけを委譲するプロトコル(パスワードを渡さず access_token を発行)
  • 主要フロー: Authorization Code Flow(Web アプリ標準) / Client Credentials(サーバ間) / PKCE(SPA・モバイル)
  • Implicit Flow は非推奨(CSRF / トークン漏洩リスク)→ PKCE で置き換える
  • access_token(短命・通常 1 時間) + refresh_token(長命・access_token 再発行用)
  • Laravel では Passport(自前で OAuth2 サーバー) / Socialite(Google/GitHub 等のクライアント)

OAuth 2.0 とは何か

OAuth 2.0 は RFC 6749 で定義された認可(Authorization)プロトコルです。誤解されがちですが、認可の仕組みであって、認証(誰なのか)は OpenID Connect が担当します。

典型例: 「このアプリに Google アカウントの連絡先を読ませて良いか?」というユーザー同意を得て、Google から連絡先 API へのアクセス権(access_token)を発行してもらいます。アプリは Google パスワードを知ることなく、必要な権限だけを得られます。

登場人物(4 つのロール)

ロール役割
Resource Ownerユーザーデータの持ち主、認可を与える
ClientサードパーティアプリAPI を呼びたいアプリ
Authorization ServerGoogle / GitHub の OAuth サーバーaccess_token を発行
Resource ServerGoogle People API 等access_token を検証してデータを返す

Authorization Code Flow(標準・最もよく使う)

サーバーサイドアプリ向けの標準フロー。ブラウザ経由でリダイレクトしながら認可コードを取得し、サーバー側で access_token と交換します。

[1] ユーザー → クライアントの「Google でログイン」ボタンをクリック
[2] クライアント → ブラウザを Google の認可エンドポイントへリダイレクト
    GET https://accounts.google.com/o/oauth2/v2/auth?
        response_type=code
        &client_id=xxx
        &redirect_uri=https://app.example.com/callback
        &scope=email%20profile
        &state=randomstring   ← CSRF 対策
[3] Google → ユーザー: ログイン + 同意画面
[4] Google → クライアントの redirect_uri に code を返す
    GET https://app.example.com/callback?code=AUTH_CODE&state=randomstring
[5] クライアント (サーバー側) → Google のトークンエンドポイントに POST
    POST https://oauth2.googleapis.com/token
    {
        "grant_type": "authorization_code",
        "code": "AUTH_CODE",
        "client_id": "xxx",
        "client_secret": "yyy",
        "redirect_uri": "https://app.example.com/callback"
    }
[6] Google → access_token + refresh_token + id_token を返す
[7] クライアント → access_token で API を呼ぶ
    GET https://www.googleapis.com/oauth2/v3/userinfo
    Authorization: Bearer ACCESS_TOKEN

PKCE(Proof Key for Code Exchange)

SPA / モバイルアプリは client_secret を安全に保管できないため、Authorization Code Flow に PKCE 拡張(RFC 7636)を組み合わせます。現在は全クライアントで PKCE 推奨

// 1. code_verifier を生成(ランダム 43-128 文字)
const codeVerifier = base64UrlEncode(crypto.getRandomValues(new Uint8Array(32)));

// 2. code_challenge = SHA256(code_verifier) を生成
const codeChallenge = base64UrlEncode(
    await crypto.subtle.digest('SHA-256', new TextEncoder().encode(codeVerifier))
);

// 3. 認可リクエストに付与
const url = `https://accounts.google.com/o/oauth2/v2/auth?
    response_type=code
    &client_id=${CLIENT_ID}
    &redirect_uri=${REDIRECT_URI}
    &scope=email
    &code_challenge=${codeChallenge}
    &code_challenge_method=S256
    &state=${state}`;

// 4. トークン交換時に code_verifier を送る
const tokenRes = await fetch('https://oauth2.googleapis.com/token', {
    method: 'POST',
    body: new URLSearchParams({
        grant_type: 'authorization_code',
        code: receivedCode,
        client_id: CLIENT_ID,
        redirect_uri: REDIRECT_URI,
        code_verifier: codeVerifier,  // ← サーバが SHA256 して code_challenge と比較
    }),
});

主なグラントタイプ一覧

Grant Type用途推奨
authorization_codeWeb アプリ・SPA・モバイル(PKCE 併用)標準
client_credentialsサーバー間通信(ユーザー不在)マシン to マシン
refresh_tokenaccess_token の再発行長期セッション
passwordユーザー ID/PW を直接渡す非推奨(レガシ用途のみ)
implicitSPA がトークンを直接受け取る非推奨(PKCE で代替)
device_codeTV / IoT 等のキー入力困難な機器RFC 8628

access_token と refresh_token

項目access_tokenrefresh_token
用途API 呼び出しaccess_token 再発行
寿命短命(5 分〜 1 時間)長命(数日〜無期限)
形式多くは JWT / Bearer トークン不透明な文字列
送信先Resource ServerAuthorization Server のみ
漏洩リスク有効期限内のみ悪用可長期悪用可 → 安全に保管必須

Laravel Passport(OAuth2 サーバ)

自前のアプリで OAuth2 サーバーを立てるなら Laravel Passport:

composer require laravel/passport
php artisan migrate
php artisan passport:install
# → Client ID / Secret が表示される
// app/Models/User.php
use Laravel\Passport\HasApiTokens;

class User extends Authenticatable
{
    use HasApiTokens, Notifiable;
}

// app/Providers/AuthServiceProvider.php
use Laravel\Passport\Passport;

public function boot(): void
{
    Passport::routes();
    Passport::tokensExpireIn(now()->addDays(15));
    Passport::refreshTokensExpireIn(now()->addDays(30));
}

// config/auth.php
'guards' => [
    'api' => [
        'driver'   => 'passport',
        'provider' => 'users',
    ],
],

Laravel Socialite(OAuth クライアント)

Google / GitHub / Twitter にログインさせる側(クライアント)として使うのが Socialite:

composer require laravel/socialite
// config/services.php
'github' => [
    'client_id'     => env('GITHUB_CLIENT_ID'),
    'client_secret' => env('GITHUB_CLIENT_SECRET'),
    'redirect'      => env('GITHUB_REDIRECT'),
],

// ルーティング
use Laravel\Socialite\Facades\Socialite;

Route::get('/auth/github', fn() => Socialite::driver('github')->redirect());

Route::get('/auth/github/callback', function () {
    $github = Socialite::driver('github')->user();

    $user = User::updateOrCreate(
        ['email' => $github->getEmail()],
        [
            'name'              => $github->getName(),
            'github_id'         => $github->getId(),
            'github_token'      => $github->token,
            'github_refresh'    => $github->refreshToken,
        ]
    );

    Auth::login($user);
    return redirect('/dashboard');
});

OAuth 2.0 と OpenID Connect の違い

項目OAuth 2.0OpenID Connect
目的認可(権限委譲)認証(誰なのか)
発行されるものaccess_tokenaccess_token + id_token (JWT)
標準クレームなしsub / email / name 等が JWT に含まれる
関係OAuth 2.0 の上に乗る拡張

セキュリティ注意点

  • state パラメータで CSRF 対策: 認可リクエストで送り、コールバックで照合
  • redirect_uri は完全一致: クライアント登録時の URI と一致しないとサーバが拒否
  • client_secret はサーバー側のみ: フロントエンドに埋めない(PKCE で代替)
  • access_token は HTTPS でのみ送信
  • JWT 検証: 署名 / iss / aud / exp を必ずチェック

FAQ

Q: OAuth 1.0 との違いは?
A: 1.0 は署名計算が複雑で実装ミスが多発、2.0 は HTTPS 前提で大幅に簡略化。現在は 1.0 はほぼ使われません。

Q: Implicit Flow を使ってはいけない?
A: 2020 年の OAuth 2.0 Security BCP で非推奨に。SPA は Authorization Code + PKCE を使ってください。

Q: refresh_token を使わずに済ませる方法は?
A: access_token の寿命を長くする方法もありますが、漏洩リスクが上がるため非推奨。短命 access + refresh の組合せが標準です。

編集
Post Share
子ページ

子ページはありません

同階層のページ

同階層のページはありません