Menu

Nexty認証機能利用ガイド

  • コアライブラリ: Better-auth
  • データベース: drizzleアダプター、テーブル: usersessionaccountverification
  • サポート機能
    • ログイン方法: Google、GitHub、Emailマジックリンク、Google One Tap
    • セキュリティ強化: Cloudflare Turnstile CAPTCHA
    • メールサービス: 新規ユーザー登録時のウェルカムメール
    • ロール・アクセス制御: user.role(デフォルトuseradminをサポート)、カプセル化されたgetSession()isAdmin()AuthGuard
    • ユーザーソース追跡: アクセスリンクパラメータに基づくユーザーソースの記録
    • ユーザーブロック: 管理者がリスクのあるユーザーを簡単にブロックしてシステムセキュリティを保護
    • ログイン方法の記録: ユーザーの最後のログイン方法をローカルに記録してより迅速なログイン完了を実現

必要な環境変数

  • 基本
    • NEXT_PUBLIC_SITE_URL (認証コールバックベースドメイン用)
    • BETTER_AUTH_SECRET (サーバーシークレットキー)
  • ソーシャルログイン
    • Google: NEXT_PUBLIC_GOOGLE_CLIENT_IDGOOGLE_CLIENT_SECRET
    • GitHub: NEXT_PUBLIC_GITHUB_CLIENT_IDGITHUB_CLIENT_SECRET
  • CAPTCHA (Turnstile)
    • NEXT_PUBLIC_TURNSTILE_SITE_KEY (フロントエンド)
    • TURNSTILE_SECRET_KEY (サーバー)

オプション: デプロイドメインに合わせてtrustedOriginslib/auth/index.ts)を調整してください。

サーバーサイドの使用方法

セッション読み取り・API保護

// 任意のServer Component / Route Handler / Server Action
import { getSession } from "@/lib/auth/server";
 
export async function SomeServerActions(req: Request) {
  const session = await getSession()
  const user = session?.user;
  if (!user) return actionResponse.unauthorized();
 
  // session.user: { id, email, role, ... }
  return actionResponse.success({});
}

ロール検証(管理者)

import { isAdmin } from "@/lib/auth/server";
 
export async function SomeAdminServerActions() {
  if (!(await isAdmin())) return actionResponse.forbidden('管理者権限が必要です。')
  // 管理者ロジック
}

ページレベルガード(同期リダイレクト)

// コンポーネント: components/auth/AuthGuard.tsx は既にカプセル化済み
import { AuthGuard } from "@/components/auth/AuthGuard";
 
export default async function LoginRequiredLayout() {
  return (
    <AuthGuard>
      {/* ログインユーザーにのみ表示されるコンテンツ */}
    </AuthGuard>
  );
}
// コンポーネント: components/auth/AuthGuard.tsx は既にカプセル化済み
import { AuthGuard } from "@/components/auth/AuthGuard";
 
export default async function AdminLayout() {
  return (
    <AuthGuard role="admin">
      {/* 管理者にのみ表示されるコンテンツ */}
    </AuthGuard>
  );
}

クライアントサイドの使用方法

セッション取得・ログイン状態確認

import { authClient } from "@/lib/auth/auth-client";
 
// Reactクライアントコンポーネント
const { data: session, isPending } = authClient.useSession();

ソーシャルログイン(Google/GitHub)

await authClient.signIn.social(
  {
    provider: "google", // または "github"
    callbackURL: window.location.origin, // ログイン後のリダイレクト先
    errorCallbackURL: "/redirect-error",
  },
  {
    onRequest: () => {/* ローディングUI */},
    onSuccess: (ctx) => {/* ログイン成功 */},
    onError: (ctx) => {/* エラーハンドリング */},
  }
);

Emailマジックリンクログイン(Turnstile検証付き)

import { Turnstile } from "@marsidev/react-turnstile";
 
// 1) フロントエンドでトークン取得
<Turnstile
  siteKey={process.env.NEXT_PUBLIC_TURNSTILE_SITE_KEY!}
  onSuccess={(token) => setCaptchaToken(token)}
/>
 
// 2) CAPTCHAを使ってマジックリンクログインを呼び出し
await authClient.signIn.magicLink({
  email, // ユーザー入力のメールアドレス
  name: "optional",
  callbackURL: window.location.origin,
  errorCallbackURL: "/redirect-error",
  fetchOptions: {
    headers: { "x-captcha-response": captchaToken }, // キー: サーバーに渡す
  },
});

Google One Tap(ユーザー名/パスワード入力不要)

import { authClient } from "@/lib/auth/auth-client";
 
await authClient.oneTap({
  fetchOptions: {
    onSuccess: () => window.location.reload(),
    onError: (ctx) => console.error(ctx.error),
  },
  onPromptNotification: (note) => console.log(note),
});

ログアウト

await authClient.signOut({
  fetchOptions: {
    onSuccess: () => router.refresh(),
  },
});

最後に使用したログイン方法

const lastLogin = authClient.getLastUsedLoginMethod(); // "google" | "github" | "email" | undefined

ロール・アカウント管理

ユーザーテーブルのロール: 'user' | 'admin'、デフォルトの新規登録はuserです。

ユーザーを管理者に昇格させる方法:

データベースのuserテーブルを開き、管理者に設定したいユーザーを見つけて、roleadminに設定します。

管理者アカウント設定

管理者アカウントで再ログインすると、管理者ディレクトリが表示されます。

管理者ディレクトリ

認証フローのビジネスフック(内蔵)

  • ユーザー作成後(databaseHooks.user.create.after):
    • Cookie referral_sourceからリファラルコードを読み取り、user.referralに書き込み
    • ウェルカムメール送信
  • これらのフックはlib/auth/index.tsで継続的に拡張できます(リスク制御、監査ログなど)。

よくある問題とトラブルシューティング

  • ログイン後に間違ったドメインにリダイレクトされる
    • BETTER_AUTH_URLまたはNEXT_PUBLIC_SITE_URLを正しいサイトアドレスに設定してください。
    • クロスドメインコールバックの場合、trustedOriginsを補完してください。
  • Turnstile検証が失敗する
    • フロントエンドでx-captcha-responseヘッダーの渡し方を確認;バックエンドではTURNSTILE_SECRET_KEYが必要です。
  • ソーシャルログインエラー
    • 対応するCLIENT_ID/SECRETを確認;プラットフォームダッシュボードでコールバックドメイン/パスをホワイトリストに追加してください。
  • セッション取得が空を返す
    • サーバーはheaders()経由でリクエストヘッダーを渡す必要があり、getSession()は既にカプセル化済み;クロスドメインまたはSameSiteポリシーによってCookieがブロックされていないか確認してください。

参考ファイル

  • 設定・プラグイン: lib/auth/index.ts
  • クライアントSDK: lib/auth/auth-client.ts
  • サーバーカプセル化: lib/auth/server.tsgetSessionisAdmin
  • ルートガードコンポーネント: components/auth/AuthGuard.tsx
  • ログインフォーム例: components/auth/LoginForm.tsx
  • One Tap: components/auth/GoogleOneTap.tsx
  • ユーザーメニュー/ログアウト: components/header/UserInfo.tsx