feat(admin): COOL-52: Admin impersonate role#9202
Conversation
Co-authored-by: gemini-code-assist[bot] <176961590+gemini-code-assist[bot]@users.noreply.github.com>
|
bugbot run |
|
bugbot run |
|
bugbot run |
|
Code scanning issue is hitting existing code |
|
Android builds 📱
|
🍹
|
rohan-bes
left a comment
There was a problem hiding this comment.
This looks well implemented, nice. Couple comments but nothing blocking.
…sential/tamanu into feat/impersonate-a-role
| } catch { | ||
| return null; | ||
| } | ||
| } |
There was a problem hiding this comment.
Manual JWT decode duplicates existing jose utility
Low Severity
getImpersonateRoleIdFromToken manually splits and decodes the JWT payload using atob with base64url-to-base64 replacements, duplicating the logic already available in the jose library (decodeJwt) which is a dependency of @tamanu/api-client and supports browser environments. The base class already uses decodeJwt from jose in its login method to decode JWT claims. Using the existing utility avoids subtle encoding edge cases (e.g., missing base64 padding) and reduces duplication.
Reviewed by Cursor Bugbot for commit c7c9b9a. Configure here.
There was a problem hiding this comment.
Cursor Bugbot has reviewed your changes and found 1 potential issue.
There are 2 total unresolved issues (including 1 from previous review).
❌ Bugbot Autofix is OFF. To automatically fix reported issues with cloud agents, enable autofix in the Cursor dashboard.
Reviewed by Cursor Bugbot for commit aada6d3. Configure here.
| [STOP_IMPERSONATION]: action => ({ | ||
| impersonatingRole: null, | ||
| ability: action.ability, | ||
| }), |
There was a problem hiding this comment.
Redux token state becomes stale after impersonation
Medium Severity
The IMPERSONATE_ROLE and STOP_IMPERSONATION Redux actions update ability and impersonatingRole but never update token. Meanwhile, startImpersonation and stopImpersonation thunks call api.setToken(token) which writes the new JWT to localStorage and the API client. This creates a split-brain state: state.auth.token holds the pre-impersonation JWT while localStorage and the API client hold the post-impersonation JWT. Any code reading from state.auth.token — such as the bootstrap code in index.js — would see a stale value.
Additional Locations (2)
Reviewed by Cursor Bugbot for commit aada6d3. Configure here.


Changes
⌘ + clickavatarimpersonate.mov
Server (
facility-server)POST /admin/impersonate— admin-only endpoint that accepts aroleId, mints a new JWT with the impersonated role embedded, and returns the token + recalculated permissions for that roleRedux store (
web/store/auth.js)startImpersonation(role)— calls the server endpoint, swaps the JWT viaapi.setToken(), persists the impersonated role + permissions to localStorage, rebuilds the CASL ability for the impersonated role, dispatchesIMPERSONATE_ROLEstopImpersonation()— same endpoint withroleId: null, restores original token/permissions/ability, dispatchesSTOP_IMPERSONATIONAuth context (
web/contexts/Auth.jsx)impersonatingRoleandabilityfrom Redux and exposes them app-wide viaAuthContextability, so the entire UI reflects the impersonated role's accessUI (
web/components/Sidebar/)HiddenSyncAvatar— meta-click (Cmd/Ctrl+click) on the avatar opens the popover (admin only), visual indicator when impersonatingDeploys
Tests
Review Hero
Remember to...