feat(admin): mimoquage (impersonation) d'une entreprise#3188
Merged
LucasCharrier merged 21 commits intoalphafrom Apr 13, 2026
Merged
feat(admin): mimoquage (impersonation) d'une entreprise#3188LucasCharrier merged 21 commits intoalphafrom
LucasCharrier merged 21 commits intoalphafrom
Conversation
Adds an is_admin boolean flag on app_user to support the backoffice access control (issue #3178).
Introduce a server-side ADMIN_EMAILS env var (comma-separated). On each login, the NextAuth jwt callback syncs app_user.is_admin with that list (promotion and demotion), and propagates the flag into the JWT and session. Refactor the dbUser block to remove non-null assertions.
Restricts procedures to users with session.user.isAdmin === true and throws FORBIDDEN otherwise.
Edge middleware checks the JWT isAdmin flag on /admin/*. The admin layout re-checks the session on the Node runtime (defense in depth) and redirects non-admin users to /mon-espace. A minimal AdminHomePage is rendered for admin users.
- middleware: treat a missing token.isAdmin as 'old token' and force re-login so the flag gets populated (otherwise existing users with a pre-PR JWT would be bounced to /mon-espace for up to 30 days). - auth/config: memoize the ADMIN_EMAILS set at module load instead of reparsing the env var on every sign-in. - admin/page: split the defense-in-depth check to redirect to /login when there is no session (avoids a redirect chain).
Adds unit tests for src/middleware.ts (4 cases: no token, missing isAdmin, non-admin, admin) and src/app/admin/page.tsx (4 cases: no session, non-admin, admin, fallback to email when name is null) to lift coverage on the new code.
The DB column was redundant: ADMIN_EMAILS is already the sole source of truth, re-evaluated on every sign-in. Remove the migration, the schema column, and the DB sync in the jwt callback. token.isAdmin is now computed directly from the memoized env var set. Promotion/demotion still takes effect at next login (same as before) but with zero persistent state to manage.
…edback - Extract the ADMIN_EMAILS parsing into a pure parseAdminEmails helper with 9 unit tests (edge cases: null/undefined, empty, whitespace, casing, trailing commas, dedup). Addresses maxgfr's review. - Remove the redundant defense-in-depth check in app/admin/page.tsx: the edge middleware + layout already guard the route. Addresses Viczei's review. - Wire ADMIN_EMAILS via a new optional 'admin' configmap on dev and preprod environments with test@fia1.fr preset, so QA and the E2E auth.setup user get backoffice access on non-prod. Prod has no configmap and must set ADMIN_EMAILS explicitly. Addresses Viczei's review.
Addresses security and RGAA findings from the validation agents: - The audit trail is now written inside the NextAuth jwt callback, so every row in app_admin_impersonation_event corresponds to an actual live JWT state (previously the client could start the mutation and never update the session, or vice-versa). - Company upsert in startImpersonate switches to onConflictDoNothing() so an admin search cannot silently overwrite metadata visible to real referents of that company. - DSFR fr-alert and fr-notice markup fixed (heading for title, proper container nesting). - Redundant role=alert + aria-live on the form removed; aria-invalid and aria-required added on the SIREN input. - ImpersonateForm preview state derived from mutation data. - modules/admin barrel re-exports schemas. - E2E: non-admin redirect covered for /admin/impersonate.
revu-bot
reviewed
Apr 8, 2026
Collaborator
revu-bot
left a comment
There was a problem hiding this comment.
Admin Impersonation Feature Review
This PR implements an admin impersonation ("mimoquage") feature with a solid overall architecture: JWT-based state, atomic audit trail writes, strict SIREN validation, and centralized access control via isImpersonatingSiren. The test coverage for the core logic is good.
However, there are several issues ranging from a security gap to silent failure modes and a write-mutation concern that need attention before merging.
| File | Lines | Severity | Issue |
|---|---|---|---|
server/auth/config.ts |
218–232 | CRITICAL | Silent no-op on invalid impersonation payload — no error feedback to client |
server/auth/config.ts |
60–80 | IMPORTANT | closeOpenImpersonationEvents runs outside the transaction in recordImpersonationStart |
server/api/routers/company.ts |
198–204 | IMPORTANT | updateHasCse mutation allowed for impersonating admin — write bypass too broad |
modules/layout/ImpersonateBanner.tsx |
24–27 | IMPORTANT | onStop has no error handling — silent failure leaves stale banner state |
server/api/routers/admin.ts |
26–27 | MINOR | searchCompany uses .mutation for a pure read — should be .query |
When an admin is impersonating a company, the server-side page was still passing the admin's own SIRET (from ProConnect) to MonEspacePage. Now it passes the impersonated SIREN, so /mon-espace correctly shows the impersonated company's declarations.
7 tasks
Viczei
approved these changes
Apr 9, 2026
…n-impersonate # Conflicts: # .kontinuous/values.yaml # packages/app/drizzle/meta/_journal.json # packages/app/src/e2e/admin.e2e.ts # packages/app/src/env.js # packages/app/src/modules/admin/index.ts # packages/app/src/server/auth/config.ts
In CI, test@fia1.fr is admin (ADMIN_EMAILS), so the non-admin redirect test always fails. Replace with a positive test that verifies the admin can access the impersonate page.
|
🎉 Deployment for commit 2f51459 : Docker images
|
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Summary
/admin/impersonate(recherche → prévisualisation → validation).app_admin_impersonation_event) écrit atomiquement dans le callbackjwtpour rester cohérent avec le token effectif.ImpersonateBannerdans le layout pour sortir du mimoquage depuis n'importe quelle page ;companyProcedureetfindUserCompanyaccordent l'accès à la SIREN mimoquée uniquement quandisAdmin && impersonation.siren === siren(bypass strict, pas de propagation).Closes #3179
Quality gates
Generated with Claude Code