認証(Authentication):「あなたは誰ですか?」
→ ユーザーの身元を確認する
→ ログイン処理がこれにあたる
認可(Authorization):「あなたは何をしていいですか?」
→ 認証済みユーザーに権限を与える
→ 管理者だけが見られるページなどがこれにあたる
【セッション方式】
サーバー側にセッション情報を保存(ステートフル)
→ サーバーが増えると全サーバーでセッション共有が必要
→ DBやRedisで一元管理が必要
【トークン方式(JWT)】
サーバー側に保存しない(ステートレス)
→ どのサーバーでも署名を検証するだけでいい
→ サーバーレスアーキテクチャと相性が良い
💡 ポイント: Next.js + SupabaseのようなサーバーレスアーキテクチャではJWTが好まれる。「どのインスタンスがリクエストを処理するかわからない」ので状態を持たないトークン方式が相性抜群
ブラウザが自動的にサーバーに送り続ける小さなデータの入れ物。
✅ ブラウザが自動で毎回送ってくれる
✅ HttpOnly属性でJSから読めない → XSS攻撃に強い
✅ SameSite属性でCSRF対策ができる
✅ SSR対応
❌ 別ドメインには送れない(CORS制約)
JSON形式のデータに署名をつけたトークン。
eyJhbGciOiJIUzI1NiJ9.eyJ1c2VySWQiOiIxMjMifQ.signature
↑ ↑ ↑
Header Payload 署名
(アルゴリズム情報) (ユーザー情報など) (改ざん検知)
| 保存場所 | XSS耐性 | CSRF耐性 | 自動送信 | SSR対応 | リロード後 |
|---|---|---|---|---|---|
| HttpOnly Cookie | ✅ | ⚠️ 対策必要 | ✅ | ✅ | ✅ |
| localStorage | ❌ | ✅ | ❌ | ❌ | ✅ |
| メモリ | ✅ | ✅ | ❌ | ❌ | ❌ 消える |
@supabase/ssr パッケージが
JWTをHttpOnly Cookieで管理し
サーバーサイドでも認証状態を読めるようにしてくれる
💡 ポイント: Next.js + SupabaseでSSRをするならJWTはHttpOnly Cookieに保存するのがベスト。
@supabase/ssrパッケージがこれを自動でやってくれる
【OAuthなしの場合】
サービスAにGoogleのメールを見せたい
→ サービスAにGoogleのパスワードを教えるしかない → 危険
【OAuthありの場合】
→ Googleが「サービスAにメールだけ見せる許可証」を発行
→ サービスAはパスワードを知らなくていい
① ユーザーが「Googleでログイン」をクリック
② アプリ → Googleの認証画面にリダイレクト
③ ユーザーがGoogleにログイン・許可を承認
④ Google → アプリに「認可コード」を返す
⑤ アプリ → Googleに認可コードを送り「アクセストークン」と交換
⑥ アプリ → アクセストークンでGoogleからユーザー情報を取得
⑦ アプリ独自のJWTを発行してログイン完了
OAuth 2.0:「認可」のプロトコル
→ 「〇〇してもいいですよ」の許可証
→ リソースへのアクセス権限を委譲する
OIDC(OpenID Connect):「認証」のプロトコル
→ OAuthの上に「認証」を追加した仕組み
→ 「この人は〇〇さんですよ」の身分証明
→ 「Googleでログイン」はOIDCを使っている
自前でホスティングできるOAuth/OIDC認証サーバー。「自前のAuth0」を作るイメージ。
✅ オープンソース・無料
✅ データを自社サーバーで管理できる(金融・医療系)
✅ 社内複数サービスのSSO基盤として使える
❌ 自前でホスティング・運用が必要
❌ 個人開発には完全にオーバースペック
💡 ポイント: 「Googleでログイン」の実態はOIDC。SupabaseやAuth0などのマネージドサービスを使うと複雑なOAuthフローを自分で実装しなくて済む
① XSS(Cross-Site Scripting)
攻撃:悪意あるJSを埋め込みlocalStorageのJWTを盗む
対策:
✅ JWTはHttpOnly Cookieに保存
✅ ユーザー入力を必ずエスケープ
✅ Content-Security-Policy(CSP)ヘッダーを設定
② CSRF(Cross-Site Request Forgery)
攻撃:罠サイトから勝手にリクエストを送らせる
対策:
✅ SameSite=Strict or Lax をCookieに設定
✅ CSRFトークンをリクエストに含める
③ ブルートフォース攻撃
攻撃:パスワードを総当たりで試す
対策:
✅ ログイン試行回数を制限(レートリミット)
✅ MFA(多要素認証)を導入
✅ Captchaを設置
④ トークン漏洩
攻撃:JWTが盗まれると有効期限まで使い放題
対策:
✅ JWTの有効期限を短くする(15分〜1時間)
✅ リフレッシュトークンで再発行する仕組みを使う
✅ HTTPS通信を強制する
アクセストークン(短命:15分〜1時間)
→ APIリクエストに使う
→ 漏洩しても短時間で無効
リフレッシュトークン(長命:数日〜数週間)
→ アクセストークンの再発行だけに使う
→ HttpOnly Cookieで厳重管理
流れ:
① アクセストークンの期限切れ
② リフレッシュトークンをサーバーに送る
③ 新しいアクセストークンを発行
④ ユーザーは再ログイン不要 ✅
❌ そのままDBに保存
❌ 単純な暗号化(復号できてしまう)
✅ bcryptでハッシュ化して保存
【登録時】
パスワード "password123"
↓ bcryptでハッシュ化(サーバー側で実行)
"$2b$10$xyz..." をDBに保存
【ログイン時】
入力されたパスワードを同じようにハッシュ化
↓ DBの値と比較
一致 → ログイン成功 ✅
// 登録時
const passwordHash = await bcrypt.hash('password123', 10);
// ログイン時
const isValid = await bcrypt.compare('password123', passwordHash);
bcryptが優れている理由:
SHA256などの単純なハッシュ → 高速・レインボーテーブルに弱い
bcrypt → 意図的に計算を遅くする・ソルトを自動付加
→ 同じパスワードでも毎回違うハッシュ値
→ 総当たり攻撃に強い ✅
💡 ポイント: マネージドサービス(Supabase・Auth0等)を使うとセキュリティのベストプラクティスが自動で適用される。自前実装するほどセキュリティの責任範囲が広がる
認証をまるごと外部サービスに任せる方法。ユーザーデータは外部サービスのDBに入る。
| サービス | 無料枠 | Next.js | Hono | 複雑さ | 向いている場面 |
|---|---|---|---|---|---|
| Supabase Auth | 50,000ユーザー | ✅ | ✅ | 低 | Supabase使用の個人開発 |
| Clerk | 10,000ユーザー | ✅ 最高 | ❌ | 最低 | Next.js単体SaaS |
| AWS Cognito | 50,000ユーザー | ⚠️ | ⚠️ | 高 | AWS統一・企業向け |
| Firebase Auth | 無制限 | ⚠️ | ❌ | 低 | モバイルアプリ中心 |
| Auth0 | 7,500ユーザー | ✅ | ✅ | 中 | 複雑な認証要件 |
// UIコンポーネントが付属 → ログイン画面をゼロで実装できる
import { SignIn } from '@clerk/nextjs';
export default function LoginPage() {
return <SignIn />; // これだけでログイン画面が完成
}
💡 ポイント: MickeyのスタックではSupabase Authが最もシンプルで強力。ClerkはNext.jsとの相性が最高だがSupabaseと組み合わせると設定が増える
認証ライブラリを使いつつ、ユーザーデータは自分のDBで管理する方法。
| NextAuth.js | Lucia | Better Auth | |
|---|---|---|---|
| Next.js相性 | ✅ 最高 | ✅ | ✅ |
| Hono相性 | ❌ | ✅ | ✅ |
| OAuth標準対応 | ✅ | ❌ 別途必要 | ✅ |
| 自分のDBで管理 | ✅ | ✅ | ✅ |
| 将来性 | ✅ | ⚠️ メンテナンスモード | ✅ 勢いあり |
// auth.ts
import { betterAuth } from 'better-auth';
import { drizzleAdapter } from 'better-auth/adapters/drizzle';
export const auth = betterAuth({
database: drizzleAdapter(db, { provider: 'pg' }),
socialProviders: {
google: {
clientId: process.env.GOOGLE_CLIENT_ID!,
clientSecret: process.env.GOOGLE_CLIENT_SECRET!,
},
},
});
// Honoとの連携
app.on(['GET', 'POST'], '/api/auth/*', (c) => {
return auth.handler(c.req.raw);
});
💡 ポイント: MickeyのNext.js + Hono + Supabaseスタックでセミマネージドを選ぶならBetter Authが最有力。HonoとNext.jsの両方に対応していてDrizzleとの連携も簡単
# 認証が必要なViewにデコレータをつけるだけ
from django.contrib.auth.decorators import login_required
@login_required
def my_page(request):
return render(request, 'mypage.html')
Djangoが自動でやってくれること:
✅ パスワードのハッシュ化(pbkdf2)
✅ セッション管理
✅ CSRFトークン
✅ @login_required デコレータ
✅ 管理画面の認証
// JWTの発行(ログイン時)
app.post('/auth/login', async (c) => {
const { email, password } = await c.req.json();
const user = await db.select().from(users)
.where(eq(users.email, email)).limit(1);
const isValid = await bcrypt.compare(password, user[0].passwordHash);
if (!isValid) return c.json({ error: 'Unauthorized' }, 401);
const token = await sign(
{ userId: user[0].id, exp: Math.floor(Date.now() / 1000) + 60 * 60 },
process.env.JWT_SECRET!
);
setCookie(c, 'token', token, {
httpOnly: true,
sameSite: 'Strict',
secure: true,
});
return c.json({ success: true });
});
// JWTの検証(ミドルウェア)
const authMiddleware = async (c, next) => {
const token = getCookie(c, 'token');
if (!token) return c.json({ error: 'Unauthorized' }, 401);
try {
const payload = await verify(token, process.env.JWT_SECRET!);
c.set('userId', payload.userId);
await next();
} catch {
return c.json({ error: 'Invalid token' }, 401);
}
};
app.use('/api/*', authMiddleware);
💡 ポイント: 完全自前実装はセキュリティの責任がすべて自分にかかる。学習目的や特殊な要件がない限り、マネージドサービスを使うのが賢明
スタート:認証を実装したい
↓
Q1. 自分のDBでユーザーを完全管理したいか?
├── No → Q2へ
└── Yes → Q5へ
↓
Q2. SupabaseをDBとして使っているか?
├── Yes → 【Supabase Auth】
└── No → Q3へ
↓
Q3. Next.js単体でSaaSを作っているか?
├── Yes → 【Clerk】
└── No → Q4へ
↓
Q4. AWSに統一したいか?企業向けSSO/SAMLが必要か?
├── Yes → 【AWS Cognito】
└── No → 【Auth0】
↓
Q5. Next.js + Honoの両方で認証を統一したいか?
├── Yes → 【Better Auth】
└── No → Q6へ
↓
Q6. Pythonが必要か?Djangoを使っているか?
├── Yes → 【Django標準認証 + simplejwt】
└── No → 【Hono自前JWT実装】
🥇 第一候補:Supabase Auth
→ DBと一体・RLSがそのまま使える
→ 追加コストなし・実装が最速
🥈 第二候補:Better Auth
→ ユーザーデータを自前DB(Drizzle)で管理したい場合
→ HonoとNext.jsの両方に対応
🥉 第三候補:Hono自前JWT
→ 認証の仕組みを深く学びたい場合
→ 特殊な認証フローが必要な場合
Supabase Auth
↓ ユーザーデータを自前管理したくなったら
Better Auth
↓ 完全にカスタマイズしたくなったら
Hono自前JWT実装
| ミス | 改善策 |
|---|---|
| JWTをlocalStorageに保存する | HttpOnly Cookieに保存する |
| パスワードをそのままDBに保存する | bcryptでハッシュ化して保存する |
| クライアント側でパスワードをハッシュ化する | サーバー側でハッシュ化する(HTTPSで通信を保護) |
| アクセストークンの有効期限を長くする | 短命に保ちリフレッシュトークンで再発行する |
| 個人開発にCognitoを採用する | SSO・SAML連携が不要ならSupabase Authで十分 |
| 最初から完全自前実装する | マネージドサービスから始めて必要に応じて移行する |
- 認証(誰か?)と認可(何をしていいか?)は別概念。SupabaseではAuth + RLSで両方を担う
- JWTはCookieの「中身の形式」、CookieはJWTの「運び方」。概念の層が違う
- bcryptはハッシュ値の中にソルトを埋め込むので、compareで取り出して再計算できる
- Supabase Authの最大のデメリットはSupabaseへの依存。移行時に認証データの扱いが複雑になる
- Better AuthはLuciaの後継的存在。HonoとNext.jsの両対応が強み
- Keycloakは「自前のAuth0」。金融・医療などデータを外部に出せない企業向け