User Impersonation SDK
The Impersonation SDK allows administrators to view data as different users, anonymous visitors, or with service-level permissions. This is invaluable for debugging issues, testing RLS policies, and providing customer support.
Overview
Section titled “Overview”User impersonation enables you to:
- Debug user-reported issues by seeing data exactly as they see it
- Test Row Level Security (RLS) policies with different user roles
- Verify anonymous access to public data
- Perform administrative operations with service-level permissions
- Maintain a complete audit trail of all impersonation sessions
All impersonation sessions are logged for security and compliance.
Basic Usage
Section titled “Basic Usage”import { createClient } from '@fluxbase/sdk'
const client = createClient( 'http://localhost:8080', 'your-service-role-key')
// Login as admin firstawait client.admin.login({ email: 'admin@example.com', password: 'password'})
// Access impersonation managerconst impersonation = client.admin.impersonationImpersonation Types
Section titled “Impersonation Types”1. User Impersonation
Section titled “1. User Impersonation”Impersonate a specific user to see data exactly as they would see it.
const { session, target_user, access_token } = await impersonation.impersonateUser({ target_user_id: 'user-uuid', reason: 'Support ticket #1234 - user reports missing data'})
console.log('Impersonating:', target_user.email)console.log('Session ID:', session.id)Use cases:
- Debugging user-reported data issues
- Verifying RLS policies for specific users
- Customer support investigations
2. Anonymous Impersonation
Section titled “2. Anonymous Impersonation”See data as an unauthenticated visitor would see it.
await impersonation.impersonateAnon({ reason: 'Testing public data access for blog posts'})
// Now all queries will use anonymous permissionsconst publicData = await client.from('posts').select('*').execute()console.log('Public posts:', publicData.data.length)Use cases:
- Testing public data access
- Verifying anon-level RLS policies
- Ensuring sensitive data is protected from public access
3. Service Role Impersonation
Section titled “3. Service Role Impersonation”View data with service-level permissions that may bypass RLS policies.
await impersonation.impersonateService({ reason: 'Administrative data cleanup'})
// Now all queries will use service role permissionsconst allRecords = await client.from('sensitive_data').select('*').execute()Use cases:
- Administrative queries
- Testing privileged operations
- Bypassing RLS for data management
API Reference
Section titled “API Reference”impersonateUser()
Section titled “impersonateUser()”Start an impersonation session as a specific user.
const response = await impersonation.impersonateUser({ target_user_id: string, reason: string})Parameters:
target_user_id(string): UUID of the user to impersonatereason(string): Required explanation for audit trail
Returns:
{ session: { id: string admin_user_id: string target_user_id: string impersonation_type: 'user' target_role: string reason: string started_at: string ended_at: string | null is_active: boolean ip_address: string | null user_agent: string | null }, target_user: { id: string email: string role: string }, access_token: string refresh_token: string expires_in: number}Errors:
User not found- Target user doesn’t existCannot impersonate yourself- Trying to impersonate own accountUnauthorized- Not logged in as admin
impersonateAnon()
Section titled “impersonateAnon()”Start an impersonation session as anonymous user.
const response = await impersonation.impersonateAnon({ reason: string})Parameters:
reason(string): Required explanation for audit trail
Returns: Same structure as impersonateUser() but with target_user: null
impersonateService()
Section titled “impersonateService()”Start an impersonation session with service role.
const response = await impersonation.impersonateService({ reason: string})Parameters:
reason(string): Required explanation for audit trail
Returns: Same structure as impersonateUser() but with target_user: null
stop()
Section titled “stop()”End the current impersonation session.
const response = await impersonation.stop()Returns:
{ success: boolean message: string}getCurrent()
Section titled “getCurrent()”Get information about the active impersonation session.
const current = await impersonation.getCurrent()Returns:
{ session: ImpersonationSession | null target_user: ImpersonationTargetUser | null}Example:
const { session, target_user } = await impersonation.getCurrent()
if (session) { console.log('Currently impersonating:', target_user?.email) console.log('Reason:', session.reason) console.log('Started:', session.started_at)} else { console.log('No active impersonation')}listSessions()
Section titled “listSessions()”List impersonation sessions for audit and compliance.
const sessions = await impersonation.listSessions(options?)Parameters:
{ limit?: number // Max sessions to return offset?: number // Pagination offset admin_user_id?: string // Filter by admin target_user_id?: string // Filter by impersonated user impersonation_type?: 'user' | 'anon' | 'service' is_active?: boolean // Filter by active status}Returns:
{ sessions: ImpersonationSession[] total: number}Common Use Cases
Section titled “Common Use Cases”1. Debugging User Issues
Section titled “1. Debugging User Issues”async function debugUserIssue(userId: string, ticketId: string) { // Start impersonation const { target_user } = await client.admin.impersonation.impersonateUser({ target_user_id: userId, reason: `Support ticket #${ticketId} - investigating data access issue` })
console.log(`Impersonating: ${target_user.email}`)
// Query data as the user would see it const { data, error } = await client .from('user_documents') .select('*') .execute()
console.log('User can see documents:', data?.length)
// Stop impersonation await client.admin.impersonation.stop()}2. Testing RLS Policies
Section titled “2. Testing RLS Policies”async function testRLSPolicy() { // Test as regular user await client.admin.impersonation.impersonateUser({ target_user_id: 'regular-user-id', reason: 'Testing RLS policy for regular users' })
const regularUserData = await client.from('posts').select('*').execute() console.log('Regular user sees:', regularUserData.data?.length)
// Stop and test as admin await client.admin.impersonation.stop()
// Test as service role await client.admin.impersonation.impersonateService({ reason: 'Testing RLS policy bypass with service role' })
const serviceData = await client.from('posts').select('*').execute() console.log('Service role sees:', serviceData.data?.length)
// Cleanup await client.admin.impersonation.stop()}3. Verifying Public Access
Section titled “3. Verifying Public Access”async function verifyPublicAccess() { // Impersonate anonymous user await client.admin.impersonation.impersonateAnon({ reason: 'Verifying public blog posts are accessible' })
// Query as anonymous user const { data } = await client .from('blog_posts') .select('*') .eq('status', 'published') .execute()
console.log('Public can see posts:', data?.length)
// Verify private posts are hidden const privateQuery = await client .from('blog_posts') .select('*') .eq('status', 'draft') .execute()
if (privateQuery.data?.length === 0) { console.log('✓ Private posts correctly hidden from public') } else { console.error('✗ Security issue: Private posts visible to public!') }
await client.admin.impersonation.stop()}4. Audit Trail Review
Section titled “4. Audit Trail Review”async function reviewImpersonationActivity() { // Get all sessions from last 7 days const { sessions, total } = await client.admin.impersonation.listSessions({ limit: 100, offset: 0 })
console.log(`Total sessions: ${total}`)
sessions.forEach(session => { const duration = session.ended_at ? new Date(session.ended_at).getTime() - new Date(session.started_at).getTime() : 'ongoing'
console.log(` Admin: ${session.admin_user_id} Type: ${session.impersonation_type} Reason: ${session.reason} Duration: ${duration}ms Started: ${session.started_at} `) })}5. Customer Support Workflow
Section titled “5. Customer Support Workflow”async function supportWorkflow(userEmail: string, issue: string) { try { // Find user const { users } = await client.admin.listUsers({ search: userEmail, limit: 1 })
if (users.length === 0) { throw new Error(`User ${userEmail} not found`) }
const user = users[0]
// Start impersonation console.log(`Starting impersonation of ${user.email}`) await client.admin.impersonation.impersonateUser({ target_user_id: user.id, reason: `Customer support: ${issue}` })
// Investigate the issue // ... perform debugging queries ...
// Take screenshots or gather data const userView = await client.from('dashboard').select('*').execute() console.log('User dashboard data:', userView.data)
} catch (error) { console.error('Support workflow error:', error) } finally { // Always stop impersonation await client.admin.impersonation.stop() console.log('Impersonation ended') }}6. Multi-Scenario Testing
Section titled “6. Multi-Scenario Testing”async function testMultipleScenarios() { const scenarios = [ { type: 'user' as const, userId: 'premium-user-id', reason: 'Testing premium user features' }, { type: 'user' as const, userId: 'free-user-id', reason: 'Testing free user limitations' }, { type: 'anon' as const, reason: 'Testing public access' } ]
for (const scenario of scenarios) { console.log(`\n--- Testing: ${scenario.reason} ---`)
if (scenario.type === 'user') { await client.admin.impersonation.impersonateUser({ target_user_id: scenario.userId!, reason: scenario.reason }) } else { await client.admin.impersonation.impersonateAnon({ reason: scenario.reason }) }
// Run tests const features = await client.from('available_features').select('*').execute() console.log('Available features:', features.data?.map(f => f.name))
// Stop before next scenario await client.admin.impersonation.stop() }}7. Finding Who Impersonated a User
Section titled “7. Finding Who Impersonated a User”async function findImpersonationHistory(userId: string) { const { sessions } = await client.admin.impersonation.listSessions({ target_user_id: userId })
console.log(`Impersonation history for user ${userId}:`)
sessions.forEach(session => { console.log(` Admin: ${session.admin_user_id} Reason: ${session.reason} Started: ${session.started_at} Ended: ${session.ended_at || 'Still active'} IP: ${session.ip_address} `) })}Error Handling
Section titled “Error Handling”try { await client.admin.impersonation.impersonateUser({ target_user_id: 'user-id', reason: 'Support investigation' })
// Perform operations
} catch (error) { if (error.message.includes('not found')) { console.error('User does not exist') } else if (error.message.includes('yourself')) { console.error('Cannot impersonate your own account') } else if (error.message.includes('Unauthorized')) { console.error('Must be logged in as admin') } else { console.error('Impersonation error:', error) }} finally { // Always stop impersonation in cleanup try { await client.admin.impersonation.stop() } catch { // Ignore if no active session }}Security & Best Practices
Section titled “Security & Best Practices”1. Always Provide Clear Reasons
Section titled “1. Always Provide Clear Reasons”// ✓ Good - Clear and specificawait impersonation.impersonateUser({ target_user_id: 'user-123', reason: 'Support ticket #5678 - user reports missing invoices'})
// ✗ Bad - Vague reasonawait impersonation.impersonateUser({ target_user_id: 'user-123', reason: 'testing'})2. Stop Impersonation When Done
Section titled “2. Stop Impersonation When Done”Always stop impersonation sessions to:
- Clear the audit trail properly
- Avoid confusion
- Prevent accidental data modifications
// Use try/finally to ensure cleanuptry { await impersonation.impersonateUser({ target_user_id: 'user-123', reason: 'Debugging data access' })
// Do work...
} finally { await impersonation.stop()}3. Review Audit Logs Regularly
Section titled “3. Review Audit Logs Regularly”// Monitor impersonation usageasync function monitorUsage() { const { sessions } = await client.admin.impersonation.listSessions({ is_active: false, limit: 50 })
// Check for suspicious patterns const byAdmin = new Map() sessions.forEach(s => { byAdmin.set(s.admin_user_id, (byAdmin.get(s.admin_user_id) || 0) + 1) })
byAdmin.forEach((count, admin) => { if (count > 20) { console.warn(`Admin ${admin} has ${count} impersonation sessions`) } })}4. Limit Impersonation Duration
Section titled “4. Limit Impersonation Duration”async function timedImpersonation(userId: string, maxMinutes: number = 15) { await client.admin.impersonation.impersonateUser({ target_user_id: userId, reason: 'Timed support session' })
// Auto-stop after timeout setTimeout(async () => { await client.admin.impersonation.stop() console.log('Impersonation auto-ended after timeout') }, maxMinutes * 60 * 1000)}5. Prevent Self-Impersonation
Section titled “5. Prevent Self-Impersonation”The SDK automatically prevents admins from impersonating themselves:
// This will throw an errorawait client.admin.impersonation.impersonateUser({ target_user_id: currentAdmin.id, // Your own ID reason: 'Testing'})// Error: Cannot impersonate yourselfIntegration with RLS
Section titled “Integration with RLS”When impersonating, all database queries respect Row Level Security policies:
-- RLS policy exampleCREATE POLICY "Users can only see their own data"ON user_documentsFOR SELECTUSING (user_id = current_setting('app.user_id')::uuid);When you impersonate a user, the app.user_id session variable is set to their ID, so the RLS policy works correctly.
Type Definitions
Section titled “Type Definitions”interface ImpersonationSession { id: string admin_user_id: string target_user_id: string | null impersonation_type: 'user' | 'anon' | 'service' target_role: string reason: string started_at: string ended_at: string | null is_active: boolean ip_address: string | null user_agent: string | null}
interface ImpersonationTargetUser { id: string email: string role: string}
interface StartImpersonationResponse { session: ImpersonationSession target_user: ImpersonationTargetUser | null access_token: string refresh_token: string expires_in: number}
interface StopImpersonationResponse { success: boolean message: string}
interface ListImpersonationSessionsResponse { sessions: ImpersonationSession[] total: number}Related Resources
Section titled “Related Resources”- User Management SDK - Manage users
- Authentication Guide - Learn about authentication
- Row Level Security - Configure RLS policies
- Admin Guide - Dashboard impersonation guide