Server-side authentication utilities for Next.js App Router. These functions provide access to authentication state and user data in Server Components, Route Handlers, and Server Actions.
Required Setup:
clerkMiddleware() must be configured in middleware.ts for auth() to workCLERK_SECRET_KEY environment variable required for server-side authDefault Behaviors:
auth() returns userId as null when signed out (not undefined)auth.protect() throws/redirects if not authenticated or authorizedcurrentUser() returns null if not authenticated (makes API call to Clerk backend)currentUser() is automatically deduped per request (multiple calls = one API call)treatPendingAsSignedOut: false by default (pending sessions treated as signed in)acceptsToken: 'session_token' by default (can accept 'm2m_token', 'oauth_token', or 'any')auth.protect() redirects to sign-in for document requests, returns 404 for API requests when not authenticatedauth.protect() returns 404 when authenticated but not authorized (regardless of request type)Threading Model:
auth() calls in same request share session state (middleware processes once)currentUser() calls are deduped via fetch() (one API call per request)auth() calls in same component/route handler share same auth stateLifecycle:
auth() reads session from request (processed by middleware)currentUser() fetches user from Clerk backend API (counts towards rate limit)auth.protect() throws error that Next.js catches and handles (redirect or 404)Edge Cases:
treatPendingAsSignedOut: true to treat as signed outacceptsToken to accept M2M or OAuth tokensauth.protect() behavior differs for document vs API requests (redirect vs 404)currentUser() makes API call (slower than auth(), use when you need full user object)auth() is lighter weight (no API call, just reads session from middleware)auth.protect() override environment variablesredirectToSignIn() and redirectToSignUp() never return (trigger redirect immediately)Exceptions:
auth() to throw errorauth.protect() throws/redirects if not authenticated (never returns normally)currentUser() throws if Clerk API call failsacceptsToken causes authentication to failredirectToSignIn() and redirectToSignUp() never return (TypeScript never type)Returns the Auth object with authentication state and redirect methods. Only available in App Router server contexts.
/**
* Returns Auth object with authentication state
* Available in Server Components, Route Handlers, and Server Actions
* Requires clerkMiddleware() to be configured
* @param options - Optional configuration for auth behavior
* @returns Promise resolving to SessionAuthWithRedirect object
*/
function auth(options?: AuthOptions): Promise<SessionAuthWithRedirect>;
interface AuthOptions {
/**
* If true, treats pending sessions as signed out
* Useful for conditional rendering based on auth state
* @default false
*/
treatPendingAsSignedOut?: boolean;
/**
* Token type to accept for authentication
* @default 'session_token'
*/
acceptsToken?: 'session_token' | 'm2m_token' | 'oauth_token' | 'any';
}
interface SessionAuthWithRedirect {
/**
* Current user's ID, null if signed out
*/
userId: string | null;
/**
* Current session ID, null if signed out
*/
sessionId: string | null;
/**
* Active organization ID, null if no active organization
*/
orgId: string | null;
/**
* Current user's role in active organization
*/
orgRole: string | null;
/**
* Active organization slug
*/
orgSlug: string | null;
/**
* Current user's permissions in active organization
*/
orgPermissions: string[] | null;
/**
* Actor identifier for impersonation scenarios
*/
actor: any | null;
/**
* Session status: 'active', 'expired', 'abandoned', 'removed', 'pending'
*/
sessionStatus: string;
/**
* Get session token or custom JWT template
* @param options - Token retrieval options
* @returns Promise resolving to token string or null
*/
getToken: (options?: GetTokenOptions) => Promise<string | null>;
/**
* Check if user has permission or role
* @param params - Permission or role to check
* @returns True if user has permission/role
*/
has: (params: HasParams) => boolean;
/**
* Redirect user to sign-in page
* Can only access URLs defined via environment variables or clerkMiddleware
* @param options - Redirect options
* @returns Never returns, triggers redirect
*/
redirectToSignIn: (options?: RedirectOptions) => never;
/**
* Redirect user to sign-up page
* Can only access URLs defined via environment variables or clerkMiddleware
* @param options - Redirect options
* @returns Never returns, triggers redirect
*/
redirectToSignUp: (options?: RedirectOptions) => never;
}
interface GetTokenOptions {
/**
* JWT template name for custom tokens
*/
template?: string;
/**
* Skip token cache and fetch fresh token
*/
skipCache?: boolean;
}
interface HasParams {
/**
* Permission to check (e.g., 'org:team:manage')
*/
permission?: string;
/**
* Role to check (e.g., 'admin', 'member')
*/
role?: string;
}
interface RedirectOptions {
/**
* URL to redirect back to after authentication
* If null, no return URL is set
*/
returnBackUrl?: string | URL | null;
}Usage Example - Server Component:
import { auth } from '@clerk/nextjs/server';
export default async function DashboardPage() {
const { userId, sessionId, orgId } = await auth();
if (!userId) {
return <div>Not authenticated</div>;
}
return (
<div>
<h1>Dashboard</h1>
<p>User ID: {userId}</p>
<p>Session ID: {sessionId}</p>
{orgId && <p>Organization ID: {orgId}</p>}
</div>
);
}Usage Example - Route Handler:
import { auth } from '@clerk/nextjs/server';
import { NextResponse } from 'next/server';
export async function GET() {
const { userId } = await auth();
if (!userId) {
return NextResponse.json({ error: 'Unauthorized' }, { status: 401 });
}
// Fetch user-specific data
const data = await fetchUserData(userId);
return NextResponse.json({ data });
}Usage Example - Server Action:
'use server';
import { auth } from '@clerk/nextjs/server';
export async function updateProfile(formData: FormData) {
const { userId } = await auth();
if (!userId) {
throw new Error('Unauthorized');
}
const name = formData.get('name') as string;
// Update user profile
await updateUserProfile(userId, { name });
return { success: true };
}Usage Example - Get Token:
import { auth } from '@clerk/nextjs/server';
export async function GET() {
const { userId, getToken } = await auth();
if (!userId) {
return new Response('Unauthorized', { status: 401 });
}
// Get default session token
const token = await getToken();
// Get custom JWT template
const supabaseToken = await getToken({ template: 'supabase' });
// Use token for external API calls
const response = await fetch('https://api.example.com/data', {
headers: {
Authorization: `Bearer ${token}`,
},
});
return response;
}Usage Example - Check Permissions:
import { auth } from '@clerk/nextjs/server';
export default async function AdminPage() {
const { userId, has } = await auth();
if (!userId) {
return <div>Not authenticated</div>;
}
const isAdmin = has({ role: 'admin' });
const canManageTeam = has({ permission: 'org:team:manage' });
if (!isAdmin) {
return <div>Access denied</div>;
}
return (
<div>
<h1>Admin Panel</h1>
{canManageTeam && <button>Manage Team</button>}
</div>
);
}Usage Example - Redirect Methods:
import { auth } from '@clerk/nextjs/server';
export default async function ProtectedPage() {
const { userId, redirectToSignIn } = await auth();
if (!userId) {
// Redirects to sign-in with return URL
redirectToSignIn({ returnBackUrl: '/dashboard' });
}
return <div>Protected content</div>;
}Protects routes with authentication and authorization checks. Automatically handles redirects and error responses.
/**
* Protects routes with authentication/authorization checks
* Returns Auth object if authorized, redirects/404 otherwise
* @param params - Authorization parameters or condition function
* @param options - Protection options
* @returns Promise resolving to SignedInAuthObject if authorized
*/
auth.protect(
params?: CheckAuthorizationParams | AuthorizationCondition,
options?: AuthProtectOptions
): Promise<SignedInAuthObject>;
interface CheckAuthorizationParams {
/**
* Required permission for authorization
*/
permission?: string;
/**
* Required role for authorization
*/
role?: string;
}
type AuthorizationCondition = (
has: (params: HasParams) => boolean
) => boolean;
interface AuthProtectOptions {
/**
* Token type to accept for authentication
* @default 'session_token'
*/
token?: 'session_token' | 'm2m_token' | 'oauth_token' | 'any';
/**
* Custom URL to redirect to if not authenticated
*/
unauthenticatedUrl?: string;
/**
* Custom URL to redirect to if not authorized
*/
unauthorizedUrl?: string;
}
interface SignedInAuthObject {
userId: string;
sessionId: string;
orgId: string | null;
orgRole: string | null;
orgSlug: string | null;
orgPermissions: string[] | null;
actor: any | null;
sessionStatus: string;
getToken: (options?: GetTokenOptions) => Promise<string | null>;
has: (params: HasParams) => boolean;
}Protection Behavior Table:
| Authenticated | Authorized | Request Type | auth.protect() will |
|---|---|---|---|
| Yes | Yes | Any | Return Auth object |
| Yes | No | Any | Return 404 error |
| No | No | Document (page) | Redirect to sign-in |
| No | No | API request | Return 404 error |
Usage Example - Basic Protection:
import { auth } from '@clerk/nextjs/server';
export default async function ProtectedPage() {
// Throws error if not authenticated, redirects to sign-in
const { userId } = await auth.protect();
// This code only runs if user is authenticated
return <div>Welcome {userId}</div>;
}Usage Example - Role-Based Protection:
import { auth } from '@clerk/nextjs/server';
export default async function AdminPage() {
// Returns 404 if user doesn't have admin role
const { userId } = await auth.protect({ role: 'admin' });
return <div>Admin Panel for {userId}</div>;
}Usage Example - Permission-Based Protection:
import { auth } from '@clerk/nextjs/server';
export default async function TeamManagementPage() {
// Returns 404 if user doesn't have permission
await auth.protect({ permission: 'org:team:manage' });
return <div>Team Management</div>;
}Usage Example - Custom Condition:
import { auth } from '@clerk/nextjs/server';
export default async function FeaturePage() {
// Custom authorization logic
await auth.protect((has) => {
return has({ permission: 'feature:access' }) || has({ role: 'admin' });
});
return <div>Feature Content</div>;
}Usage Example - Route Handler:
import { auth } from '@clerk/nextjs/server';
import { NextResponse } from 'next/server';
export async function POST(req: Request) {
// Protects API route, returns 404 if not authenticated
const { userId } = await auth.protect();
const body = await req.json();
// Process authenticated request
await createResource(userId, body);
return NextResponse.json({ success: true });
}Usage Example - Custom Redirect URLs:
import { auth } from '@clerk/nextjs/server';
export default async function PremiumPage() {
await auth.protect(
{ role: 'premium' },
{
unauthenticatedUrl: '/sign-in',
unauthorizedUrl: '/upgrade',
}
);
return <div>Premium Content</div>;
}Usage Example - Machine-to-Machine Token:
import { auth } from '@clerk/nextjs/server';
import { NextResponse } from 'next/server';
export async function POST(req: Request) {
// Accept M2M tokens for API authentication
const authObject = await auth.protect({ token: 'm2m_token' });
// authObject.isAuthenticated is true for M2M tokens
return NextResponse.json({ success: true });
}Returns the current User object from Clerk's backend or null if not authenticated.
/**
* Returns the Backend User object of currently active user
* Available in Server Components, Route Handlers, and Server Actions
* Automatically deduped per request via fetch()
* Counts towards Backend API rate limit
* @param options - Optional configuration
* @returns Promise resolving to User object or null
*/
function currentUser(options?: CurrentUserOptions): Promise<User | null>;
interface CurrentUserOptions {
/**
* If true, treats pending sessions as signed out
* @default false
*/
treatPendingAsSignedOut?: boolean;
}
interface User {
/**
* User's unique identifier
*/
id: string;
/**
* User's email addresses
*/
emailAddresses: EmailAddress[];
/**
* User's phone numbers
*/
phoneNumbers: PhoneNumber[];
/**
* User's external OAuth accounts
*/
externalAccounts: ExternalAccount[];
/**
* User's web3 wallets
*/
web3Wallets: Web3Wallet[];
/**
* User's passkeys
*/
passkeys: Passkey[];
/**
* User's first name
*/
firstName: string | null;
/**
* User's last name
*/
lastName: string | null;
/**
* User's username
*/
username: string | null;
/**
* User's profile image URL
*/
imageUrl: string;
/**
* Whether user has uploaded a profile image
*/
hasImage: boolean;
/**
* User's primary email address ID
*/
primaryEmailAddressId: string | null;
/**
* User's primary phone number ID
*/
primaryPhoneNumberId: string | null;
/**
* User's primary web3 wallet ID
*/
primaryWeb3WalletId: string | null;
/**
* User's public metadata (readable by anyone)
*/
publicMetadata: Record<string, any>;
/**
* User's private metadata (readable only by backend)
*/
privateMetadata: Record<string, any>;
/**
* User's unsafe metadata (readable/writable by client)
*/
unsafeMetadata: Record<string, any>;
/**
* Whether user is banned
*/
banned: boolean;
/**
* Whether user is locked
*/
locked: boolean;
/**
* Timestamp when user was created
*/
createdAt: number;
/**
* Timestamp when user was last updated
*/
updatedAt: number;
/**
* Timestamp of last sign-in
*/
lastSignInAt: number | null;
/**
* Whether two-factor authentication is enabled
*/
twoFactorEnabled: boolean;
/**
* Whether backup codes are enabled
*/
backupCodeEnabled: boolean;
/**
* Password digest (hashed password)
*/
passwordEnabled: boolean;
/**
* Legal accepted status
*/
legalAcceptedAt: number | null;
/**
* Create email address verification
*/
createEmailAddress: (params: CreateEmailAddressParams) => Promise<EmailAddress>;
/**
* Get OAuth access token
*/
getOAuthAccessToken: (provider: string) => Promise<OAuthAccessToken[]>;
}Usage Example - Server Component:
import { currentUser } from '@clerk/nextjs/server';
export default async function ProfilePage() {
const user = await currentUser();
if (!user) {
return <div>Not signed in</div>;
}
return (
<div>
<h1>{user.firstName} {user.lastName}</h1>
<p>Email: {user.emailAddresses[0]?.emailAddress}</p>
<p>Username: {user.username}</p>
<img src={user.imageUrl} alt="Profile" />
<p>Member since: {new Date(user.createdAt).toLocaleDateString()}</p>
</div>
);
}Usage Example - Route Handler:
import { currentUser } from '@clerk/nextjs/server';
import { NextResponse } from 'next/server';
export async function GET() {
const user = await currentUser();
if (!user) {
return NextResponse.json({ error: 'Unauthorized' }, { status: 401 });
}
return NextResponse.json({
id: user.id,
email: user.emailAddresses[0]?.emailAddress,
name: `${user.firstName} ${user.lastName}`,
imageUrl: user.imageUrl,
});
}Usage Example - Access Metadata:
import { currentUser } from '@clerk/nextjs/server';
export default async function DashboardPage() {
const user = await currentUser();
if (!user) {
return <div>Not signed in</div>;
}
// Access different metadata types
const preferences = user.publicMetadata.preferences;
const subscription = user.publicMetadata.subscription;
const customData = user.unsafeMetadata.customData;
return (
<div>
<h1>Dashboard</h1>
<p>Subscription: {subscription?.tier}</p>
<p>Theme: {preferences?.theme}</p>
</div>
);
}Usage Example - Server Action:
'use server';
import { currentUser } from '@clerk/nextjs/server';
export async function getUserProfile() {
const user = await currentUser();
if (!user) {
throw new Error('Not authenticated');
}
return {
id: user.id,
email: user.emailAddresses[0]?.emailAddress,
fullName: `${user.firstName} ${user.lastName}`,
imageUrl: user.imageUrl,
metadata: user.publicMetadata,
};
}Performance Note:
currentUser() uses fetch() internally, which means:
currentUser() in the same request only make one API callauth() if you only need userId/sessionId (lighter weight)Utilities for triggering reverification flows in Route Handlers and Server Actions.
/**
* Returns a reverification error response
* Use in Route Handlers to trigger reverification flow
* Returns 403 response with reverification metadata
* @param missingConfig - Optional reverification configuration
* @returns Response object with 403 status
*/
function reverificationErrorResponse<MC extends ReverificationConfig>(
missingConfig?: MC
): Response;
/**
* Throws a reverification error
* Use in Server Actions to trigger reverification flow
* Returns error object with reverification metadata
* @param missingConfig - Optional reverification configuration
* @returns Reverification error object
*/
function reverificationError<MC extends ReverificationConfig>(
missingConfig?: MC
): ReverificationError<{ metadata?: { reverification?: MC } }>;
interface ReverificationConfig {
/**
* Maximum age in seconds for reverification
*/
maxAgeInSeconds?: number;
/**
* Additional reverification level
*/
level?: string;
}Usage Example - Route Handler:
import { auth, reverificationErrorResponse } from '@clerk/nextjs/server';
import { NextResponse } from 'next/server';
export async function DELETE(req: Request) {
const { userId, sessionId } = await auth();
if (!userId) {
return NextResponse.json({ error: 'Unauthorized' }, { status: 401 });
}
// Check if action requires reverification
const requiresReverification = true; // Your logic here
if (requiresReverification) {
// Return reverification error response
return reverificationErrorResponse({
maxAgeInSeconds: 300, // Require reverification within 5 minutes
level: 'strict',
});
}
// Perform sensitive operation
await deleteSensitiveData(userId);
return NextResponse.json({ success: true });
}Usage Example - Server Action:
'use server';
import { auth, reverificationError } from '@clerk/nextjs/server';
export async function deleteSensitiveData() {
const { userId } = await auth();
if (!userId) {
throw new Error('Unauthorized');
}
// Check if action requires reverification
const requiresReverification = true; // Your logic here
if (requiresReverification) {
// Throw reverification error
throw reverificationError({
maxAgeInSeconds: 300,
level: 'strict',
});
}
// Perform sensitive operation
await performDeletion(userId);
return { success: true };
}All App Router server authentication functions require:
// middleware.ts
import { clerkMiddleware } from '@clerk/nextjs/server';
export default clerkMiddleware();
export const config = {
matcher: [
'/((?!_next|[^?]*\\.(?:html?|css|js(?!on)|jpe?g|webp|png|gif|svg|ttf|woff2?|ico|csv|docx?|xlsx?|zip|webmanifest)).*)',
'/(api|trpc)(.*)',
],
};