Superadmin impersonation, hardened auth/upload paths, dependency updates

- Add superadmin impersonation: sessions.impersonated_by (migration 0003) is
  stamped onto audit rows so impersonated actions are attributable, with a
  persistent ImpersonationBanner in the app shell.
- Harden auth and upload handling across routes (safe redirect targets,
  filename sanitization, checkout grant handling).
- Update dependencies: Sentry 8 -> 10, @fastify/static 8 -> 10,
  react-router-dom 6.30.6; add find-my-way / fast-uri overrides.
- Add tests for safe-next, checkout-grant, and upload-filename.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
This commit is contained in:
Leon Serfaty
2026-08-26 10:25:39 -04:00
co-authored by Claude Opus 5
parent 4a1122a7c9
commit eb36b81dc9
28 changed files with 7936 additions and 5763 deletions
+19
View File
@@ -10,6 +10,8 @@ export interface AuthUser {
isSuperadmin?: boolean;
isSuspended?: boolean;
emailVerified?: boolean;
/** Superadmin id when this session was opened by admin impersonation, else null. */
impersonatedBy?: string | null;
}
interface MeResponse {
@@ -69,3 +71,20 @@ export function useLogout() {
onSuccess: () => qc.setQueryData(ME_KEY, null),
});
}
/**
* Leave an impersonated session. The server destroys the borrowed session outright, so there is
* no session left to return to — the admin lands on the login page and signs in as themselves.
*/
export function useEndImpersonation() {
const qc = useQueryClient();
return useMutation<void, ApiError>({
mutationFn: async () => {
await api.post('/api/auth/end-impersonation');
},
onSuccess: () => {
qc.setQueryData(ME_KEY, null);
qc.clear();
},
});
}