feat(admin): view the app as another user (#443)#445
Conversation
Adds an admin-only "View as user" control so admins can experience the product exactly as a specified user for QA. The single seam is getPageSession(): applyImpersonation() swaps identity_id, account, and memberships to the target when the *real* resolved account is an admin and an encrypted `sc_impersonate` cookie is present. Because it's a full identity swap, all ~148 authz call sites and the data-proxy STS minting act as the target with no other changes. isAdmin() reads the swapped account, so the admin correctly loses admin powers while impersonating. - Gate is isAdmin(realSession); a forged cookie from a non-admin is inert, so encryption is defense-in-depth, not the boundary. - Only individual accounts (which have an Ory identity) are assumable. - Start: admin-only button on a user's profile → startImpersonation action. - Exit: always-on amber banner in the (app) layout → stopImpersonation action. - Cleared on /logout too. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
|
The latest updates on your projects. Learn more about Vercel for GitHub.
|
|
Claude finished @alukach's task in 4m 36s —— View job Review of #445 — view the app as another user
❌ Changes requested — see findings below.
Everything else — the |
Closes #443 — admin QA: view the product as a specified user.
What
An admin can open any individual user's profile and click View as user. The whole app then renders and behaves as that user. An always-on amber banner names the target and offers Exit.
How
The one seam is
getPageSession()— the single server-side identity resolver every server component, server action, and cookie-based API call flows through.applyImpersonation()runs at the end and, only when the real resolved account is an admin and an encryptedsc_impersonatecookie is present, returns a session that fully assumes the target individual account:identity_id,account, andmembershipsall swapped to the targetimpersonatorset to the real admin (for the banner + audit log line)orySessionkeptBecause it's a full identity swap, every one of the ~148 authz call sites and the data-proxy STS credential minting act as the target with no other changes.
isAdmin()reads the swapped account, so the admin correctly loses admin powers while impersonating — you see exactly what the target sees.Security
isAdmin(realSession). A forged cookie from a non-admin is inert, so the AES-GCM encryption (reusing the existingencrypted-cookieservice) is defense-in-depth, not the boundary.httpOnly,secure,sameSite=lax; cleared on Exit and on /logout./api/v1Bearer auth never touchesgetPageSession(), so token clients can't be impersonated.identity_id, so entering/exiting impersonation re-mints cleanly with no stale-cred leakage.Decisions baked in (per design discussion)
Tests
impersonation.test.tscovers the four branches: admin+valid cookie → full swap; non-admin → inert (short-circuits before reading the cookie); admin+no cookie → unchanged; org target → ignored.tscclean, existing admin/page-session suites pass.Deliberately skipped (add when needed)
LOGGER.infoline)🤖 Generated with Claude Code