Skip to content

fix(impersonation): prevent CF auth loop and add target user CF token - #1052

Draft
lewisojile wants to merge 2 commits into
mainfrom
fix/LFXV2-2367
Draft

fix(impersonation): prevent CF auth loop and add target user CF token#1052
lewisojile wants to merge 2 commits into
mainfrom
fix/LFXV2-2367

Conversation

@lewisojile

@lewisojile lewisojile commented Jul 1, 2026

Copy link
Copy Markdown

Summary

Fixes an auth redirect loop that occurred when an admin impersonated a user and navigated to /crowdfunding pages. The error presented was:

{"error": "Authentication required to access this resource", "code": "AUTHENTICATION_REQUIRED"}

Root Causes (three simultaneous)

  1. SSR redirect triggered before Angular booted — the catch-all handler called the CF auth-code redirect on every /crowdfunding page load during impersonation, before Angular had a chance to render anything.
  2. Admin's CF token loaded under impersonationextractCrowdfundingToken loaded the real (admin's) CF token into the impersonation session, returning the wrong user's data or triggering a confusing upstream 401.
  3. 401 CF_UNAUTHENTICATED triggered Angular redirect loopcfFetch returned 401 CF_UNAUTHENTICATED which is the exact condition the Angular handleCfError handler uses to call redirectToCfAuth(), creating a loop.

Fix

Phase 1 (no Auth0 changes required — ships immediately):

  • Guard the SSR CF auth-code redirect with !isImpersonating
  • Return 503 CF_UNAVAILABLE_DURING_IMPERSONATION from cfFetch/cfFetchNullable when no CF token is available during impersonation — Angular's handleCfError falls through to of(fallback) (empty state) instead of redirecting
  • extractCrowdfundingToken returns early during impersonation, never loading the admin's CF token

Phase 2 (best-effort — shows target user's real CF data when Auth0 is configured):

  • During startImpersonation, attempt a second audience-scoped CTE for CROWDFUNDING_API_AUDIENCE using the same NATS subject
  • If Auth0 is configured to allow cross-audience exchange, the target user's CF token is stored in the session (impersonationCrowdfundingToken / impersonationCrowdfundingExpiresAt)
  • If Auth0 returns access_denied (not yet configured), the try/catch logs a warning, impersonation still starts, and CF shows empty state (Phase 1 fallback)

Auth Service Dependency

The Phase 2 CTE requires lfx-v2-auth-service to accept an optional audience field on lfx.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:

  1. Authorize AUTH0_M2M_CLIENT_ID for the CF API with scopes openid profile access:me offline_access
  2. Add a CF audience branch to the CTE Action

Testing

  • Admin impersonates user → navigates to /crowdfunding → no redirect loop, no console auth errors
  • CF sections show empty state (Phase 1) or target user's CF data (Phase 2)
  • Admin stops impersonation → own CF data returns normally
  • Non-impersonating user with no CF token still gets redirected to CF auth (no regression)

Related

  • JIRA: LFXV2-2367
  • Auth service PR: (link once open)

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>
Copilot AI review requested due to automatic review settings July 1, 2026 14:07
@coderabbitai

coderabbitai Bot commented Jul 1, 2026

Copy link
Copy Markdown
Contributor

Important

Review skipped

Draft detected.

Please check the settings in the CodeRabbit UI or the .coderabbit.yaml file in this repository. To trigger a single review, invoke the @coderabbitai review command.

⚙️ Run configuration

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro

Run ID: 88046f09-57e2-4cd8-a53c-c0d4921a9c7e

You can disable this status message by setting the reviews.review_status to false in the CodeRabbit configuration file.

Use the checkbox below for a quick retry:

  • 🔍 Trigger review
✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch fix/LFXV2-2367

Comment @coderabbitai help to get the list of available commands.

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

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.

Comment on lines +59 to +68
// 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',
});
}
Comment on lines +135 to +140
if (req.appSession?.['impersonationToken']) {
throw new MicroserviceError('Crowdfunding is unavailable during impersonation', 503, 'CF_UNAVAILABLE_DURING_IMPERSONATION', {
operation,
service: 'crowdfunding',
});
}
Comment on lines +260 to 265
* 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>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants