Olympus Docs
CookbookTools

Admin impersonates a user (support flow)

Login as a customer to debug their issue

Support workflows often need: "I want to see what the customer sees." Done safely, this is an admin tool. Done wrong, it's a giant security hole.

Threat model

Risks:

  • Admin abuses the feature.
  • An XSS or compromised admin account gains user access broadly.
  • Audit trail is unclear who did what (was it actually the user or the admin?).

Mitigations:

  • Only specific admin role can impersonate.
  • Time-limited impersonation sessions.
  • Every impersonation logged.
  • User notified after the fact (optional).
  • Impersonated tokens are clearly tagged (so app shows a banner, audit logs distinguish).

Implementation in Olympus

Olympus doesn't have a built-in impersonate button. Two patterns:

Pattern A: Admin-issued session

Use Kratos admin API to create a session for the user's identity, then redirect to your app with that session cookie.

async function impersonate(identityId: string, adminId: string) {
  // Audit
  await db`
    INSERT INTO impersonation_log (admin_id, target_id, started_at, reason)
    VALUES (${adminId}, ${identityId}, NOW(), ${reason})
  `;
  
  // Create session via admin API
  const session = await fetch(`${KRATOS_ADMIN}/admin/identities/${identityId}/sessions`, {
    method: "POST",
    body: JSON.stringify({
      expires_in: "10m",
      authenticator_assurance_level: "aal1",
    }),
  }).then(r => r.json());
  
  // Return a short-lived cookie / token
  return session.session_token;
}

Limitations:

  • Kratos admin API doesn't directly issue impersonation sessions in a session-cookie-friendly format.
  • You'd build a bridge endpoint.

Pattern B: Token exchange with impersonation claim

Mint an OAuth2 token with an act (actor) claim per RFC 8693:

{
  "iss": "https://ciam.your-domain",
  "sub": "user-uuid",     // who we're acting as
  "act": {
    "sub": "admin-uuid"   // who's actually doing it
  },
  "scope": "...",
  "exp": ...
}

Hydra supports token exchange grant. Configure an OAuth2 client for "impersonation":

hydra create client \
  --name "Internal Impersonation" \
  --grant-types urn:ietf:params:oauth:grant-type:token-exchange \
  --scope "impersonate:user" \
  --token-endpoint-auth-method client_secret_basic

Admin app calls:

curl -X POST $HYDRA/oauth2/token \
  -u $CLIENT_ID:$CLIENT_SECRET \
  -d "grant_type=urn:ietf:params:oauth:grant-type:token-exchange" \
  -d "subject_token=$ADMIN_TOKEN" \
  -d "subject_token_type=urn:ietf:params:oauth:token-type:access_token" \
  -d "audience=https://your-api" \
  -d "requested_subject=user-uuid"

Gets back an access token where sub = user, act.sub = admin.

Your API decodes act claim and treats it as "impersonation in progress."

When act claim is present, show a banner:

{user.act && (
  <div className="bg-yellow-200 p-2 text-center">
    Acting as <strong>{user.email}</strong> on behalf of admin <strong>{user.act.email}</strong>
    <button onClick={endImpersonation}>End session</button>
  </div>
)}

Helps the admin remember they're in impersonation. Also visible if there's a screen-sharing session etc.

Audit log

Every impersonation must be logged:

CREATE TABLE impersonation_log (
  id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
  admin_id UUID NOT NULL,
  target_id UUID NOT NULL,
  reason TEXT,
  started_at TIMESTAMPTZ NOT NULL DEFAULT NOW(),
  ended_at TIMESTAMPTZ,
  ip TEXT,
  user_agent TEXT
);

Track every action under impersonation as the admin in audit logs, not as the user. E.g.:

event_type: "data.deleted"
actor: <admin-id>  (NOT <user-id>)
target: <user-id>
acted_as: <user-id>

This way, when looking at the user's history, you see "admin John deleted X while impersonating you."

Time limits

Impersonation sessions should expire fast:

  • 10 minutes default.
  • User must re-issue if needed longer.

Implementation: short exp claim, or session_token with short lifespan.

User notification

Some companies notify the user after each impersonation session:

Subject: Support session

Hi, a member of our support team accessed your account on May 5 at 3:21 PM
to investigate ticket #12345. The session lasted 4 minutes.

If you didn't expect this contact, please reply or contact security@.

Trade-off: builds trust, but adds email noise. Consent during signup.

What admin can and cannot do

Restrict what impersonation can change:

  • ✅ Read user's data (the point).
  • ❌ Change password.
  • ❌ Add MFA factors.
  • ❌ Make payments.
  • ❌ Delete account.

Either gate these in your app (if user.act exists, deny) or in Kratos (set a flag, hooks check it).

On this page