Repository navigation
fix(auth): recover from a blacklisted session instead of looping - #540
Open
nicdavidson wants to merge 2 commits into
Open
nicdavidson wants to merge 2 commits into
nicdavidson wants to merge 2 commits into
Conversation
Changing the admin password blacklists the current session token. If the dead token stays in the cookie, every page load sends it with the bootstrap GET /system/environment and DF answers 403 "The token has been blacklisted: Session terminated. Please re-login". The error interceptor only cleared the token on 401, and returned early for silent requests (the environment call is silent), so the login page never rendered until the user cleared browser storage by hand. - errorInterceptor: a 401/403 whose message says the token was blacklisted, terminated or expired, on a request that carried a session token, now clears the token, routes to login, and retries the request once without the token. This runs before the silent opt-out, so bootstrap recovers. - normalizeError: such 403s normalize to kind auth instead of forbidden. - DfPasswordService.updatePassword: only store a session token the server actually returned. - Tests cover the bootstrap recovery, the single retry, plain 403s, plain 401s, and staying put on the login page. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
nicdavidson
added a commit
that referenced
this pull request
Oct 9, 2026
…ckers-presentation-mode Brings the dead-session recovery into the branch df-dev runs. Conflict in error.interceptor.ts resolved by keeping the trial-lock branch first (it must win over the dead-session check, and still applies to silent requests), then the dead-session clear-and-retry, then the existing severity routing. error.interceptor.spec.ts now holds both the trial-lock and the dead-session suites. dist rebuilt. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
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.
Problem
After changing the admin password in the UI, the admin interface can lock itself out until the user clears browser storage by hand.
session_tokencookie, every page load sends it with the bootstrapGET /api/v2/system/environment.The token has been blacklisted: Session terminated. Please re-login.errorInterceptoronly cleared the token on 401, and returned early forsilentrequests. The environment call is silent, so the token was never cleared and the login page never rendered.Seen on df-dev: every load produced the 403 on
/system/environment, while the same URL with no token returned 200.Fix
errorInterceptor: a 401/403 whose message says the token was blacklisted, the session was terminated, or the token expired, on a request that carried a session token, now clears the token, routes to login (unless already onauth/*), and retries the request once without the token. This check runs before thesilentopt-out, so bootstrap recovers. Public endpoints then succeed; protected ones return a plain 401 and take the existing auth branch.SESSION_RETRY(newHttpContextToken) limits it to one retry. The handler is restructured around a localsend()so the retry gets the same success/error handling without callinginject()outside the injection context.normalizeError: a 403 with a dead-session message normalizes to kindauthinstead offorbidden, so component-level handling agrees.DfPasswordService.updatePassword: only stores a session token the server actually returned.dist/: rebuilt in a separate commit, per repo convention.Tests
error.interceptor.spec.ts:app-error.spec.tscase: a blacklisted 403 becomesauth, a permission 403 staysforbidden.jest --config jest.config.ci.js: 21 suites, 169 tests pass. Prettier and ESLint are clean on the changed files;tsc -p tsconfig.app.jsonpasses.Not in this PR
PUT /api/v2/system/admin/1(and the matching GET withrelated=...) returnedResource '1' not found for service 'admin'from the profile/admin page. Worth a separate look.🤖 Generated with Claude Code