| name | nexty-auth |
| description | Implement authentication in NEXTY.DEV using Better Auth. Use when checking sessions, protecting routes, adding social logins, or managing user roles. Covers server/client auth, guards, and admin checks. |
Authentication in NEXTY.DEV
Overview
- Library: Better Auth with Drizzle adapter
- Providers: Google, GitHub, Magic Link
- Config:
lib/auth/index.ts, lib/auth/auth-client.ts
- Server helpers:
lib/auth/server.ts
Server-Side Auth
Get Session in Server Components
import { getSession } from '@/lib/auth/server';
export default async function MyPage() {
const session = await getSession();
if (!session) {
redirect('/login');
}
const user = session.user;
return <div>Welcome, {user.name}</div>;
}
Get Session in Server Actions
'use server';
import { getSession } from '@/lib/auth/server';
import { actionResponse } from '@/lib/action-response';
export async function myServerAction() {
const session = await getSession();
if (!session) {
return actionResponse.unauthorized();
}
const userId = session.user.id;
}
Admin Check
'use server';
import { isAdmin } from '@/lib/auth/server';
import { actionResponse } from '@/lib/action-response';
export async function adminOnlyAction() {
if (!(await isAdmin())) {
return actionResponse.forbidden('Admin privileges required.');
}
}
Get Session in API Routes
import { getSession } from '@/lib/auth/server';
import { apiResponse } from '@/lib/api-response';
export async function GET() {
const session = await getSession();
if (!session) {
return apiResponse.unauthorized();
}
}
Client-Side Auth
Using Auth Client
'use client';
import { authClient } from '@/lib/auth/auth-client';
const { data: session } = await authClient.getSession();
await authClient.signOut();
await authClient.signIn.social({ provider: 'google' });
await authClient.signIn.social({ provider: 'github' });
await authClient.signIn.magicLink({ email: 'user@example.com' });
useSession Hook
'use client';
import { authClient } from '@/lib/auth/auth-client';
export function MyComponent() {
const { data: session, isPending } = authClient.useSession();
if (isPending) {
return <div>Loading...</div>;
}
if (!session) {
return <div>Not logged in</div>;
}
return <div>Hello, {session.user.name}</div>;
}
Protected Route Patterns
Server Component Page
import { getSession } from '@/lib/auth/server';
import { redirect } from 'next/navigation';
export default async function MyProtectedPage() {
const session = await getSession();
if (!session) {
redirect('/login');
}
return <div>Protected content for {session.user.email}</div>;
}
Admin-Only Page
import { isAdmin } from '@/lib/auth/server';
import { redirect } from 'next/navigation';
export default async function MyAdminPage() {
if (!(await isAdmin())) {
redirect('/403');
}
return <div>Admin only content</div>;
}
Client Component with Auth Guard
'use client';
import { authClient } from '@/lib/auth/auth-client';
import { useRouter } from 'next/navigation';
import { useEffect } from 'react';
export function ProtectedComponent({ children }) {
const { data: session, isPending } = authClient.useSession();
const router = useRouter();
useEffect(() => {
if (!isPending && !session) {
router.push('/login');
}
}, [session, isPending, router]);
if (isPending) {
return <div>Loading...</div>;
}
if (!session) {
return null;
}
return <>{children}</>;
}
User Data Access
Session User Properties
const session = await getSession();
const user = session?.user;
user.id
user.email
user.name
user.image
user.role
user.emailVerified
user.createdAt
user.updatedAt
Query User from Database
import { db } from '@/lib/db';
import { user as userSchema } from '@/lib/db/schema';
import { eq } from 'drizzle-orm';
const [user] = await db.select()
.from(userSchema)
.where(eq(userSchema.id, userId))
.limit(1);
Login UI Components
Using Existing Components
import { LoginDialog } from '@/components/auth/LoginDialog';
import { LoginForm } from '@/components/auth/LoginForm';
import { LoginButton } from '@/components/header/LoginButton';
import { GoogleOneTap } from '@/components/auth/GoogleOneTap';
<LoginDialog />
<LoginForm />
<LoginButton />
<GoogleOneTap />
Sign Out
Server-Side (API Route)
import { auth } from '@/lib/auth';
export async function POST(request: Request) {
await auth.api.signOut({ headers: request.headers });
return Response.redirect('/');
}
Client-Side
'use client';
import { authClient } from '@/lib/auth/auth-client';
import { useRouter } from 'next/navigation';
function SignOutButton() {
const router = useRouter();
const handleSignOut = async () => {
await authClient.signOut();
router.push('/');
};
return <button onClick={handleSignOut}>Sign Out</button>;
}
Adding Social Providers
To add a new social provider, update lib/auth/index.ts:
export const auth = betterAuth({
socialProviders: {
google: {
clientId: process.env.NEXT_PUBLIC_GOOGLE_CLIENT_ID!,
clientSecret: process.env.GOOGLE_CLIENT_SECRET!,
},
github: {
clientId: process.env.NEXT_PUBLIC_GITHUB_CLIENT_ID!,
clientSecret: process.env.GITHUB_CLIENT_SECRET!,
},
},
});
Then add corresponding env variables.
Checklist
- Use
getSession() for server-side auth checks
- Use
isAdmin() for admin-only operations
- Use
authClient for client-side auth
- Always redirect unauthenticated users appropriately
- Never trust client-provided user roles
- Use existing auth components when possible
- Keep secrets in environment variables