Skip to main content

Overview

User impersonation allows administrators to temporarily log in as another user without knowing their password. This is particularly useful for:
  • Providing customer support
  • Testing user-specific issues
  • Debugging permission problems
  • Quality assurance testing
User impersonation is a powerful feature that should only be available to trusted administrators. It provides full access to the impersonated user’s account.

How It Works

The impersonation system uses AdonisJS sessions to store the original user’s ID before switching to the target user. This allows administrators to seamlessly switch back to their own account.

Authorization Policy

Impersonation is controlled by a dedicated policy that ensures only admins can impersonate users, and prevents self-impersonation:
app/users/policies/impersonate_policy.ts
import { BasePolicy } from '@adonisjs/bouncer'
import { AuthorizerResponse } from '@adonisjs/bouncer/types'
import User from '#users/models/user'

export default class ImpersonatePolicy extends BasePolicy {
  create(currentUser: User, user: User): AuthorizerResponse {
    return currentUser.isAdmin && currentUser.id !== user.id
  }
}
The policy checks two conditions:
  1. The current user must be an admin (currentUser.isAdmin)
  2. The admin cannot impersonate themselves (currentUser.id !== user.id)

Implementation

Controller

The impersonation controller handles the session management and authentication:
app/users/controllers/impersonates_controller.ts
import type { HttpContext } from '@adonisjs/core/http'
import { afterAuthRedirectRoute } from '#config/auth'
import User from '#users/models/user'
import ImpersonatePolicy from '#users/policies/impersonate_policy'

export default class ImpersonatesController {
  async store({ session, bouncer, params, response, auth }: HttpContext) {
    const impersonatedUser = await User.findOrFail(params.id)

    // Check if the current user has permission to impersonate
    await bouncer.with(ImpersonatePolicy).authorize('create', impersonatedUser)

    // Store the original user ID in the session
    session.put('originalUserId', auth.user!.id)
    
    // Log in as the target user
    await auth.use('web').login(impersonatedUser)

    return response.redirect().toRoute(afterAuthRedirectRoute)
  }
}

Route Setup

Add the impersonation route to your routes file:
app/users/routes.ts
import { middleware } from '#start/kernel'
import router from '@adonisjs/core/services/router'

const ImpersonatesController = () => import('#users/controllers/impersonates_controller')

router
  .post('/users/impersonate/:id', [ImpersonatesController, 'store'])
  .middleware(middleware.auth())

Registering the Policy

Register the impersonation policy in your policies configuration:
app/core/policies/main.ts
export const policies = {
  UserPolicy: () => import('#users/policies/user_policy'),
  ImpersonatePolicy: () => import('#users/policies/impersonate_policy'),
  TokenPolicy: () => import('#users/policies/token_policy'),
}

Using Impersonation

Starting an Impersonation Session

To impersonate a user, make a POST request to the impersonation endpoint:
// Using Inertia.js
import { router } from '@inertiajs/react'

const impersonateUser = (userId: number) => {
  router.post(`/users/impersonate/${userId}`)
}

Ending an Impersonation Session

To stop impersonating and return to your original account, implement a stop impersonation endpoint:
export default class ImpersonatesController {
  async destroy({ session, auth, response }: HttpContext) {
    const originalUserId = session.get('originalUserId')
    
    if (originalUserId) {
      const originalUser = await User.findOrFail(originalUserId)
      await auth.use('web').login(originalUser)
      session.forget('originalUserId')
    }
    
    return response.redirect().toRoute(afterAuthRedirectRoute)
  }
}

Security Considerations

Audit Logging

Consider logging all impersonation events for security audits and compliance.

Time Limits

Implement session timeouts for impersonation to automatically end sessions after a period.

Notifications

Optionally notify users when their account has been accessed through impersonation.

Permission Checks

Always verify admin status server-side, never trust client-side checks alone.

UI Integration

The starter kit includes a React dialog component for triggering impersonation:
// Example UI button
<Button 
  onClick={() => impersonateUser(user.id)}
  disabled={!canImpersonate}
>
  Impersonate User
</Button>
The impersonation feature is already implemented in the starter kit’s user management interface. Admins will see an “Impersonate” option in the user list.

Next Steps

Authorization

Learn more about the authorization system and policies

User Management

Explore the complete user management features

Build docs developers (and LLMs) love