前提知識

Web 認証徹底解説: JWT、セッション、Remember Me

サインアップ、ログイン、アクセス/リフレッシュトークン、セッション ID とリメンバー用トークン、遅延リフレッシュ、ログアウト、そして JWT 方式とサーバーセッション方式の選び方まで。実務で役立つエンドツーエンドガイド。

0. 全体像:裏側で何が起きている?

Web 認証は大きく 3 つの仕事を解くために存在します。

  1. サインアップ:ユーザーを作る。
  2. ログイン:本人確認(メール + パスワード、OAuth など)。
  3. ログイン状態を保つ:リクエストや時間をまたいで認証済みを維持する。

おもしろいのは 3 つ目。どうやって「だれか」を覚え、どれくらいの期間覚えておくのか?

実装は 2 系統ですがパターンは同じです。

  • JWT ベース(ステートレス)
  • サーバーサイドセッション(ステートフル)

どちらも 2 種類のクレデンシャルを持ちます。

短命長命役割
アクセストークン(JWT)リフレッシュトークン新しいアクセストークンをもらう
セッション ID(セッション方式)Remember-me トークン新しいセッションをもらう

考え方は同じ、実装だけが違う。


1. フェーズ1 – サインアップ(アカウント作成)

JWT でもセッションでも共通です。

  1. ユーザーがメール、パスワード、必要なら名前/ロケールを送る。
  2. サーバーでメール形式、パスワード強度、メールの重複を検証。
  3. パスワードをハッシュ化(bcrypt/argon2/scrypt)。平文を保存しない。

例:

users
------------------------------------
id | email   | password_hash | ...
1  | a@b.com | $2b$10$...    | ...
------------------------------------
  1. (任意)メール確認リンクを送る。

サインアップ後、ユーザーは作られますが自動ログインするかどうかは実装次第です。


2. フェーズ2 – ログイン(認証)

どちらの方式でも流れは同じ。

POST /login
{ "email": "a@b.com", "password": "secret123", "remember": true }

サーバーがパスワードを検証したら、以下のどちらかを作ります。

  • JWT 2 つ(アクセス + リフレッシュ)
  • セッション ID(必要なら remember トークンも)

この先はどちらの方式を選ぶかで分岐します。

2.2 Google / OAuth ログインを足す(同じパターン)

OAuth(Google/GitHub など)も同じ流れに差し込めます。

  • ユーザーが「Google で続行」をクリック。
  • ブラウザが Google へリダイレクトされ同意画面へ。
  • Google が認可コードをバックエンドに返す。
  • バックエンドがコードをトークン+プロフィールと交換。
  • バックエンドがローカルユーザーを作成/紐付けし、自分の認証情報を発行:
    • JWT 方式: アクセス + リフレッシュトークンを発行。
    • セッション方式: session_id(+長期化したければ remember トークン)。

実装のコツ:

  • プロバイダーからのレスポンスは「本人証明」として扱い、ローカルユーザーを必ず保存/リンクする。
  • 重複防止のためプロバイダーの ID(例: google_sub)を保存する。
  • Cookie は HttpOnly/SameSite/Secure。プロバイダーのトークンを localStorage に置かない。
  • パスワードログインと同じリフレッシュ/remember ルールを適用。OAuth はパスワードチェックを置き換えるだけ。
  • パケットレベルの完全版は専用ガイドへ: ソーシャルサインイン(Google 例)

2.3 従来のメール+パスワード vs Google ログイン(詳細比較)

2.3.1 Google ログインでパスワードは消える?

  • あなたのアプリでは、はい。ユーザーはパスワードを作らないし入力もしません。
  • ただし Google 上ではパスワードや 2FA などを使います。そこを Google に委譲します。

2.3.2 アプリではパスワードの代わりに何を使う?

  • ローカルログインは email + password_hash
  • Google ログインは検証済みメール + Google の一意な subject (sub)。
  • バックエンドは google_sub を保存/紐付け(例: user.google_sub = "112233445566778899")。
  • フローは google_sub → user_id → あなたのセッション/JWT を発行。ローカルパスワードは不要。

2.3.3 Google ログインでパスワードリセットは?

  • 対応不要。ユーザーがパスワードを忘れたら Google で復旧します。
  • Google 専用アカウントには「パスワードをお忘れですか」フローは不要です。Google の本人確認を信頼するだけ。

2.3.4 アカウント復旧と本人確認はどうなる?

  • あなたのアプリではコード/SMS/パスワードリセットを使わなくなります。
  • Google がリカバリー、2FA、デバイスチェック、リスク分析、CAPTCHA、不審ログイン警告、バックアップコードを担当。
  • Google のリカバリー/検証スタックをそのまま継承できます。

2.3.5 新規ユーザー確認は?

  • Google は ID トークンで検証済みメールを返します。
{
  "email": "user@gmail.com",
  "email_verified": true
}
  • email_verified が true なら、独自のメール/SMS 確認を省略可能。Google の検証を借ります。

2.3.6 Google ログインは安全?

  • Google の防御を引き継ぎます: MFA、セキュリティキー(FIDO2/U2F)、デバイス認識、リスクベースアクセス、CAPTCHA/ボット対策、漏洩パスワード検知、不審ログイン通知、リカバリーフロー。
  • これを自前で作るのは高コスト。だから多くのチームが Google サインインを好みます。

2.3.7 Google アカウントにアクセスできなくなったら?

  • 復旧するまでログインできません。
  • 冗長性を用意しましょう: 後からメール+パスワードを紐付けたり、別の OAuth プロバイダー(GitHub/Apple など)を追加してサポートが切り替えられるようにする。

2.3.8 図式サマリー

             Google なし
  +-----------------------------------------+
  | あなたが担当:                           |
  |  - パスワードハッシュ                   |
  |  - ログイン試行管理                     |
  |  - メール検証                           |
  |  - SMS 検証                             |
  |  - 2FA                                  |
  |  - パスワードリセット                   |
  |  - アカウント復旧                       |
  |  - 不審ログイン検知                     |
  +-----------------------------------------+


             Google ログインあり
  +-----------------------------------------+
  | Google がすべてのアイデンティティ保護を担当 |
  |                                         |
  | あなたが担当するのは:                   |
  |   - ユーザーレコード                    |
  |   - セッション/JWT トークン             |
  |                                         |
  +-----------------------------------------+

2.3.9 Google ログインは認証フローのどこに入る?

通常ログイン
email + password -> パスワード検証 -> セッション作成 -> ログイン完了

GOOGLE ログイン
google_sub      -> id_token 検証  -> セッション作成 -> ログイン完了

最初のステップだけが異なり、その後(セッション/JWT、リフレッシュ/remember、ログアウト)は同じです。

2.3.10 パスワードはなくなる?

  • 近年、多くのアプリがパスワードレス/OAuth ファーストを採用: Google、Apple、Microsoft、GitHub、Slack、Notion、Discord、Figma、Linear、Superhuman、さらにマジックリンクやパスキー。
  • トレンド: 「Google/Apple/Microsoft/GitHub でログイン」または「マジックリンク/パスキーでログイン」→ パスワードはレガシーへ。

2.3.11 Q&A まとめ

質問回答
Google ログインでパスワードは必要?ローカルパスワード不要。Google の本人証明を信頼する。
リカバリーはどうなる?Google がパスワードリセットとアカウント復旧を全面担当。
本人確認は?Google が検証済みメールを返す(email_verified=true)。
セッション/JWT は必要?はい。Google ログイン後も自分のトークン/セッションを発行する。
安全性は?Google のセキュリティ/リカバリー機構を継承するので高い。

3. システム A – JWT ベース認証(ステートレス)

コア概念

  • アクセストークン: 署名付き JWT。短命(15〜60 分)。毎リクエストで送る。
  • リフレッシュトークン: 長命(7〜30 日)。新しいアクセストークンを得るときだけ使う。

アクセストークン = 今日の入場券。
リフレッシュトークン = 入場券をもらうためのパスポート。

JWT でのログイン

  1. メール + パスワードを検証。
  2. アクセストークンを作成(短い exp)。
  3. リフレッシュトークンを作成(長い exp)。
  4. HttpOnly Cookie で送信:
  • access_token=...; HttpOnly; Secure; SameSite=Lax
  • refresh_token=...; HttpOnly; Secure; SameSite=Lax

各リクエストでアクセストークンを使う

ヘッダーまたは Cookie:

Authorization: Bearer <access_token>
Cookie: access_token=...

サーバーは署名と exp を検証し、ユーザーを紐付ける。DB lookup 不要(ステートレス)。

アクセストークン失効時 – 遅延リフレッシュ

  1. 期限切れアクセストークンでのリクエスト → サーバーが 401 token_expired
  2. フロントエンドのインターセプターが POST /auth/refresh を呼ぶ。
  3. サーバーがリフレッシュトークンを検証し、新しいアクセストークン(必要なら新しいリフレッシュ)を発行。
  4. フロントエンドが元リクエストを再送。ユーザーは気づかない。

リフレッシュトークンのローテーション

リフレッシュごとに:

  1. 古いリフレッシュトークンを検証。
  2. 新しいアクセス + リフレッシュトークンを発行。
  3. 古いリフレッシュを使用済み/無効として DB に記録。

JWT でのログアウト

  • クライアント: Cookie/localStorage を削除。
  • サーバー: リフレッシュトークンを DB で失効させる、またはユーザーの token_version を更新して古い JWT を無効化。

JWT の長所と短所

長所: アクセストークンは DB 参照なし。SPA、モバイル、マイクロサービス、サーバーレスに向く。
短所: 失効管理が難しめ。トークンの保存ミスが起きやすい。リフレッシュトークンのセキュリティが重要。


4. システム B – サーバーサイドセッション(ステートフル)

コア概念

  • session_id: ランダム文字列の Cookie。DB/Redis のユーザー行に紐付く。短命。
  • remember_token: 任意の長命ランダム文字列。DB にはハッシュで保存。新しいセッションを発行するために使う。リフレッシュトークンの同等物。

セッション ID = 今日の入場券。
Remember トークン = 複数日のパス。

セッションでのログイン(Remember Me なし)

  1. メール + パスワードを検証。
  2. session_id を生成。sessions テーブルに有効期限つきで保存。
  3. Cookie 設定: session_id=...; HttpOnly; Secure; SameSite=Lax
  4. 期限が切れたら再ログイン。

セッションでのログイン(Remember Me あり)

  1. メール + パスワードを検証。
  2. session_id(短期)と remember_token(長期)を生成。
  3. 保存:
sessions
session_id | user_id | expires_at
---------------------------------
abcd1234   | 42      | +1 day

remember_tokens
hashed_token | user_id | expires_at
-----------------------------------
HASH(xyz...) | 42      | +30 days
  1. Cookie を設定:
  • session_id=abcd1234; HttpOnly; Secure
  • remember_token=xyz...; HttpOnly; Secure; Max-Age=30 days

各リクエストでセッションを使う

ブラウザが自動で session_id を送る。サーバーは DB/Redis を参照:

  • 見つかり有効なら → 認証済み。必要なら有効期限を延長。
  • ない/期限切れ → 一時的に未認証。

スライド式有効期限

有効なリクエストごとに expires_at を延長(例: 現在 +1 日)。アクティブなユーザーはログインしっぱなし。放置すると失効。

セッション期限切れ時 – Remember Me を使う

session_id がない/失効していて remember_token がある場合:

  1. remember_token をハッシュして検索。
  2. 有効なら → 自動ログインし、新しい session_id を発行。必要なら remember もローテーション。
  3. 無効/なし → 再ログインを要求。

セッションでのログアウト

  1. セッション行を削除し、session_id Cookie をクリア。
  2. Remember を使っている場合: 行を削除し、remember_token Cookie もクリア。

セッションの長所と短所

長所: 削除で簡単に失効。シンプルな思考モデル。従来型サイトや高セキュリティに向く。
短所: セッションストアが必要。毎リクエストで DB/Redis 参照。分散/モバイルのヘビーな構成では不利。


5. モダン UX の現実 – 「Remember Me」は見えない

多くのプロダクトはデフォルトで「ログイン状態を維持」します。チェックボックスを出さず、長命トークン(remember/リフレッシュ)を静かに使います。敏感な操作ではパスワードや MFA を再入力させることもあります。

プロダクトの選択肢:

  • 明示的に「Remember me」チェックを出す
  • 何も出さず常に記憶(ユーザーがログアウトするまで)

6. 並べて対応づける

概念マッピング

JWT ワールドセッションワールドメモ
アクセストークンセッション ID短命クレデンシャル
リフレッシュトークンRemember-me トークン長命クレデンシャル
署名検証DB/Redis 参照バリデーション手順
/auth/refreshRemember トークンで自動ログインリフレッシュ経路
token_expiredセッション期限切れ失敗モード

比較表

項目JWT ベース認証サーバーサイドセッション
短命なものアクセストークンセッション ID
長命なものリフレッシュトークンRemember-me トークン
毎リクエスト送るものアクセストークンセッション ID
検証方法署名/クレーム検証DB/Redis lookup
サーバー状態アクセストークンはなしセッションストア必須
リフレッシュ/auth/refreshRemember 経由で新しいセッション
ログアウトトークン削除/失効セッション/Remember 行を削除
向いているケースSPA、モバイル、API、マイクロサービス従来型 Web、モノリス、高セキュリティ

7. どちらを選ぶ?

SPA/モバイル/マイクロサービスがあり、セッションストアを持ちたくない、かつリフレッシュトークンを堅牢に扱えるなら JWT。パターン: アクセストークンは HttpOnly Cookie または Authorization ヘッダー、リフレッシュトークンは HttpOnly Cookie、401 をトリガーに遅延リフレッシュ。

単一バックエンドが主で、主にサーバーレンダリング、失効をシンプルにしたいならサーバーセッション。パターン: session_id Cookie、セッションテーブル(または Redis)、継続ログイン用に remember_tokens を追加。


8. 最終的な頭の地図

SIGN UP  (ユーザー + ハッシュ済みパスワード作成)
          |
          v
       LOG IN  (メール + パスワード検証)
          |
          v
   認証方式を選ぶ
   ---------------------
   |                   |
   v                   v

JWT ベース認証              サーバーセッション
-----------------------     -----------------------
ACCESS TOKEN を作成          SESSION_ID を作成
REFRESH TOKEN を作成         セッションを DB に保存
両方をクライアントへ送信     SESSION_ID Cookie を送信

毎リクエスト:                毎リクエスト:
  ACCESS TOKEN を送信          SESSION_ID を送信
  署名を検証                  DB lookup

期限切れ時:                  期限切れ時:
  REFRESH TOKEN を使用          REMEMBER TOKEN が有効なら:
  /auth/refresh で再発行             新しい SESSION_ID を発行
                                     無効なら再ログイン

ログアウト:                  ログアウト:
  トークンを削除/失効           セッション/Remember 行を削除

どちらも同じ要件を満たします。「一度ログインしたら、サインイン状態を保ちつつ安全を維持したい」だけです。

On this page

0. 全体像:裏側で何が起きている?
1. フェーズ1 – サインアップ(アカウント作成)
2. フェーズ2 – ログイン(認証)
2.2 Google / OAuth ログインを足す(同じパターン)
2.3 従来のメール+パスワード vs Google ログイン(詳細比較)
2.3.1 Google ログインでパスワードは消える?
2.3.2 アプリではパスワードの代わりに何を使う?
2.3.3 Google ログインでパスワードリセットは?
2.3.4 アカウント復旧と本人確認はどうなる?
2.3.5 新規ユーザー確認は?
2.3.6 Google ログインは安全?
2.3.7 Google アカウントにアクセスできなくなったら?
2.3.8 図式サマリー
2.3.9 Google ログインは認証フローのどこに入る?
2.3.10 パスワードはなくなる?
2.3.11 Q&A まとめ
3. システム A – JWT ベース認証(ステートレス)
コア概念
JWT でのログイン
各リクエストでアクセストークンを使う
アクセストークン失効時 – 遅延リフレッシュ
リフレッシュトークンのローテーション
JWT でのログアウト
JWT の長所と短所
4. システム B – サーバーサイドセッション(ステートフル)
コア概念
セッションでのログイン(Remember Me なし)
セッションでのログイン(Remember Me あり)
各リクエストでセッションを使う
スライド式有効期限
セッション期限切れ時 – Remember Me を使う
セッションでのログアウト
セッションの長所と短所
5. モダン UX の現実 – 「Remember Me」は見えない
6. 並べて対応づける
概念マッピング
比較表
7. どちらを選ぶ?
8. 最終的な頭の地図