Server-side authentication utilities for Next.js Pages Router. These functions provide access to authentication state in getServerSideProps and API routes.
Required Setup:
CLERK_SECRET_KEY environment variable requiredpages/_app.tsxbuildClerkProps() required in getServerSideProps for SSR auth stategetAuth() is synchronous (no await needed)Default Behaviors:
getAuth() is synchronous (returns immediately, no Promise)getAuth() returns userId as null when signed out (not undefined)buildClerkProps() must be spread into page props for ClerkProvidertreatPendingAsSignedOut: false by defaultacceptsToken: 'session_token' by defaultgetToken() is async (returns Promise) even though getAuth() is synchas() function is synchronous (returns boolean immediately)Threading Model:
getAuth() executes synchronously in Node.js server runtimegetToken() is async and must be awaitedbuildClerkProps() is synchronous (returns object immediately)getAuth() calls in same request return same auth stateclerkClient() calls are async and must be awaitedLifecycle:
getAuth() reads session from request object (NextApiRequest)buildClerkProps() serializes auth state for client-side hydrationbuildClerkProps() must be passed to ClerkProvider via pagePropsEdge Cases:
getAuth() in API routes: Pass req parametergetAuth() in getServerSideProps: Pass ctx.req parameterbuildClerkProps() must be called in getServerSideProps, not API routesbuildClerkProps() causes client-side auth state mismatchgetToken() can return null if session invalid or expiredhas() returns false if no active organization (even with permission)treatPendingAsSignedOut: true to treat as signed outExceptions:
CLERK_SECRET_KEY causes getAuth() to throw errorgetAuth() to failgetToken() throws if Clerk not initialized or session invalidbuildClerkProps() throws if request object invalidReturns the Auth object from the request. Synchronous function for Pages Router server contexts.
/**
* Retrieves authentication state from request object
* Available in getServerSideProps and API routes
* Synchronous operation - does not return a Promise
* @param req - Next.js API request object
* @param options - Optional configuration
* @returns SessionAuthObject with authentication state
*/
function getAuth(
req: NextApiRequest,
options?: GetAuthOptions
): SessionAuthObject;
interface GetAuthOptions {
/**
* Optional secret key override
* If not provided, uses CLERK_SECRET_KEY environment variable
*/
secretKey?: string;
/**
* If true, treats pending sessions as signed out
* @default false
*/
treatPendingAsSignedOut?: boolean;
/**
* Token type to accept for authentication
* @default 'session_token'
*/
acceptsToken?: 'session_token' | 'm2m_token' | 'oauth_token' | 'any';
}
interface SessionAuthObject {
/**
* 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;
/**
* Additional debug information
*/
debug: () => any;
}
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;
}Usage Example - API Route:
import { getAuth } from '@clerk/nextjs/server';
import type { NextApiRequest, NextApiResponse } from 'next';
export default function handler(req: NextApiRequest, res: NextApiResponse) {
const { userId } = getAuth(req);
if (!userId) {
return res.status(401).json({ error: 'Unauthorized' });
}
return res.status(200).json({ userId });
}Usage Example - Get Token:
import { getAuth } from '@clerk/nextjs/server';
import type { NextApiRequest, NextApiResponse } from 'next';
export default async function handler(
req: NextApiRequest,
res: NextApiResponse
) {
const { userId, getToken } = getAuth(req);
if (!userId) {
return res.status(401).json({ error: 'Unauthorized' });
}
// 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}`,
},
});
const data = await response.json();
return res.status(200).json(data);
}Usage Example - Check Permissions:
import { getAuth } from '@clerk/nextjs/server';
import type { NextApiRequest, NextApiResponse } from 'next';
export default function handler(req: NextApiRequest, res: NextApiResponse) {
const { userId, has, orgRole } = getAuth(req);
if (!userId) {
return res.status(401).json({ error: 'Unauthorized' });
}
const isAdmin = has({ role: 'admin' });
const canManageTeam = has({ permission: 'org:team:manage' });
if (!isAdmin) {
return res.status(403).json({ error: 'Forbidden' });
}
return res.status(200).json({
userId,
orgRole,
isAdmin,
canManageTeam,
});
}Usage Example - getServerSideProps:
import { getAuth } from '@clerk/nextjs/server';
import type { GetServerSideProps } from 'next';
export const getServerSideProps: GetServerSideProps = async (ctx) => {
const { userId, sessionId } = getAuth(ctx.req);
if (!userId) {
return {
redirect: {
destination: '/sign-in',
permanent: false,
},
};
}
// Fetch user-specific data
const userData = await fetchUserData(userId);
return {
props: {
userId,
sessionId,
userData,
},
};
};
export default function ProtectedPage({ userId, userData }: any) {
return (
<div>
<h1>Protected Page</h1>
<p>User ID: {userId}</p>
<pre>{JSON.stringify(userData, null, 2)}</pre>
</div>
);
}Usage Example - Organization Context:
import { getAuth } from '@clerk/nextjs/server';
import type { NextApiRequest, NextApiResponse } from 'next';
export default function handler(req: NextApiRequest, res: NextApiResponse) {
const { userId, orgId, orgRole, orgSlug, orgPermissions } = getAuth(req);
if (!userId) {
return res.status(401).json({ error: 'Unauthorized' });
}
if (!orgId) {
return res.status(400).json({ error: 'No active organization' });
}
return res.status(200).json({
userId,
organization: {
id: orgId,
role: orgRole,
slug: orgSlug,
permissions: orgPermissions,
},
});
}Builds Clerk props for server-side rendering in getServerSideProps. Used to pass authentication state to client components.
/**
* Builds Clerk props for SSR in getServerSideProps
* Informs client-side helpers of authentication state
* @param req - Next.js API request object
* @param authState - Optional initial auth state
* @returns Object with __clerk_ssr_state for ClerkProvider
*/
function buildClerkProps(
req: NextApiRequest,
authState?: BuildClerkPropsInitState
): Record<string, unknown>;
interface BuildClerkPropsInitState {
/**
* Optional user object to include in SSR state
*/
user?: User | null;
/**
* Optional session object to include in SSR state
*/
session?: Session | null;
/**
* Optional organization object to include in SSR state
*/
organization?: Organization | null;
}Usage Example - Basic:
import { getAuth, buildClerkProps } from '@clerk/nextjs/server';
import type { GetServerSideProps } from 'next';
export const getServerSideProps: GetServerSideProps = async (ctx) => {
const { userId } = getAuth(ctx.req);
if (!userId) {
return {
redirect: {
destination: '/sign-in',
permanent: false,
},
};
}
// Build Clerk props for client-side hydration
return {
props: {
...buildClerkProps(ctx.req),
},
};
};
export default function Page() {
return <div>Protected content</div>;
}Usage Example - With User Data:
import { getAuth, buildClerkProps, clerkClient } from '@clerk/nextjs/server';
import type { GetServerSideProps } from 'next';
export const getServerSideProps: GetServerSideProps = async (ctx) => {
const { userId } = getAuth(ctx.req);
if (!userId) {
return {
redirect: {
destination: '/sign-in',
permanent: false,
},
};
}
// Fetch user from Clerk backend
const client = await clerkClient();
const user = await client.users.getUser(userId);
// Pass user to client-side
return {
props: {
...buildClerkProps(ctx.req, { user }),
},
};
};
export default function ProfilePage() {
return <div>Profile page with SSR user data</div>;
}Usage Example - With Session and Organization:
import { getAuth, buildClerkProps, clerkClient } from '@clerk/nextjs/server';
import type { GetServerSideProps } from 'next';
export const getServerSideProps: GetServerSideProps = async (ctx) => {
const { userId, sessionId, orgId } = getAuth(ctx.req);
if (!userId) {
return {
redirect: {
destination: '/sign-in',
permanent: false,
},
};
}
const client = await clerkClient();
// Fetch user, session, and organization
const user = await client.users.getUser(userId);
const session = sessionId
? await client.sessions.getSession(sessionId)
: null;
const organization = orgId
? await client.organizations.getOrganization({ organizationId: orgId })
: null;
return {
props: {
...buildClerkProps(ctx.req, { user, session, organization }),
},
};
};
export default function DashboardPage() {
return <div>Dashboard with full SSR state</div>;
}Usage Example - Combined with Custom Props:
import { getAuth, buildClerkProps } from '@clerk/nextjs/server';
import type { GetServerSideProps } from 'next';
export const getServerSideProps: GetServerSideProps = async (ctx) => {
const { userId } = getAuth(ctx.req);
if (!userId) {
return {
redirect: {
destination: '/sign-in',
permanent: false,
},
};
}
// Fetch additional data
const posts = await fetchUserPosts(userId);
const settings = await fetchUserSettings(userId);
return {
props: {
...buildClerkProps(ctx.req),
posts,
settings,
},
};
};
interface PageProps {
posts: Post[];
settings: Settings;
}
export default function UserDashboard({ posts, settings }: PageProps) {
return (
<div>
<h1>Dashboard</h1>
<PostList posts={posts} />
<SettingsPanel settings={settings} />
</div>
);
}For Pages Router, buildClerkProps must be passed to ClerkProvider in _app.tsx:
/**
* Pages Router ClerkProvider setup
* Spread pageProps to pass buildClerkProps data
*/
// pages/_app.tsx
import { ClerkProvider } from '@clerk/nextjs';
import type { AppProps } from 'next/app';
export default function App({ Component, pageProps }: AppProps) {
return (
<ClerkProvider {...pageProps}>
<Component {...pageProps} />
</ClerkProvider>
);
}Combine getAuth() with clerkClient() to access Clerk's Backend API:
/**
* Using getAuth() with clerkClient()
* Access Backend API for user/organization management
*/
import { getAuth, clerkClient } from '@clerk/nextjs/server';
import type { NextApiRequest, NextApiResponse } from 'next';
export default async function handler(
req: NextApiRequest,
res: NextApiResponse
) {
const { userId } = getAuth(req);
if (!userId) {
return res.status(401).json({ error: 'Unauthorized' });
}
const client = await clerkClient();
// Get user details
const user = await client.users.getUser(userId);
// Update user metadata
await client.users.updateUserMetadata(userId, {
publicMetadata: {
lastApiAccess: new Date().toISOString(),
},
});
return res.status(200).json({
id: user.id,
email: user.emailAddresses[0]?.emailAddress,
metadata: user.publicMetadata,
});
}import { getAuth } from '@clerk/nextjs/server';
import type { NextApiRequest, NextApiResponse } from 'next';
export default function handler(req: NextApiRequest, res: NextApiResponse) {
const { userId } = getAuth(req);
if (!userId) {
return res.status(401).json({ error: 'Unauthorized' });
}
// Handle authenticated request
return res.status(200).json({ message: 'Success' });
}import { getAuth } from '@clerk/nextjs/server';
import type { NextApiRequest, NextApiResponse } from 'next';
export default function handler(req: NextApiRequest, res: NextApiResponse) {
const { userId, has } = getAuth(req);
if (!userId) {
return res.status(401).json({ error: 'Unauthorized' });
}
if (!has({ role: 'admin' })) {
return res.status(403).json({ error: 'Forbidden' });
}
// Handle admin request
return res.status(200).json({ message: 'Admin action completed' });
}import { getAuth } from '@clerk/nextjs/server';
import type { NextApiRequest, NextApiResponse } from 'next';
export default function handler(req: NextApiRequest, res: NextApiResponse) {
const { userId, orgId, orgRole } = getAuth(req);
if (!userId) {
return res.status(401).json({ error: 'Unauthorized' });
}
if (!orgId) {
return res.status(400).json({ error: 'Organization required' });
}
// Handle organization-scoped request
return res.status(200).json({
message: 'Success',
organizationId: orgId,
role: orgRole,
});
}import { getAuth } from '@clerk/nextjs/server';
import type { NextApiRequest, NextApiResponse } from 'next';
export default function handler(req: NextApiRequest, res: NextApiResponse) {
const { userId } = getAuth(req);
if (!userId) {
return res.status(401).json({ error: 'Unauthorized' });
}
switch (req.method) {
case 'GET':
return handleGet(req, res, userId);
case 'POST':
return handlePost(req, res, userId);
case 'DELETE':
return handleDelete(req, res, userId);
default:
return res.status(405).json({ error: 'Method not allowed' });
}
}
function handleGet(
req: NextApiRequest,
res: NextApiResponse,
userId: string
) {
// Handle GET request
return res.status(200).json({ userId });
}
function handlePost(
req: NextApiRequest,
res: NextApiResponse,
userId: string
) {
// Handle POST request
return res.status(201).json({ userId, created: true });
}
function handleDelete(
req: NextApiRequest,
res: NextApiResponse,
userId: string
) {
// Handle DELETE request
return res.status(200).json({ userId, deleted: true });
}import { getAuth } from '@clerk/nextjs/server';
import type { NextApiRequest, NextApiResponse } from 'next';
export default async function handler(
req: NextApiRequest,
res: NextApiResponse
) {
try {
const { userId, getToken } = getAuth(req);
if (!userId) {
return res.status(401).json({ error: 'Unauthorized' });
}
const token = await getToken();
if (!token) {
return res.status(401).json({ error: 'Invalid session' });
}
// Perform authenticated operations
const result = await performOperation(userId, token);
return res.status(200).json(result);
} catch (error) {
console.error('API error:', error);
return res.status(500).json({ error: 'Internal server error' });
}
}Pages Router authentication functions require:
// pages/_app.tsx
import { ClerkProvider } from '@clerk/nextjs';
import type { AppProps } from 'next/app';
export default function App({ Component, pageProps }: AppProps) {
return (
<ClerkProvider {...pageProps}>
<Component {...pageProps} />
</ClerkProvider>
);
}