第8章
認証と認可の設計 — セッション・JWT・OAuth
約5分
この章の目次開く
「ログイン機能を作って」と言われたとき、認証の方式選定から始められるかどうかは設計力の分かれ目です。 セッション、JWT、OAuth——それぞれの仕組みと使いどころを理解しておかないと、セキュリティ上の事故にもつながります。
この章では、認証と認可の違いから、主要な認証方式の比較までを整理します。
学習者「認証」と「認可」って同じじゃないの?セッションとJWT、どっちを選べばいいの…?
認証と認可の違い
この2つは混同されがちですが、まったく別の概念です。
| 概念 | 質問 | 例 |
|---|---|---|
| 認証(Authentication) | 「あなたは誰?」 | ログイン、本人確認 |
| 認可(Authorization) | 「あなたは何ができる?」 | 管理者のみ削除可能、一般ユーザーは閲覧のみ |
認証が通っても、認可されていない操作はブロックする必要があります。
主要な認証方式の比較
| 方式 | 状態管理 | 適するケース |
|---|---|---|
| セッション | サーバー側(メモリ/DB/Redis) | 従来型Webアプリ、SSR中心 |
| JWT | クライアント側(トークン) | SPA、モバイルアプリ、マイクロサービス |
| OAuth 2.0 | 外部サービスに委譲 | ソーシャルログイン、外部API連携 |
セッション方式の仕組み
セッション方式は、ログイン状態をサーバー側で管理する伝統的な手法です。
// Express + express-session の例
app.post('/login', async (req, res) => {
const user = await db.findByEmail(req.body.email);
if (!user || !await bcrypt.compare(req.body.password, user.passwordHash)) {
return res.status(401).json({ error: '認証失敗' });
}
req.session.userId = user.id;
res.json({ message: 'ログイン成功' });
});
app.get('/profile', (req, res) => {
if (!req.session.userId) {
return res.status(401).json({ error: '未認証' });
}
// req.session.userId でユーザー情報を取得
});typescript
JWTの仕組み
JWT(JSON Web Token)はトークンにユーザー情報を含め、サーバー側でセッションを持たない方式です。
JWTの構造
JWTは3つのパートをドット(.)で繋いだ文字列です。
eyJhbGciOiJIUzI1NiJ9.eyJ1c2VySWQiOjQyLCJleHAiOjE3MTk5MH0.署名
↑ Header ↑ Payload ↑ Signature| パート | 内容 | Base64デコードすると |
|---|---|---|
| Header | アルゴリズム情報 | {"alg":"HS256"} |
| Payload | ユーザー情報・有効期限 | {"userId":42,"exp":1719900000} |
| Signature | 改ざん検知用の署名 | サーバーの秘密鍵で生成 |
先生Payloadは暗号化されていないので、Base64デコードすれば誰でも中身を読める。パスワードやクレジットカード番号を入れてはいけないよ。
// jsonwebtoken の例
import jwt from 'jsonwebtoken';
// トークン生成(ログイン時)
const token = jwt.sign(
{ userId: user.id },
process.env.JWT_SECRET!,
{ expiresIn: '1h' }
);
// トークン検証(リクエストごと)
const decoded = jwt.verify(token, process.env.JWT_SECRET!);
// decoded.userId でユーザーを特定typescript
リフレッシュトークンの設計
アクセストークンの有効期限は短くし(15分〜1時間)、長期間のログイン維持にはリフレッシュトークンを使います。
リフレッシュトークンはサーバー側のDBに保存し、無効化できるようにしておく。ユーザーがログアウトしたら、そのリフレッシュトークンを削除する。OAuth 2.0 — 認可コードフロー
OAuth 2.0は、ユーザーのパスワードを預からずに外部サービスの認証を利用する仕組みです。「Googleでログイン」「GitHubでログイン」がこれにあたります。

ロールベースアクセス制御(RBAC)
認可の実装でよく使われるのが RBAC です。ユーザーにロール(役割)を割り当て、ロールごとに操作権限を定義します。
// シンプルなRBACの例
type Role = 'admin' | 'editor' | 'viewer';
const permissions: Record<Role, string[]> = {
admin: ['read', 'write', 'delete', 'manage_users'],
editor: ['read', 'write'],
viewer: ['read'],
};
function authorize(userRole: Role, action: string): boolean {
return permissions[userRole].includes(action);
}
// ミドルウェアとして使う
function requirePermission(action: string) {
return (req, res, next) => {
if (!authorize(req.user.role, action)) {
return res.status(403).json({ error: '権限がありません' });
}
next();
};
}
app.delete('/posts/:id', requirePermission('delete'), deletePost);typescript
方式選定のフローチャート
ちゃんと使うためのポイント
- 認証(誰か)と認可(何ができるか)は別の仕組みとして設計する
- JWTはステートレスだがトークン無効化が難しい。リフレッシュトークンをDB管理して対応する
- セッション方式はCSRF対策が必須
- JWTのPayloadは暗号化されていない。秘密情報を含めない
- OAuth 2.0はパスワードを預からずに済む。ソーシャルログインに適する
- 「どの方式が正解か」ではなく、要件(SPA?SSR?モバイル?)で選ぶ
次の章では、認証後のアプリケーション内部の状態管理の設計を見ていきます。