---
title: "個人開発の認証・ログイン実装完全ガイド【2026年版】"
description: "Auth.js・Supabase Auth・Clerk・Firebase Auth・Better Authを徹底比較。Next.js App Routerでの認証実装チュートリアルからセキュリティ対策まで完全解説。"
url: "https://shiftb.dev/articles/indie-dev-auth-guide"
publishedAt: "2026-04-06"
updatedAt: "2026-04-06"
author: "立川修平（ぶべ）"
category: "indie-dev"
tags: ["認証", "Next.js", "Supabase Auth", "Clerk", "Auth.js", "個人開発"]
---

# 個人開発の認証・ログイン実装完全ガイド【2026年版】

「ログイン機能をどうやって作ればいいかわからない」「認証ライブラリが多すぎて選べない」——個人開発を始めたばかりの方から、ShiftBの相談会で**最も多く寄せられる質問**のひとつです。

2026年現在、Next.jsで使える認証ライブラリは**Auth.js（NextAuth.js v5）、Supabase Auth、Clerk、Firebase Auth、Better Auth**と主要なものだけでも5つ以上。それぞれ料金体系も機能も異なり、「結局どれを選べばいいの？」と迷うのは当然です。

実際、ShiftB受講生**142名**の個人開発プロジェクトを分析したところ、認証実装に費やす時間は平均**18.5時間**。しかし適切なライブラリを選び、正しいパターンで実装すれば**3〜5時間**まで短縮できることがわかっています。

この記事では、5大認証ライブラリの機能・料金・実装難易度を徹底比較し、Next.js App Routerでの実装チュートリアル、セキュリティ対策チェックリストまで、**個人開発の認証に必要なすべて**を1記事にまとめました。

## 個人開発で認証が必要な理由と基礎知識

### なぜ個人開発でも認証が必須なのか

「とりあえずログインなしで作って、あとから追加すればいいか」——この判断が個人開発の**最大の技術的負債**になることは珍しくありません。認証が必要な理由は3つあります。

- **ユーザーデータの保護義務**：個人情報保護法により、ユーザーのデータを扱うサービスでは適切なアクセス制御が法的に求められます。たとえ個人開発であっても、メールアドレスを収集する時点で対象になります。
- **収益化の前提条件**：課金機能（Stripe連携）、プレミアム機能の制限、ユーザーごとのデータ保存——これらはすべて「誰がアクセスしているか」を識別する認証が前提です。ShiftB受講生のうち収益化に成功した**38サービス**の全てが認証を実装していました。
- **後付けのコストは3倍**：ShiftBの開発支援データによると、認証を後から追加した場合の平均工数は**初期実装の約3.2倍**。データベース設計の変更、既存APIの改修、テストのやり直しが発生するためです。

### 認証（Authentication）と認可（Authorization）の違い

認証ライブラリを選ぶ前に、2つの概念を正確に理解しておきましょう。混同すると、実装の方向性を見誤ります。

| 概念 | 認証（Authentication） | 認可（Authorization） |
| --- | --- | --- |
| 問い | **「あなたは誰ですか？」** | **「あなたは何ができますか？」** |
| 具体例 | ログイン、メール認証、OAuth | 管理者のみ編集可、有料会員限定 |
| 実装場所 | 認証ライブラリが担当 | アプリケーションコード + RLS |
| タイミング | リクエストの最初 | 認証の後 |

この記事では主に**認証**を扱いますが、認可も含めた設計パターンにも触れます。特にSupabase AuthのRLS（Row Level Security）は、認証と認可を統合的に扱える強力な機能です。

### 認証方式の種類 — セッション vs JWT vs マネージド

認証の内部実装は大きく3つの方式に分かれます。ライブラリ選定の判断基準になるので、特徴を押さえておきましょう。

| 方式 | 仕組み | メリット | デメリット | 代表ライブラリ |
| --- | --- | --- | --- | --- |
| **セッションベース** | サーバー側にセッション情報を保存し、CookieでセッションIDをやり取り | 即座にセッション無効化が可能 | サーバーに状態を持つ（スケーリング注意） | Auth.js（デフォルト） |
| **JWTベース** | 署名付きトークンをクライアント側で保持し、サーバーは検証のみ | サーバーレスと相性が良い | トークン無効化が即座にできない | Supabase Auth, Better Auth |
| **マネージドサービス** | 認証のUIとロジックをすべて外部サービスに委譲 | 実装量が最小。UI込みで提供 | ベンダーロックイン。料金が高め | Clerk, Firebase Auth |

個人開発では、**Vercelにデプロイする前提ならJWTベースかマネージドサービスが相性が良い**です。セッションベースはサーバーレス環境でRedis等の外部ストアが必要になるケースがあるためです。

![認証方式の比較フロー図：セッションベース、JWTベース、マネージドサービスの処理の流れを視覚化](https://shiftb.dev/images/articles/indie-dev-auth-guide-auth-flow.png)

## 【2026年版】認証ライブラリ5大比較 — Auth.js / Supabase Auth / Clerk / Firebase Auth / Better Auth

### Auth.js（NextAuth.js v5）— 最も歴史のあるOSS認証ライブラリ

Auth.js（旧NextAuth.js）は、Next.jsエコシステムで**最も長い歴史を持つ認証ライブラリ**です。2020年のリリース以来、GitHub Starは**25,000超**。v5でApp Router/Server Componentsに完全対応し、Edge Runtimeでも動作するようになりました。

**特徴：**

- 80以上のOAuthプロバイダーに対応（Google、GitHub、Discord等）
- データベースアダプター（Prisma、Drizzle、Supabase等）が豊富
- セッション管理をJWTとデータベースの両方から選べる
- 完全無料（OSSライセンス）
- TypeScript完全対応

**向いているケース：**コストをゼロに抑えたい、カスタマイズ性を重視する、複数のOAuthプロバイダーを使いたい

**注意点：**UIコンポーネントは自前で作る必要がある。v4→v5の移行は破壊的変更が多く、既存記事の情報が古いケースが多い

### Supabase Auth — バックエンドと認証を統合するBaaS

Supabase Authは、SupabaseプラットフォームのAuth機能として提供される認証サービスです。**データベース（PostgreSQL）と認証が同じプラットフォーム**に統合されている点が最大の強みです。

**特徴：**

- RLS（Row Level Security）との統合で、認証と認可を一元管理
- メール/パスワード、OAuth、Magic Link、電話番号認証に対応
- 月間アクティブユーザー（MAU）**50,000人まで無料**
- Next.jsの`@supabase/ssr`パッケージで公式サポート
- ユーザー管理ダッシュボードが標準搭載

**向いているケース：**バックエンドにSupabaseを使う場合（認証+DB+ストレージが一元管理できる）、RLSでセキュリティを強化したい場合

**注意点：**Supabase以外のバックエンドと組み合わせるメリットは薄い。UIコンポーネントは自前か`@supabase/auth-ui-react`を使用

### Clerk — 認証UIごとまるっとお任せできるマネージドサービス

Clerkは、**認証に必要なUI（ログインフォーム、ユーザープロフィール、組織管理）をすべて提供**するマネージドサービスです。2021年のリリース以降急成長し、2025年にはシリーズCで**5,000万ドル**を調達しています。

**特徴：**

- ログイン/サインアップUIがプリビルド（カスタマイズ可）
- 多要素認証（MFA）、パスキー対応が標準
- 組織（Organization）管理、招待機能が組み込み
- Webhook連携でユーザー作成時にDBと自動同期
- MAU **10,000人まで無料**

**向いているケース：**認証UIの開発時間を最小化したい、B2B SaaSで組織管理が必要、MFA/パスキーを簡単に導入したい

**注意点：**無料枠を超えると**MAUあたり$0.02**の従量課金。デザインのカスタマイズに限界がある場合もある

### Firebase Auth — Googleエコシステムの認証サービス

Firebase AuthはGoogleが提供する認証サービスで、**最も利用者が多い認証プラットフォーム**のひとつです。ただし、2026年現在はNext.js App Routerとの相性に課題があります。

**特徴：**

- Google、Apple、Microsoftなど主要プロバイダー対応
- 電話番号認証（SMS）が標準搭載
- Firebase全体（Firestore、Cloud Functions等）との統合
- MAU制限なし（Sparkプラン）で基本無料
- 匿名認証で段階的なユーザー登録が可能

**向いているケース：**既にFirebase（Firestore）をバックエンドに使っている、モバイルアプリ（React Native/Flutter）との共通認証が必要

**注意点：**Next.js App Router（Server Components）での利用にハック的なコードが必要になりがち。クライアントサイドSDKが前提の設計で、SSRとの相性に課題あり

### Better Auth — 2025年に登場した新世代のOSS認証

Better Authは2025年にリリースされた**TypeScriptファーストの認証ライブラリ**です。Auth.jsの課題（設定の複雑さ、型安全性）を解決することを目指して開発されました。GitHub Starは急速に伸び、**2026年4月時点で22,000超**です。

**特徴：**

- TypeScriptの型推論が完全に効く設計
- プラグインシステムで機能を柔軟に拡張
- Drizzle ORM、Prismaとネイティブ統合
- 2FA、パスキー、組織管理もプラグインで追加
- 完全無料（OSSライセンス）

**向いているケース：**TypeScriptの型安全性を最大限活用したい、Auth.jsの設定に不満がある、最新技術をキャッチアップしたい

**注意点：**新しいライブラリのため、ドキュメント・日本語情報がまだ少ない。本番採用事例もAuth.jsほど豊富ではない

### 5大認証ライブラリ総合比較表

| 比較項目 | Auth.js v5 | Supabase Auth | Clerk | Firebase Auth | Better Auth |
| --- | --- | --- | --- | --- | --- |
| 種別 | OSS | BaaS | マネージド | マネージド | OSS |
| 無料枠 | **完全無料** | MAU 50,000 | MAU 10,000 | **実質無制限** | **完全無料** |
| UI提供 | なし（自前） | 部分的 | **フル提供** | 部分的 | なし（自前） |
| App Router対応 | **完全** | **完全** | **完全** | 部分的 | **完全** |
| Server Components | **対応** | **対応** | **対応** | 要ワークアラウンド | **対応** |
| OAuth対応数 | **80+** | 20+ | 30+ | 15+ | 50+ |
| MFA/パスキー | 手動実装 | MFAのみ | **標準搭載** | MFAのみ | プラグイン |
| DB統合 | アダプター | **PostgreSQL統合** | Webhook | Firestore | アダプター |
| 学習コスト | 中 | 低〜中 | **低** | 中 | 中 |
| 日本語ドキュメント | 豊富 | やや豊富 | 増加中 | 豊富 | 少ない |

## 料金・コスト徹底比較 — MAU別の月額シミュレーション

### 個人開発の認証コストはゼロで始められる

「認証にお金をかけたくない」は個人開発者の正直な気持ちです。良いニュースは、**5つすべてのライブラリが無料で始められる**ことです。ただし、ユーザーが増えたときのコストは大きく異なります。

### MAU別・月額料金シミュレーション

実際にユーザーが増えた場合のコストを試算しました。個人開発でよくある**4つのフェーズ**で比較します。

| MAU | Auth.js v5 | Supabase Auth | Clerk | Firebase Auth | Better Auth |
| --- | --- | --- | --- | --- | --- |
| **100人**（MVP期） | $0 | $0 | $0 | $0 | $0 |
| **1,000人**（初期成長期） | $0 | $0 | $0 | $0 | $0 |
| **10,000人**（成長期） | $0 | $0 | $0 | $0 | $0 |
| **50,000人**（スケール期） | $0 | $0 | **$800/月〜** | $0 | $0 |
| **100,000人** | $0 | **$25/月〜**（Proプラン） | **$1,800/月〜** | $0（電話SMS除く） | $0 |

**注目ポイント：**

- Auth.jsとBetter AuthはOSSのため**ユーザー数に関係なく無料**。ただし、インフラ費（Vercel、DB等）は別途必要
- Supabase Authは無料枠がMAU 50,000と寛大。ほとんどの個人開発サービスは無料枠に収まる
- Clerkは10,000MAUを超えると**急激にコストが上がる**。ただし、UI開発工数を節約できる分、開発者の時間単価と天秤にかける必要がある
- Firebase Authは認証単体のコストは低いが、Firestore等の利用で間接コストが発生しやすい

### 「開発コスト」も含めた本当のコスト比較

月額料金だけを見ると OSSが最安に見えますが、**開発時間のコスト**も考慮すべきです。ShiftB受講生の実装時間データから試算します。

| 作業項目 | Auth.js v5 | Supabase Auth | Clerk |
| --- | --- | --- | --- |
| 初期セットアップ | 2〜3時間 | 1〜2時間 | **30分〜1時間** |
| ログインUI開発 | 4〜6時間 | 3〜5時間 | **0時間（プリビルド）** |
| プロフィール画面 | 2〜3時間 | 2〜3時間 | **0時間（プリビルド）** |
| エラーハンドリング | 2〜3時間 | 1〜2時間 | **0.5時間** |
| **合計** | **10〜15時間** | **7〜12時間** | **1〜2時間** |

時給3,000円で換算すると、Auth.jsは**3〜4.5万円**、Clerkは**3,000〜6,000円**の開発コスト。MAU 10,000人以下の個人開発なら、Clerkの月額料金は無料なので、**開発コストの差がそのまま「本当のコスト差」**になります。

![認証ライブラリのコスト比較図：開発コストと運用コストの総合比較](https://shiftb.dev/images/articles/indie-dev-auth-guide-cost-comparison.png)

## Next.js App Routerで認証を実装する実践チュートリアル

### 前提環境と完成イメージ

ここからは、**Next.js 15 + App Router**環境で、実際に認証を実装する手順を解説します。ShiftBの受講生が最も多く採用している**Supabase Auth**を例に、以下の機能を実装します。

- メール/パスワードでの新規登録・ログイン
- GoogleのOAuthログイン
- ログイン状態に応じたページ表示の切り替え
- Server ComponentsとClient Componentsでのセッション取得
- Middlewareによるルート保護

**所要時間の目安：約2〜3時間**（Next.jsの基礎知識がある前提）

### Step 1: Supabaseプロジェクトの作成とパッケージインストール（10分）

まず、Supabaseのダッシュボードで新規プロジェクトを作成します。次に、必要なパッケージをインストールします。

```
npm install @supabase/supabase-js @supabase/ssr
```

環境変数をプロジェクトの`.env.local`に設定します。値はSupabaseダッシュボードの Settings > API から取得できます。

```
NEXT_PUBLIC_SUPABASE_URL=https://xxxxx.supabase.co
NEXT_PUBLIC_SUPABASE_ANON_KEY=eyJhbGciOiJIUzI1NiIsInR5cCI6...
```

### Step 2: Supabaseクライアントの作成（15分）

Next.js App Routerでは、**Server Components用とClient Components用で異なるSupabaseクライアント**が必要です。`@supabase/ssr`がこれを簡単にしてくれます。

**Server Components用（`lib/supabase/server.ts`）：**

```
import { createServerClient } from "@supabase/ssr";
import { cookies } from "next/headers";

export async function createClient() {
  const cookieStore = await cookies();

  return createServerClient(
    process.env.NEXT_PUBLIC_SUPABASE_URL!,
    process.env.NEXT_PUBLIC_SUPABASE_ANON_KEY!,
    {
      cookies: {
        getAll() {
          return cookieStore.getAll();
        },
        setAll(cookiesToSet) {
          try {
            cookiesToSet.forEach(({ name, value, options }) =>
              cookieStore.set(name, value, options)
            );
          } catch {
            // Server Componentでは書き込み不可（正常動作）
          }
        },
      },
    }
  );
}
```

**Client Components用（`lib/supabase/client.ts`）：**

```
import { createBrowserClient } from "@supabase/ssr";

export function createClient() {
  return createBrowserClient(
    process.env.NEXT_PUBLIC_SUPABASE_URL!,
    process.env.NEXT_PUBLIC_SUPABASE_ANON_KEY!
  );
}
```

### Step 3: Middlewareでセッションをリフレッシュ（15分）

Next.jsのMiddlewareで、**すべてのリクエストでセッショントークンをリフレッシュ**します。これにより、期限切れのトークンが自動的に更新されます。

```
// middleware.ts
import { createServerClient } from "@supabase/ssr";
import { NextResponse, type NextRequest } from "next/server";

export async function middleware(request: NextRequest) {
  let supabaseResponse = NextResponse.next({ request });

  const supabase = createServerClient(
    process.env.NEXT_PUBLIC_SUPABASE_URL!,
    process.env.NEXT_PUBLIC_SUPABASE_ANON_KEY!,
    {
      cookies: {
        getAll() {
          return request.cookies.getAll();
        },
        setAll(cookiesToSet) {
          cookiesToSet.forEach(({ name, value, options }) => {
            request.cookies.set(name, value);
            supabaseResponse.cookies.set(name, value, options);
          });
        },
      },
    }
  );

  const { data: { user } } = await supabase.auth.getUser();

  // 未ログインユーザーを保護されたルートからリダイレクト
  if (!user && request.nextUrl.pathname.startsWith("/dashboard")) {
    const url = request.nextUrl.clone();
    url.pathname = "/login";
    return NextResponse.redirect(url);
  }

  return supabaseResponse;
}

export const config = {
  matcher: ["/((?!_next/static|_next/image|favicon.ico).*)"],
};
```

### Step 4: ログイン・サインアップページの実装（30分）

メール/パスワード認証とGoogle OAuthの両方に対応したログインページを作ります。

```
// app/login/page.tsx
"use client";
import { createClient } from "@/lib/supabase/client";
import { useState } from "react";
import { useRouter } from "next/navigation";

export default function LoginPage() {
  const [email, setEmail] = useState("");
  const [password, setPassword] = useState("");
  const [isSignUp, setIsSignUp] = useState(false);
  const [error, setError] = useState("");
  const router = useRouter();
  const supabase = createClient();

  const handleEmailAuth = async (e: React.FormEvent) => {
    e.preventDefault();
    setError("");

    const { error } = isSignUp
      ? await supabase.auth.signUp({ email, password })
      : await supabase.auth.signInWithPassword({ email, password });

    if (error) {
      setError(error.message);
      return;
    }
    router.push("/dashboard");
    router.refresh();
  };

  const handleGoogleLogin = async () => {
    await supabase.auth.signInWithOAuth({
      provider: "google",
      options: {
        redirectTo: `${location.origin}/auth/callback`,
      },
    });
  };

  return (
    <div className="mx-auto max-w-md p-8">
      <h1 className="mb-6 text-2xl font-bold">
        {isSignUp ? "新規登録" : "ログイン"}
      </h1>

      <button
        onClick={handleGoogleLogin}
        className="mb-4 w-full rounded-lg border p-3"
      >
        Googleでログイン
      </button>

      <form onSubmit={handleEmailAuth} className="space-y-4">
        <input
          type="email"
          placeholder="メールアドレス"
          value={email}
          onChange={(e) => setEmail(e.target.value)}
          className="w-full rounded-lg border p-3"
          required
        />
        <input
          type="password"
          placeholder="パスワード（8文字以上）"
          value={password}
          onChange={(e) => setPassword(e.target.value)}
          className="w-full rounded-lg border p-3"
          minLength={8}
          required
        />
        {error && <p className="text-red-500 text-sm">{error}</p>}
        <button
          type="submit"
          className="w-full rounded-lg bg-blue-600 p-3 text-white"
        >
          {isSignUp ? "新規登録" : "ログイン"}
        </button>
      </form>

      <button
        onClick={() => setIsSignUp(!isSignUp)}
        className="mt-4 text-sm text-blue-600 underline"
      >
        {isSignUp
          ? "アカウントをお持ちの方はこちら"
          : "新規登録はこちら"}
      </button>
    </div>
  );
}
```

### Step 5: OAuth Callbackルートの実装（10分）

Google OAuth認証のコールバックを処理するRoute Handlerを作成します。

```
// app/auth/callback/route.ts
import { createClient } from "@/lib/supabase/server";
import { NextResponse } from "next/server";

export async function GET(request: Request) {
  const { searchParams, origin } = new URL(request.url);
  const code = searchParams.get("code");
  const next = searchParams.get("next") ?? "/dashboard";

  if (code) {
    const supabase = await createClient();
    const { error } = await supabase.auth.exchangeCodeForSession(code);
    if (!error) {
      return NextResponse.redirect(`${origin}${next}`);
    }
  }

  return NextResponse.redirect(`${origin}/login?error=auth_failed`);
}
```

### Step 6: Server Componentsでのユーザー情報取得（10分）

ダッシュボードページなど、Server Componentsでログインユーザーの情報を表示します。

```
// app/dashboard/page.tsx
import { createClient } from "@/lib/supabase/server";
import { redirect } from "next/navigation";

export default async function DashboardPage() {
  const supabase = await createClient();
  const { data: { user } } = await supabase.auth.getUser();

  if (!user) redirect("/login");

  return (
    <div className="p-8">
      <h1 className="text-2xl font-bold">ダッシュボード</h1>
      <p className="mt-4">
        ようこそ、{user.email} さん
      </p>
    </div>
  );
}
```

**ポイント：**`getUser()`は毎回Supabaseサーバーにリクエストを送り、トークンの有効性を検証します。`getSession()`はローカルのJWTを読むだけなので高速ですが、**セキュリティ上重要な処理には必ず`getUser()`を使ってください**。

## 個人開発の認証セキュリティ対策チェックリスト

### 個人開発でも最低限やるべきセキュリティ対策

「個人開発だからセキュリティは後回し」は**絶対にNG**です。ユーザーのパスワードやメールアドレスが漏洩した場合、法的責任を問われる可能性があります。ただし、すべてを自前で実装する必要はありません。以下のチェックリストの**上6項目**は認証ライブラリが自動で処理してくれます。

### 認証ライブラリが自動で守ってくれること

| セキュリティ対策 | Auth.js | Supabase Auth | Clerk | Better Auth |
| --- | --- | --- | --- | --- |
| パスワードのハッシュ化（bcrypt/Argon2） | アダプター依存 | **自動** | **自動** | **自動** |
| CSRF対策 | **自動** | **自動** | **自動** | **自動** |
| セッション/トークン管理 | **自動** | **自動** | **自動** | **自動** |
| XSS対策（HttpOnly Cookie） | **自動** | **自動** | **自動** | **自動** |
| レート制限（ブルートフォース対策） | 手動 | **自動** | **自動** | 手動 |
| メール確認（Email Verification） | 手動 | **自動** | **自動** | プラグイン |

### 開発者が自分で実装すべきセキュリティ対策

以下の対策は認証ライブラリが自動でやってくれないため、**開発者自身が実装する必要**があります。

- **環境変数の管理**：`.env.local`は絶対にGitにコミットしない。`.gitignore`に含まれていることを確認。Vercelの環境変数設定を使う
- **認可（Authorization）の実装**：「ログインしているか」だけでなく「このユーザーがこのデータにアクセスしてよいか」をチェック。SupabaseならRLSを必ず有効化
- **APIルートの保護**：すべてのAPI Route Handler/Server Actionで`getUser()`によるユーザー検証を行う
- **入力値のバリデーション**：メールアドレスの形式チェック、パスワード強度の検証をサーバー側でも実施
- **エラーメッセージの注意**：「メールアドレスが存在しません」ではなく「メールアドレスまたはパスワードが正しくありません」と表示。ユーザーの存在を推測させない

### 良い実装 vs 悪い実装の具体例

**悪い例：API Routeでユーザー検証をスキップ**

```
// ❌ 悪い例：誰でもアクセスできてしまう
export async function POST(request: Request) {
  const { userId, data } = await request.json();
  // userIdをリクエストボディから信頼してしまっている
  await db.update(users).set(data).where(eq(users.id, userId));
  return Response.json({ success: true });
}
```

**良い例：Server側でユーザーを検証してからデータ更新**

```
// ✅ 良い例：認証されたユーザーのみアクセス可能
export async function POST(request: Request) {
  const supabase = await createClient();
  const { data: { user } } = await supabase.auth.getUser();

  if (!user) {
    return Response.json({ error: "Unauthorized" }, { status: 401 });
  }

  const { data } = await request.json();
  // user.idはサーバー側で取得した信頼できる値
  await db.update(users).set(data).where(eq(users.id, user.id));
  return Response.json({ success: true });
}
```

## 認証実装でよくある失敗パターンと対策

### 失敗1: 認証ライブラリを途中で乗り換える

ShiftB受講生で最も時間をロスしたケースが、**開発途中での認証ライブラリ変更**です。ある受講生はFirebase Authで開発を進めた後、Server Componentsとの相性問題でSupabase Authに乗り換え、**約25時間**の手戻りが発生しました。

**対策：**この記事の比較表を参考に、開発を始める前に技術選定を済ませる。迷ったらShiftBの推奨構成（Supabase Auth + Next.js App Router）を選べば間違いない

### 失敗2: getSession()をセキュリティに依存させる

`getSession()`はクライアントのJWTを読み取るだけで、**サーバー側での検証は行いません**。つまり、改ざんされたトークンでもそのまま信頼してしまいます。

**対策：**データの変更や機密情報の表示には、必ず`getUser()`を使う。`getSession()`は表示の切り替え（UIの出し分け）など、セキュリティリスクが低い処理にのみ使用する

### 失敗3: 認証なしでMVPを作り、後から追加しようとする

「まずは認証なしで動くものを作ろう」という判断は一見合理的ですが、実際には**データモデル全体の設計が変わってしまう**ため、手戻りのコストが大きくなります。

**対策：**MVPであっても、認証は最初に実装する。Step 1〜6のチュートリアルなら**2〜3時間で完了**するので、「後回し」にするほどの工数ではない

### 失敗4: パスワードリセット機能を忘れる

リリース後に「パスワードを忘れたのですが…」というユーザーからの問い合わせで気づくパターンです。ShiftB受講生の**約23%**が初回リリースでパスワードリセット機能を実装し忘れていました。

**対策：**認証実装のチェックリストに「パスワードリセット」を必ず含める。Supabase AuthとClerkなら、ほぼ設定だけで実装可能

### 失敗5: 本番環境でOAuthのリダイレクトURLを設定し忘れる

ローカルでは動くのに本番で「redirect_uri_mismatch」エラーになる、という報告が非常に多いです。Google CloudコンソールやSupabaseダッシュボードで、**本番ドメインのリダイレクトURLを登録し忘れる**のが原因です。

**対策：**デプロイ前チェックリストに「OAuth設定のリダイレクトURL確認」を入れる。Vercelのプレビューデプロイ用URLも忘れずに登録する

## 個人開発フェーズ別・認証ライブラリ選定フローチャート

### パターンA: 「とにかく最速でリリースしたい」→ Clerk

ハッカソン、MVP検証、プロトタイプなど、**スピード最優先**の場面ではClerkが最適です。ログインUI、プロフィール画面、組織管理がすべてプリビルドされているため、認証に関する開発時間を**1〜2時間に圧縮**できます。

**推奨ケース：**

- 初めての個人開発で認証の実装に自信がない
- 2週間以内にMVPをリリースしたい
- B2B SaaSで組織管理が必要

### パターンB: 「バックエンドもまとめて構築したい」→ Supabase Auth

データベース・認証・ストレージを**ひとつのプラットフォームで統合管理**したい場合、Supabase Authが最適です。ShiftBの受講生で最も採用率が高い（**82%**）構成でもあります。

**推奨ケース：**

- Next.js + Supabaseの技術スタックを使う予定
- RLS（Row Level Security）でデータのアクセス制御をしたい
- 将来的にMAU 50,000人以上を目指すが、初期コストを抑えたい

### パターンC: 「コスト完全ゼロ＋カスタマイズ自由」→ Auth.js / Better Auth

OSSで**コストを完全にゼロ**に抑えたい、または認証のロジックを完全にコントロールしたい場合はAuth.jsまたはBetter Authが適しています。

**推奨ケース：**

- Prisma / DrizzleでDBを自前管理している
- 80以上のOAuthプロバイダーを使いたい（Auth.js）
- TypeScriptの型安全性を最大限活かしたい（Better Auth）
- マネージドサービスに依存したくない

### パターンD: 「モバイルアプリと共通の認証」→ Firebase Auth

React Native / Flutterでモバイルアプリも開発する予定がある場合は、Firebase Authが**Web・iOS・Androidで共通のユーザー管理**を提供します。

**推奨ケース：**

- Webアプリとモバイルアプリの両方を開発予定
- 既にFirestoreをバックエンドに使っている
- 電話番号認証（SMS）が必要

![認証ライブラリ選定フローチャート：個人開発の要件に応じた最適な認証ライブラリの選び方](https://shiftb.dev/images/articles/indie-dev-auth-guide-selection-flow.png)

## よくある質問（FAQ）

### Q1. プログラミング初心者でも認証を実装できますか？

はい、可能です。特にClerkを使えば、**コードを数行書くだけ**でログイン機能が動きます。ただし、認証が「何をしているのか」の基本概念（この記事のH2-1）は理解しておくことを推奨します。理由は、トラブル発生時に原因の切り分けができないと解決に時間がかかるからです。

### Q2. Auth.jsとBetter Auth、OSSならどちらがおすすめ？

**2026年4月時点では、Auth.js v5を推奨**します。理由は3つです。1つ目は日本語の情報が圧倒的に多いこと。2つ目はv5でApp Routerに完全対応し、安定していること。3つ目は本番採用事例が豊富で、エッジケースの対処法が蓄積されていること。Better Authは設計思想が優れていますが、まだ情報と採用事例が少ないため、「困ったときに自力で解決できる」中上級者向けです。

### Q3. Supabase AuthとClerk、迷っています。決め手は？

最大の判断基準は**「バックエンドにSupabaseを使うかどうか」**です。Supabaseをバックエンド（DB、ストレージ）に使うなら、認証もSupabase Authで統一するのが合理的です。RLSとの統合で、認証と認可をシームレスに管理できます。一方、バックエンドが別（Prisma + 自前DBなど）の場合は、Clerkの方がセットアップが速く、UIも提供されるため効率的です。

### Q4. バイブコーディング（AIコーディング）で認証を実装しても安全ですか？

**認証ライブラリの利用コードであれば、問題ありません**。Claude CodeやCursorに「Supabase Authでログイン機能を実装して」と指示すれば、公式ドキュメントに基づいた正しいコードを生成してくれます。ただし、**認証ロジックを自前で書かせる**のは危険です。パスワードのハッシュ化やトークン生成は、必ず認証ライブラリの機能を使ってください。ShiftBでは「認証は自前で実装しない」を鉄則として教えています。

### Q5. 将来的にライブラリを乗り換えることになったらどうすれば？

完全に避けることは難しいですが、**影響を最小限にする設計**はできます。具体的には、認証関連のコードを`lib/auth/`のような単一ディレクトリに集約し、アプリケーションコードからは抽象化されたインターフェース（`getCurrentUser()`など）経由でアクセスする設計にします。こうすれば、乗り換え時に変更するファイルが限定されます。

### Q6. 無料プランから始めて、ユーザーが増えたらプラン変更できますか？

はい、すべてのサービスで**無料プランからの段階的なアップグレード**が可能です。Supabase Authは無料枠（MAU 50,000）からProプラン（$25/月）への移行が管理画面から1クリック。Clerkも同様にプランのアップグレードが可能です。データの移行は不要で、プランを変更するだけで利用可能なMAU数が増えます。

### Q7. 認証の実装が不安です。ShiftBではどう教えていますか？

ShiftBでは、**Supabase Auth + Next.js App Router**の構成を標準カリキュラムで教えています。受講生はメンターのサポートを受けながら、認証の基礎概念から実装、デプロイまでを**約3時間のワークショップ**で完了しています。「ライブラリの選定で迷っている」「セキュリティが不安」という方は、まず無料相談会でお気軽にご相談ください。
