ERA-13429 Collapse EarthRanger Web's flow selection to two-way - #1689
Conversation
a2d5fd8 to
f9ab334
Compare
There was a problem hiding this comment.
Pull request overview
This PR simplifies EarthRanger Web’s authentication flow selection by removing the now-dead Auth0-Organizations (org-scoped) branch and standardizing behavior across all Auth0 (require_idp=true) sites. It ensures Auth0 login and post-callback handling no longer depend on idp_org_id, while keeping legacy ROPC (require_idp=false) untouched.
Changes:
- Remove
idp_org_id-driven branching: Auth0 login always uses{ audience }authorization params, and MFA step-up no longer sends anorganization. - Apply the post-PKCE account-linking gate consistently on all Auth0 sites.
- Stop storing
idp_org_idin system config state and remove the now-unused Auth0 param builder + locale string.
Reviewed changes
Copilot reviewed 18 out of 18 changed files in this pull request and generated no comments.
Show a summary per file
| File | Description |
|---|---|
| src/utils/auth0.js | Removes buildAuth0AuthorizationParams; keeps only callback param detection. |
| src/utils/auth0.test.js | Drops tests for the removed authorization param builder. |
| src/utils/auth.test.js | Updates system-config-loaded fixture to no longer include idp_org_id. |
| src/Login/index.js | Collapses Auth0 UI/behavior to a single path; always uses audience-only params and always shows the info box when require_idp. |
| src/Login/index.test.js | Updates expectations for button label, absence of organization param, and info box rendering on all Auth0 sites. |
| src/hooks/useAuthRecovery.js | Removes Redux dependency and ensures MFA step-up uses audience-only params (plus acr_values/max_age). |
| src/hooks/useAuthRecovery.test.js | Updates assertions to match the new step-up authorization params. |
| src/ducks/system-config/index.js | Stops persisting idp_org_id into Redux system config state. |
| src/ducks/system-config/index.test.js | Pins “ignore server-supplied idp_org_id” behavior by ensuring it isn’t stored. |
| src/Auth0TokenManager/index.js | Removes org-scoped bypass; runs account-linking gate for all require_idp sites. |
| src/Auth0TokenManager/index.test.js | Updates gating tests to reflect “gate always runs” behavior. |
| src/Auth0TokenManager/accountLinkingGate.integration.test.js | Updates integration fixture to no longer depend on idp_org_id. |
| public/locales/en-US/login.json | Removes loginButtonIdp string (no longer referenced). |
| public/locales/es/login.json | Removes loginButtonIdp string (no longer referenced). |
| public/locales/fr/login.json | Removes loginButtonIdp string (no longer referenced). |
| public/locales/ne-NP/login.json | Removes loginButtonIdp string (no longer referenced). |
| public/locales/pt/login.json | Removes loginButtonIdp string (no longer referenced). |
| public/locales/sw/login.json | Removes loginButtonIdp string (no longer referenced). |
🚀 PR Environment Deployed
Access: https://era-13429.dev.pamdas.org |
f9ab334 to
5f66650
Compare
|
To keep the environment alive:
|
|
🗑️ Environment torn down due to inactivity |
EarthRanger has moved away from Auth0 Organizations, so no site sets idp_org_id and the org-scoped render branch is dead. The login page now shows one Auth0 path -- "Sign in with email" plus the migration info box -- and sends only the audience, ignoring an idp_org_id the status response still reports until ERA-13666 clears it. The managed-user button loses its org-scoped exclusion for the same reason, and its redirect carries the site connection without an organization. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
The gate previously ran only on the common-DB path, skipping org-scoped sites. With the org branch gone, every Auth0 return goes through it, so users on formerly org-scoped sites with an unconverted account are handed to the server link page rather than straight through. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Step-up now re-runs PKCE against the common DB like any other login, so the hook no longer reads systemConfig at all. buildAuth0AuthorizationParams is left with no callers and goes next. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Nothing reads it now that routing is two-way. The status response keeps sending the field until ERA-13666 clears it, so the payload drops it rather than passing it through. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
f04cf54 to
a514555
Compare
buildAuth0AuthorizationParams lost its last organization caller when step-up stopped sending one, so it drops the idp_org_id param and keeps only the connection the managed-user path needs. The loginButtonIdp label died with the org render branch. No behavior change. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Every Auth0 site now signs in through EarthRanger Identity, so the organization's-identity-provider route AGENTS.md still described is gone. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
a514555 to
dfae138
Compare
There was a problem hiding this comment.
Claude Code Review
This repository is configured for manual code reviews. Comment @claude review for a one-time review, or @claude review always to subscribe this PR to a review on every future push.
Tip: disable this comment in your organization's Code Review settings.
luixlive
left a comment
There was a problem hiding this comment.
Changes look good to me ✅
Claude found some minor issue, probably worth validating them. None of them seems like a blocker to me.
-
MFA step-up drops the managed-user connection —
src/hooks/useAuthRecovery.js:25
When a managed user hits an MFA step-up, the re-login redirect doesn't send the siteconnection. They sign in against the default directory instead, which is whatLogin/index.js:62warns about. This bug was already there before the PR, but the PR rewrites this exact params object, so this is the place to decide whether step-up should carry the connection. -
No regression test for the
access_deniedchange —src/Login/index.test.js:952
The org-membership alert test was deleted rather than turned around. The onlyaccess_deniedtest left would also pass on the old code. Add a case witherror_description=user+is+not+part+of+the+orgthat expects the generic "Access denied" alert. -
Dead guard and needless effect dependency —
src/Auth0TokenManager/index.js:130
The guardif (!requireIdp || !isAuthenticated || existingToken) return;is the last statement in the function, so it does nothing. TheexistingTokenselector (line 28) and its effect dependency (line 135) exist only for this guard, and they re-run the effect every time the token changes. Remove all three. The PR already edits that dependency list. -
buildAuth0AuthorizationParamsis left half-used —src/utils/auth0.js:8
It now has a single caller, the managed-user login, wheresiteSlugis always already trimmed and non-empty, so its trimming and empty check never matter. The other three Auth0 calls build{ audience }inline. Either inline it and delete the helper and its 4 tests, or use it in all four places. -
Rewritten JSX comment breaks the comment rules —
src/Login/index.js:209
It's a three-line{/* */}block that mostly describes what the code does. Cut it, or reduce it to one line giving the reason.
What this does
Collapses EarthRanger Web's three-way flow selection to two-way. EarthRanger has moved away from Auth0 Organizations, so no site sets
idp_org_idand the org-scoped branch is dead code and routing reduces to common-DB Auth0 vs. legacy ROPC.Flow selection still keys on
feature_flags.require_idpfrom/api/v1.0/status. Replacing that with RFC 9728 discovery is deliberately not here — it is ERA-13805, stacked on this branch, and it carries a different release gate. Splitting them means this half does not have to wait on the server and ingress work that one needs.Changes
Login/index.jsconnectionwithout anorganization.access_deniedalways shows the generic alert; the org-membership one is gone.Auth0TokenManager/index.jshooks/useAuthRecovery.jsorganization. The hook no longer reads Redux at all.ducks/system-configidp_org_id. The status response still carries the field, null on every site since ERA-13666, so Web drops it on ingest rather than passing it through.utils/auth0.js, localesbuildAuth0AuthorizationParamsdrops itsidp_org_idparam and keeps only theconnectionthe managed-user path needs. TheloginButtonIdpandaccessDeniedNotAuthorizedstrings go from all six locale files, withI18N_FILES_VERSIONbumped to 1.77.User-visible impact — not transparent, by design
On a formerly org-scoped site, users see the migration info box and the "Sign in with email" label instead of "Sign in". More significantly, their first Auth0 return now passes through the account-linking gate, so an account that has not been converted is handed to the server account linker rather than straight through. That is the intended Phase 3 end state. Where the site supports managed users, a formerly org-scoped site now offers the managed-user button too.
The legacy ROPC path (
require_idp = false) is untouched.ERA-13666 is done
ERA-13666 cleared
idp_org_idon the last site that set it, so no site is org-scoped and this PR no longer waits on anything.developalready sends noorganizationanywhere; this PR removes the code that could.Auth0 now enforces the same end state: since 2026-09-29 the Web SPA is at
organization_usage = "deny"inauth0-platform-management, so a login carrying anorganizationwould be rejected outright. That also makes Auth0's org-membership error unreachable, which is why its alert goes too.Testing
Full suite green: 5537 tests / 409 suites. Written test-first, one behaviour per commit; each commit passes on its own.
Three test files deliberately keep
idp_org_id: 'org_abc'in their fixtures — insystem-config,Auth0TokenManagerandLogin. The status response still carries that field, so "Web ignores a server-supplied organization ID" is a live behaviour worth pinning, not a leftover.Commits
Each is a red test → implementation → refactor cycle.
AGENTS.md)