Nexty Authorization Feature Usage Guide
- Core Library: Better-auth
- Database:
drizzleadapter, tables:user,session,account,verification - Supported Features:
- Login Methods: Google, GitHub, Email Magic Link, Google One Tap
- Security Enhancement: Cloudflare Turnstile CAPTCHA
- Email Service: Welcome email for new user registration
- Role & Access Control:
user.role(defaultuser, supportsadmin), encapsulatedgetSession(),isAdmin()andAuthGuard - User Source Tracking: Record user source based on access link parameters
- User Blocking: Admins can easily block risky users to protect system security
- Login Method Recording: Locally record user's last login method for faster login completion
Required Environment Variables
- Basic:
NEXT_PUBLIC_SITE_URL(for auth callback base domain)BETTER_AUTH_SECRET(server secret key)
- Social Login:
- 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(frontend)TURNSTILE_SECRET_KEY(server)
Optional: Adjust trustedOrigins (lib/auth/index.ts) to match your deployment domain.
Server-side Usage
Reading Session & Protecting APIs
// Any 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({});
}Role Validation (Admin)
import { isAdmin } from "@/lib/auth/server";
export async function SomeAdminServerActions() {
if (!(await isAdmin())) return actionResponse.forbidden('Admin privileges required.')
// Admin logic
}Page-level Guard (Synchronous Redirect)
// Component: components/auth/AuthGuard.tsx already encapsulated
import { AuthGuard } from "@/components/auth/AuthGuard";
export default async function LoginRequiredLayout() {
return (
<AuthGuard>
{/* Content visible only to logged-in users */}
</AuthGuard>
);
}// Component: components/auth/AuthGuard.tsx already encapsulated
import { AuthGuard } from "@/components/auth/AuthGuard";
export default async function AdminLayout() {
return (
<AuthGuard role="admin">
{/* Content visible only to admins */}
</AuthGuard>
);
}Client-side Usage
Getting Session & Login Status
import { authClient } from "@/lib/auth/auth-client";
// React client component
const { data: session, isPending } = authClient.useSession();Social Login (Google/GitHub)
await authClient.signIn.social(
{
provider: "google", // or "github"
callbackURL: window.location.origin, // where to redirect after login
errorCallbackURL: "/redirect-error",
},
{
onRequest: () => {/* loading UI */},
onSuccess: (ctx) => {/* logged in */},
onError: (ctx) => {/* error handling */},
}
);Email Magic Link Login (with Turnstile Verification)
import { Turnstile } from "@marsidev/react-turnstile";
// 1) Frontend gets token
<Turnstile
siteKey={process.env.NEXT_PUBLIC_TURNSTILE_SITE_KEY!}
onSuccess={(token) => setCaptchaToken(token)}
/>
// 2) Call magic link login with CAPTCHA
await authClient.signIn.magicLink({
email, // User input email
name: "optional",
callbackURL: window.location.origin,
errorCallbackURL: "/redirect-error",
fetchOptions: {
headers: { "x-captcha-response": captchaToken }, // Key: pass to server
},
});Google One Tap (No Username/Password Input)
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),
});Sign Out
await authClient.signOut({
fetchOptions: {
onSuccess: () => router.refresh(),
},
});Last Used Login Method
const lastLogin = authClient.getLastUsedLoginMethod(); // "google" | "github" | "email" | undefinedRole & Account Management
User table roles: 'user' | 'admin', default new registration is user.
Method to promote a user to admin:
Open the database user table, find the user you want to set as admin, and set role to admin.

Now re-login with the admin account, and you can see the admin directory.

Business Hooks in Auth Flow (Built-in)
- After user creation (
databaseHooks.user.create.after):- Read referral code from Cookie
referral_sourceand write touser.referral - Send welcome email
- Read referral code from Cookie
- You can continue to extend these hooks in
lib/auth/index.ts(such as risk control, audit logs, etc.).
Common Issues & Troubleshooting
- Redirected to wrong domain after login
- Set
BETTER_AUTH_URLorNEXT_PUBLIC_SITE_URLto correct site address. - For cross-domain callbacks, supplement
trustedOrigins.
- Set
- Turnstile verification failed
- Frontend ensure passing
x-captcha-responseheader; backendTURNSTILE_SECRET_KEYrequired.
- Frontend ensure passing
- Social login error
- Check corresponding
CLIENT_ID/SECRET; add callback domain/path to whitelist in platform dashboard.
- Check corresponding
- Session retrieval returns empty
- Server must pass request headers through
headers(),getSession()already encapsulated; confirm cookies are not blocked by cross-domain or SameSite policy.
- Server must pass request headers through
Reference Files
- Configuration & plugins:
lib/auth/index.ts - Client SDK:
lib/auth/auth-client.ts - Server encapsulation:
lib/auth/server.ts(getSession,isAdmin) - Route guard component:
components/auth/AuthGuard.tsx - Login form example:
components/auth/LoginForm.tsx - One Tap:
components/auth/GoogleOneTap.tsx - User menu/logout:
components/header/UserInfo.tsx