fix: demo logins (acme/globex) on Aspire launch + expired-token session restore#1260
Merged
Conversation
The dashboard's demo-login panel advertises acme/globex accounts, but AppHost only ran the migrator's `apply --seed` verb, which provisions the root tenant/admin only. The acme/globex demo tenants are created exclusively by the dev-only `seed-demo` verb, which AppHost never invoked - so every acme/globex login failed against a freshly-provisioned DB. - AppHost: add a dev-only `fsh-demo-seeder` resource that runs `seed-demo` after the base migration; the API waits on it so the demo accounts exist before the dashboard is reachable. Pin DOTNET_ENVIRONMENT=Development (the generic-host migrator ignores Aspire's injected ASPNETCORE_ENVIRONMENT) and pass Seed__DemoPassword explicitly. - DbMigrator: disable build-time DI validation. The migrator runs a reduced service graph (Mailing/SignalR/Jobs disabled); Development's auto-on ValidateOnBuild walked request handlers the migrator never invokes (needing IHubContext/IMailService) and crashed startup. The migrator only resolves migration/seed services. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Both apps derived isAuthenticated from token presence/decodability and never checked `exp`. A stale/expired token in localStorage booted straight into the app, firing protected requests that 401'd in a loop (the SecurityTokenExpiredException bursts) instead of refreshing or routing to /login. - jwt.ts: add isTokenExpired (with a small clock-skew margin). - auth-context: on boot, if the access token is missing/expired but a refresh token is present, run one silent refresh before deciding signed-in vs /login (new isInitializing flag). A live session whose access token expires mid-use stays authenticated (reactive 401-refresh handles it) so long-lived sessions are preserved. - protected-route: render a loader while initializing so no protected request fires with a dead token. - api-client: export refreshAccessToken for the boot path. - tests: session-restore specs for both apps (refresh-fails -> /login; refresh-succeeds -> session restored, no /login bounce). Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
iammukeshm
added a commit
to fullstackhero/docs
that referenced
this pull request
May 27, 2026
- changelog: entries for the demo-seed wiring (new fsh-demo-seeder resource) and the expired-token session-restore fix in both React apps. - aspire / database-migrations / introduction: document the fsh-demo-seeder resource and that Aspire auto-runs seed-demo on launch; correct the seed-demo env gate to DOTNET_ENVIRONMENT (the migrator is a generic-host console app, not a web host that reads ASPNETCORE_ENVIRONMENT). Tracks fullstackhero/dotnet-starter-kit#1260. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
iammukeshm
added a commit
that referenced
this pull request
May 27, 2026
The demo seeder realigns every tenant admin -- including root's admin@root.com -- to the shared demo password, so under the Aspire dev stack the root account signs in with Password123!, not the framework default 123Pa$$word!. Surfaced by running seed-demo on launch (PR #1260). Point the demo login panel (ROOT_PASSWORD) and the sample token request at Password123! so the advertised dev credential actually works. The framework seed default (Seed:DefaultAdminPassword) is unchanged for non-demo / production seeds. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
pull Bot
pushed a commit
to NicoJuicy/dotnet-webapi-boilerplate
that referenced
this pull request
May 27, 2026
The demo seeder realigns every tenant admin -- including root's admin@root.com -- to the shared demo password, so under the Aspire dev stack the root account signs in with Password123!, not the framework default 123Pa$$word!. Surfaced by running seed-demo on launch (PR fullstackhero#1260). Point the demo login panel (ROOT_PASSWORD) and the sample token request at Password123! so the advertised dev credential actually works. The framework seed default (Seed:DefaultAdminPassword) is unchanged for non-demo / production seeds. Co-Authored-By: Claude Opus 4.7 (1M context) <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.
Why
On a fresh
dotnet run --project src/Host/FSH.Starter.AppHost, logging into the dashboard with the advertised demo accounts (admin@acme.com/Password123!) failed in every browser, including incognito. Two independent defects:apply --seedverb (root tenant/admin only). Theacme/globexdemo tenants are created solely by the dev-onlyseed-demoverb, which AppHost never invoked — so the demo-login panel advertised accounts that didn't exist.isAuthenticatedfrom token presence/decodability without checkingexp, so a leftover token inlocalStoragerendered the app and fired protected requests that 401'd in a loop (SecurityTokenExpiredException) instead of refreshing or routing to/login.What changed
Backend / scaffold
fsh-demo-seederresource runsseed-demoafter the base migration; the API waits for it so demo accounts exist before the dashboard is reachable. Idempotent. PinsDOTNET_ENVIRONMENT=Development(the generic-host migrator ignores Aspire's injectedASPNETCORE_ENVIRONMENT) and passesSeed__DemoPasswordexplicitly.ValidateOnBuildwalked request handlers the migrator never invokes (needingIHubContext/IMailService) and crashed startup. The migrator only resolves migration/seed services, so this validation was a false positive (no-op in Production).Frontend (admin + dashboard, symmetric)
isTokenExpiredadded tojwt.ts.isInitializing), deciding signed-in vs/loginbefore rendering. A live session whose access token expires mid-use stays authenticated (reactive 401-refresh), so the 45-min access / 7-day refresh UX is preserved.ProtectedRouterenders a loader while initializing so no request fires with a dead token.refreshAccessTokenfor the boot path.Tests
tests/auth/session-restore.spec.tsin both apps: expired token + refresh-fails → routes to/login; expired token + refresh-succeeds → session restored, no/loginbounce. 4/4 pass.tsc -bclean for both apps;eslint0 errors; backenddotnet buildclean.Verification
Relaunching Aspire seeds
acme/globexand demo logins succeed; a stale token now silently refreshes or cleanly redirects to/logininstead of dumpingSecurityTokenExpiredException.🤖 Generated with Claude Code