Skip to content

User Impersonation

Impersonation allows admins or support to sign in as other users for debugging, customer support, or demonstrations.

Enable impersonation in your createAuth configuration:

auth.ts
export const auth = createAuth({
adapter,
providers,
roles: {
adminRoles: ['admin'],
adminUserIds: ['special-admin-id'],
},
impersonation: {
enabled: true,
allowedRoles: ['admin', 'support'],
cannotImpersonate: ['admin'],
maxTTL: 3600,
onImpersonate: ({ adminUserId, targetUserId, reason, timestamp }) => {
console.log(`Audit: ${adminUserId} impersonated ${targetUserId}: ${reason}`)
},
},
})
  • enabled - Set to true to enable the impersonation feature
  • allowedRoles - Array of roles that can impersonate others (defaults to adminRoles)
  • cannotImpersonate - Array of roles that cannot be impersonated (defaults to adminRoles)
  • maxTTL - Maximum duration of an impersonation session in seconds (default: 3600)
  • onImpersonate - Hook called when impersonation starts

Use the startImpersonation method to begin impersonating a user. reason is optional, it’s passed to the onImpersonate hook for auditing purposes, you can also override the default ttl here.

routes/api/admin/impersonate/+server.ts
import { auth } from '$lib/server/auth'
export async function POST({ request, locals }) {
const session = await locals.getSession()
if (!session?.user?.isAdmin)
return new Response('Forbidden', { status: 403 })
const { targetUserId, reason } = await request.json()
const result = await auth.startImpersonation(session.user.id, targetUserId, {
reason,
ttl: 60 * 30,
})
if (!result)
return new Response('Impersonation failed', { status: 500 })
return new Response(JSON.stringify({ success: true }), {
headers: [
['Set-Cookie', result.cookie],
['Set-Cookie', result.originalCookie],
],
})
}
  • token - The impersonation session JWT containing impersonatedBy and impersonationExpiresAt claims
  • cookie - Set-Cookie header for the impersonation session
  • originalCookie - Set-Cookie header to stash the admin’s session info
  • maxAge - The actual TTL used (capped by maxTTL)

Check if the current session is an impersonation session:

routes/+layout.server.ts
import { isImpersonating } from '@rttnd/gau'
export async function load({ locals }) {
const session = await locals.getSession()
return {
user: session?.user,
isImpersonating: isImpersonating(session?.session),
impersonatedBy: session?.session?.impersonatedBy,
}
}

Use this to show a visual indicator in your UI that the user is in impersonation mode.

Restore the admin’s original session when they’re done:

routes/api/admin/unimpersonate/+server.ts
import { auth } from '$lib/server/auth'
export async function POST({ request }) {
const result = await auth.endImpersonation(request)
if (!result)
return new Response('No active impersonation', { status: 400 })
const headers = new Headers()
headers.set('Set-Cookie', result.cookie)
result.clearCookies.forEach(cookie => headers.append('Set-Cookie', cookie))
return new Response(JSON.stringify({ success: true }), { headers })
}
  • token - The restored admin session JWT
  • cookie - Set-Cookie header for the restored session
  • clearCookies - Array of Set-Cookie headers to clear the stash cookie
  • Log all impersonation events via the onImpersonate hook
  • Set a reasonable maxTTL
  • Display a clear visual indicator when impersonation is active