Nexty認証機能利用ガイド
- コアライブラリ: Better-auth
- データベース:
drizzle
アダプター、テーブル:user
、session
、account
、verification
- サポート機能:
- ログイン方法: Google、GitHub、Emailマジックリンク、Google One Tap
- セキュリティ強化: Cloudflare Turnstile CAPTCHA
- メールサービス: 新規ユーザー登録時のウェルカムメール
- ロール・アクセス制御:
user.role
(デフォルトuser
、admin
をサポート)、カプセル化されたgetSession()
、isAdmin()
、AuthGuard
- ユーザーソース追跡: アクセスリンクパラメータに基づくユーザーソースの記録
- ユーザーブロック: 管理者がリスクのあるユーザーを簡単にブロックしてシステムセキュリティを保護
- ログイン方法の記録: ユーザーの最後のログイン方法をローカルに記録してより迅速なログイン完了を実現
必要な環境変数
- 基本:
NEXT_PUBLIC_SITE_URL
(認証コールバックベースドメイン用)BETTER_AUTH_SECRET
(サーバーシークレットキー)
- ソーシャルログイン:
- Google:
NEXT_PUBLIC_GOOGLE_CLIENT_ID
、GOOGLE_CLIENT_SECRET
- GitHub:
NEXT_PUBLIC_GITHUB_CLIENT_ID
、GITHUB_CLIENT_SECRET
- Google:
- CAPTCHA (Turnstile):
NEXT_PUBLIC_TURNSTILE_SITE_KEY
(フロントエンド)TURNSTILE_SECRET_KEY
(サーバー)
オプション: デプロイドメインに合わせてtrustedOrigins
(lib/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
テーブルを開き、管理者に設定したいユーザーを見つけて、role
をadmin
に設定します。

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

認証フローのビジネスフック(内蔵)
- ユーザー作成後(
databaseHooks.user.create.after
):- Cookie
referral_source
からリファラルコードを読み取り、user.referral
に書き込み - ウェルカムメール送信
- Cookie
- これらのフックは
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.ts
(getSession
、isAdmin
) - ルートガードコンポーネント:
components/auth/AuthGuard.tsx
- ログインフォーム例:
components/auth/LoginForm.tsx
- One Tap:
components/auth/GoogleOneTap.tsx
- ユーザーメニュー/ログアウト:
components/header/UserInfo.tsx