Clerk session handling, JWT verification, token management, and multi-session workflows. Use when implementing session validation, JWT claims customization, token refresh patterns, session lifecycle management, or when user mentions session errors, authentication tokens, JWT verification, multi-device sessions, or session security.
Limited to specific tools
Additional assets for this skill
This skill is limited to using the following tools:
README.mdexamples/multi-session.tsxexamples/session-debugging.tsexamples/session-refresh.tsscripts/configure-sessions.shscripts/setup-jwt.shscripts/test-sessions.shtemplates/custom-claims.tstemplates/jwt-verification.tstemplates/session-config.tstemplates/session-types.tsPurpose: Autonomously configure, validate, and troubleshoot Clerk session handling, JWT verification, and token management.
Activation Triggers:
Key Resources:
scripts/configure-sessions.sh - Session configuration helperscripts/setup-jwt.sh - JWT template setup and validationscripts/test-sessions.sh - Session testing and verificationtemplates/session-config.ts - Session configuration patternstemplates/jwt-verification.ts - JWT verification middlewaretemplates/custom-claims.ts - Custom JWT claims setuptemplates/session-types.ts - TypeScript type definitionsexamples/multi-session.tsx - Multi-session managementexamples/session-refresh.ts - Session refresh patternsexamples/session-debugging.ts - Debugging utilities# Interactive session configuration
./scripts/configure-sessions.sh
# Options configured:
# - Session lifetime (default, maximum)
# - Multi-session mode (single, multi-device)
# - Refresh token strategy
# - Session activity tracking
# - Secure cookie settings
What it configures:
# Create/update JWT templates for custom claims
./scripts/setup-jwt.sh <template-name>
# Examples:
./scripts/setup-jwt.sh default # Standard user claims
./scripts/setup-jwt.sh hasura # Hasura integration claims
./scripts/setup-jwt.sh supabase # Supabase integration claims
./scripts/setup-jwt.sh custom # Custom business logic claims
Configures:
# Test session validation and JWT verification
./scripts/test-sessions.sh <test-type>
# Test types:
# - basic → Verify session creation and validation
# - jwt-verify → Test JWT signature verification
# - custom-claims → Validate custom claims presence
# - multi-session → Test multi-device session handling
# - refresh → Test token refresh flow
# - expiration → Test session expiration handling
Next.js App Router:
// Use auth() for session access
import { auth } from '@clerk/nextjs/server';
export async function GET() {
const { userId, sessionId, sessionClaims } = await auth();
if (!userId) {
return Response.json({ error: 'Unauthorized' }, { status: 401 });
}
// Access custom claims
const userRole = sessionClaims?.role;
const orgId = sessionClaims?.org_id;
return Response.json({ userId, role: userRole });
}
Middleware Pattern:
// See templates/jwt-verification.ts for complete implementation
import { clerkMiddleware, createRouteMatcher } from '@clerk/nextjs/server';
const isProtectedRoute = createRouteMatcher(['/dashboard(.*)']);
export default clerkMiddleware((auth, req) => {
if (isProtectedRoute(req)) {
auth().protect();
}
});
React/Next.js:
import { useAuth, useSession } from '@clerk/nextjs';
function Component() {
const { userId, sessionId } = useAuth();
const { session } = useSession();
// Session properties
const lastActiveAt = session?.lastActiveAt;
const expireAt = session?.expireAt;
// Session management
const handleRefresh = () => session?.touch(); // Extend session
return (
<div>
<p>Session ID: {sessionId}</p>
<p>Expires: {expireAt?.toLocaleString()}</p>
</div>
);
}
Enable multi-session mode:
// See examples/multi-session.tsx for complete implementation
import { useClerk } from '@clerk/nextjs';
function SessionSwitcher() {
const { client } = useClerk();
const sessions = client?.sessions || [];
// Switch between sessions
const switchSession = async (sessionId: string) => {
await client?.setActiveSession(sessionId);
};
// Sign out of specific session
const signOutSession = async (sessionId: string) => {
const session = client?.sessions.find(s => s.id === sessionId);
await session?.remove();
};
return (/* session switcher UI */);
}
Manual verification:
// See templates/jwt-verification.ts
import { verifyToken } from '@clerk/backend';
async function verifySessionToken(token: string) {
try {
const payload = await verifyToken(token, {
secretKey: process.env.CLERK_SECRET_KEY!,
// Optional: custom verification options
authorizedParties: ['https://app.example.com'],
});
return {
valid: true,
userId: payload.sub,
sessionId: payload.sid,
claims: payload,
};
} catch (error) {
return { valid: false, error: error.message };
}
}
Dashboard setup:
{
"metadata": "{{user.public_metadata}}",
"role": "{{user.public_metadata.role}}",
"org_id": "{{org.id}}",
"org_role": "{{org_membership.role}}",
"permissions": "{{org_membership.permissions}}"
}
Access in code:
// See templates/custom-claims.ts
import { auth } from '@clerk/nextjs/server';
const { sessionClaims } = await auth();
const role = sessionClaims?.role as string;
const orgId = sessionClaims?.org_id as string;
const permissions = sessionClaims?.permissions as string[];
Client-side auto-refresh:
// See examples/session-refresh.ts
import { useSession } from '@clerk/nextjs';
import { useEffect } from 'react';
function useSessionRefresh() {
const { session } = useSession();
useEffect(() => {
if (!session) return;
// Refresh session before expiration
const expiresAt = session.expireAt?.getTime() || 0;
const refreshAt = expiresAt - (5 * 60 * 1000); // 5 min before expiry
const now = Date.now();
if (refreshAt > now) {
const timeout = setTimeout(() => {
session.touch(); // Extends session
}, refreshAt - now);
return () => clearTimeout(timeout);
}
}, [session]);
}
import { useSession } from '@clerk/nextjs';
function Component() {
const { session } = useSession();
const extendSession = async () => {
// Touch session to extend lifetime
await session?.touch();
};
return <button onClick={extendSession}>Stay Logged In</button>;
}
Recommended settings:
Verification checklist:
exp claim)iss claim matches Clerk)aud if using multiple apps)Frontend:
Backend:
Problem: User logged out on page refresh
Solutions:
# Check cookie configuration
./scripts/test-sessions.sh basic
# Verify:
# - Domain settings match deployment URL
# - SameSite attribute compatible with architecture
# - Secure flag enabled in production only
Problem: verifyToken throws error
Diagnosis:
./scripts/test-sessions.sh jwt-verify
# Common causes:
# - Wrong CLERK_SECRET_KEY (check .env)
# - Token expired (check exp claim)
# - Issuer mismatch (check iss claim)
# - Invalid signature (token tampered)
Problem: Custom claims undefined in sessionClaims
Fix:
# Verify JWT template configuration
./scripts/setup-jwt.sh custom
# Steps:
# 1. Ensure template is set as default in Dashboard
# 2. User must sign out and sign in again
# 3. Check claim paths match metadata structure
Problem: Wrong session active after sign-in
Solutions:
// See examples/multi-session.tsx
// Force specific session active
await clerk.setActiveSession(sessionId);
// Or restrict to single session in Dashboard:
// Settings → Sessions → Multi-session handling → Single session
Scripts: All scripts in scripts/ directory handle:
Templates: templates/ contains production-ready code for:
Examples: examples/ demonstrates:
CRITICAL: This skill follows strict security rules:
.gitignore protection documented in all setup scriptsSupported Frameworks: Next.js (App Router, Pages Router), React, Express, Fastify, Remix Clerk SDK Version: @clerk/nextjs 5+, @clerk/backend 1+ Version: 1.0.0