fix(impersonation): prevent CF auth loop and add target user CF token - #1052
fix(impersonation): prevent CF auth loop and add target user CF token#1052lewisojile wants to merge 2 commits into
Conversation
Fix the auth redirect loop when impersonating on /crowdfunding pages. Three simultaneous root causes: - SSR handler triggered the CF auth-code redirect before Angular booted - extractCrowdfundingToken loaded the admin's CF token during impersonation - cfFetch threw 401 CF_UNAUTHENTICATED, triggering the Angular redirect loop Phase 1: guard SSR redirect with isImpersonating; return 503 CF_UNAVAILABLE_DURING_IMPERSONATION so Angular shows empty state instead of triggering the redirectToCfAuth() loop. Phase 2: attempt an audience-scoped CTE for CROWDFUNDING_API_AUDIENCE during startImpersonation. On success, the target user's CF token is stored in session and loaded by extractCrowdfundingToken. The CTE is best-effort — if Auth0 is not yet configured for cross-audience exchange, CF sections show empty state and impersonation still starts normally. Requires lfx-v2-auth-service to accept the optional 'audience' field on the lfx.auth-service.impersonation.token_exchange NATS subject (separate PR in that repo). LFXV2-2367 Signed-off-by: lewisojile <lewisojile@gmail.com>
|
Important Review skippedDraft detected. Please check the settings in the CodeRabbit UI or the ⚙️ Run configurationConfiguration used: Organization UI Review profile: CHILL Plan: Pro Run ID: You can disable this status message by setting the Use the checkbox below for a quick retry:
✨ Finishing Touches🧪 Generate unit tests (beta)
Comment |
There was a problem hiding this comment.
Pull request overview
Fixes an admin impersonation edge case on /crowdfunding routes by preventing SSR from initiating the Crowdfunding auth-code redirect during impersonation, avoiding loading the admin’s CF token into an impersonated session, and (optionally) fetching/storing a target-user CF token via an audience-scoped CTE at impersonation start.
Changes:
- Skip the SSR crowdfunding auth-code redirect while impersonating, preventing redirect loops before Angular bootstraps.
- Ensure impersonation never reuses the admin’s CF token; optionally store a target-user CF token via audience-scoped CTE when available.
- Return a distinct CF “unavailable during impersonation” error when no CF token is available, allowing the client to fall back to empty-state instead of redirecting.
Reviewed changes
Copilot reviewed 6 out of 7 changed files in this pull request and generated 3 comments.
Show a summary per file
| File | Description |
|---|---|
| apps/lfx-one/src/types/express.d.ts | Adds session typings for storing impersonated user’s CF token + expiry. |
| apps/lfx-one/src/server/utils/auth-helper.ts | Clears impersonation CF token fields when stopping impersonation. |
| apps/lfx-one/src/server/services/impersonation.service.ts | Adds optional audience to CTE exchange and stores a CF-audience token in the impersonation session when obtained. |
| apps/lfx-one/src/server/services/crowdfunding.service.ts | Adds impersonation-aware behavior when CF token is missing (prevent CF auth redirect loop). |
| apps/lfx-one/src/server/server.ts | Skips SSR CF auth redirect during impersonation to prevent loops. |
| apps/lfx-one/src/server/middleware/auth.middleware.ts | Loads impersonation CF token onto req.crowdfundingToken and avoids loading admin’s CF token while impersonating. |
| apps/lfx-one/src/server/controllers/impersonation.controller.ts | Best-effort CF-audience CTE during impersonation start; stores token if available. |
💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.
| // During impersonation, a CF-audience CTE is attempted at session start but may | ||
| // fail (e.g. Auth0 not configured for cross-audience exchange, or target user has | ||
| // no CF account). Use 503 so the client falls through to the empty-state fallback | ||
| // rather than triggering the CF auth-code redirect loop. | ||
| if (req.appSession?.['impersonationToken']) { | ||
| throw new MicroserviceError('Crowdfunding is unavailable during impersonation', 503, 'CF_UNAVAILABLE_DURING_IMPERSONATION', { | ||
| operation, | ||
| service: 'crowdfunding', | ||
| }); | ||
| } |
| if (req.appSession?.['impersonationToken']) { | ||
| throw new MicroserviceError('Crowdfunding is unavailable during impersonation', 503, 'CF_UNAVAILABLE_DURING_IMPERSONATION', { | ||
| operation, | ||
| service: 'crowdfunding', | ||
| }); | ||
| } |
| * During impersonation the normal (admin's) CF token is never used — it would return | ||
| * the admin's data under the target user's session. Instead, a CF-audience CTE token | ||
| * for the target user is stored at impersonation start and loaded here. If no such | ||
| * token exists (CTE failed or Auth0 cross-audience not configured), req.crowdfundingToken | ||
| * is left undefined and cfFetch() falls back to an empty-state 503 response. | ||
| */ |
Signed-off-by: lewisojile <lewisojile@gmail.com>
Summary
Fixes an auth redirect loop that occurred when an admin impersonated a user and navigated to
/crowdfundingpages. The error presented was:{"error": "Authentication required to access this resource", "code": "AUTHENTICATION_REQUIRED"}Root Causes (three simultaneous)
/crowdfundingpage load during impersonation, before Angular had a chance to render anything.extractCrowdfundingTokenloaded the real (admin's) CF token into the impersonation session, returning the wrong user's data or triggering a confusing upstream 401.cfFetchreturned401 CF_UNAUTHENTICATEDwhich is the exact condition the AngularhandleCfErrorhandler uses to callredirectToCfAuth(), creating a loop.Fix
Phase 1 (no Auth0 changes required — ships immediately):
!isImpersonating503 CF_UNAVAILABLE_DURING_IMPERSONATIONfromcfFetch/cfFetchNullablewhen no CF token is available during impersonation — Angular'shandleCfErrorfalls through toof(fallback)(empty state) instead of redirectingextractCrowdfundingTokenreturns early during impersonation, never loading the admin's CF tokenPhase 2 (best-effort — shows target user's real CF data when Auth0 is configured):
startImpersonation, attempt a second audience-scoped CTE forCROWDFUNDING_API_AUDIENCEusing the same NATS subjectimpersonationCrowdfundingToken/impersonationCrowdfundingExpiresAt)access_denied(not yet configured), thetry/catchlogs a warning, impersonation still starts, and CF shows empty state (Phase 1 fallback)Auth Service Dependency
The Phase 2 CTE requires
lfx-v2-auth-serviceto accept an optionalaudiencefield onlfx.auth-service.impersonation.token_exchange. That change has been implemented in a separate PR in that repo. If the auth service is deployed without that change, the CF CTE NATS call will still succeed (the auth service silently ignores unknown fields) but will issue a primary LFX V2 token scoped to the wrong audience — the token will be rejected by the CF API and the BFF will fall through to Phase 1 empty state. No breakage.Auth0 Tenant Configuration (for Phase 2 to show live CF data)
See
cf-impersonation-solution.md§9 for the two Auth0 tenant changes needed:AUTH0_M2M_CLIENT_IDfor the CF API with scopesopenid profile access:me offline_accessTesting
/crowdfunding→ no redirect loop, no console auth errorsRelated