fix(auth): bypass OIDC challenge when placeholder Auth0 config is active (#396)#397
Conversation
In Development/Testing with no real Auth0 credentials configured, the placeholder domain (test.auth0.com) would cause /Account/Login to hang with IDX20803 until timeout. Changes: - Add isPlaceholderAuth0Config flag (true only when placeholder values are used); /Account/Login redirects to /test/login when set, avoiding any real OIDC discovery call. - Developers with real user-secrets configured are unaffected — flag stays false and Auth0 Universal Login flow is preserved. - Guard existingOnTokenValidated against null before invoking it, preventing a latent NullReferenceException on token validation. - Update docs/AUTH0_SETUP.md with IDX20803 troubleshooting entry. Closes #396 Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
There was a problem hiding this comment.
Pull request overview
This PR improves the local Development/Testing login experience when Auth0 configuration is missing by avoiding an OIDC discovery call to the placeholder test.auth0.com domain, while keeping the real Auth0 flow unchanged for developers who have configured credentials.
Changes:
- Introduces an
isPlaceholderAuth0Configflag and uses it to short-circuit/Account/Loginto the existing/test/loginendpoint in Development/Testing. - Adds a null-guard when invoking a captured
OnTokenValidateddelegate to preventNullReferenceException. - Documents the
IDX20803timeout pattern and the intended fallback behavior in Auth0 setup docs.
Reviewed changes
Copilot reviewed 3 out of 3 changed files in this pull request and generated 2 comments.
| File | Description |
|---|---|
src/Web/Program.cs |
Adds placeholder-config detection, bypasses OIDC challenge in placeholder mode, and guards OnTokenValidated delegate invocation. |
docs/AUTH0_SETUP.md |
Adds troubleshooting guidance for IDX20803 timeouts when placeholder Auth0 config is used locally. |
.squad/agents/gandalf/history.md |
Records the issue context, fix details, and suggested test scenarios for the squad security agent history. |
Comments suppressed due to low confidence (1)
src/Web/Program.cs:63
- Production/staging config validation uses
string.IsNullOrEmptyforAuth0:DomainandAuth0:ClientId, so whitespace-only values would pass startup checks but still break OIDC later. Usingstring.IsNullOrWhiteSpacehere (to match the Dev/Testing branch) would fail fast on clearly invalid configuration.
if (!builder.Environment.IsDevelopment() && !builder.Environment.IsEnvironment("Testing"))
{
if (string.IsNullOrEmpty(auth0Domain) || string.IsNullOrEmpty(auth0ClientId))
{
throw new InvalidOperationException(
"Auth0 configuration is missing or incomplete. Set these user secrets for the Web project:\n" +
" dotnet user-secrets set \"Auth0:Domain\" \"<your-tenant>.auth0.com\" --project src/Web\n" +
" dotnet user-secrets set \"Auth0:ClientId\" \"<your-client-id>\" --project src/Web\n" +
" dotnet user-secrets set \"Auth0:ClientSecret\" \"<your-client-secret>\" --project src/Web");
}
| // In Development/Testing with no real Auth0 credentials configured, bypass the OIDC | ||
| // discovery call — which would time out against the placeholder domain — and redirect | ||
| // straight to the test login endpoint instead. | ||
| if (isPlaceholderAuth0Config) | ||
| { | ||
| ctx.Response.Redirect("/test/login"); | ||
| return; | ||
| } |
| // Auth0 login/logout endpoints | ||
| app.MapGet("/Account/Login", async (HttpContext ctx, string? returnUrl) => | ||
| { | ||
| var safeReturn = !string.IsNullOrEmpty(returnUrl) | ||
| && Uri.IsWellFormedUriString(returnUrl, UriKind.Relative) | ||
| ? returnUrl | ||
| : "/"; | ||
|
|
||
| // In Development/Testing with no real Auth0 credentials configured, bypass the OIDC | ||
| // discovery call — which would time out against the placeholder domain — and redirect | ||
| // straight to the test login endpoint instead. | ||
| if (isPlaceholderAuth0Config) | ||
| { | ||
| ctx.Response.Redirect("/test/login"); | ||
| return; | ||
| } |
Test Results Summary514 tests +2 512 ✅ +1 37s ⏱️ ±0s For more details on these failures, see this check. Results for commit b5bf7e8. ± Comparison against base commit 7107862. ♻️ This comment has been updated with latest results. |
Codecov Report✅ All modified and coverable lines are covered by tests. Additional details and impacted files@@ Coverage Diff @@
## dev #397 +/- ##
=======================================
Coverage 85.46% 85.46%
=======================================
Files 70 70
Lines 1693 1693
Branches 207 207
=======================================
Hits 1447 1447
Misses 166 166
Partials 80 80 🚀 New features to boost your workflow:
|
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
|
Gimli update: added AppHost coverage for /Account/Login in Testing to prove it redirects locally to /test/login (never test.auth0.com), plus an Architecture contract test that pins the placeholder short-circuit before ChallengeAsync while preserving the real Auth0 path. Local validation passed: Architecture.Tests 17/17, AppHost.Tests 56 passed with 1 existing skip, and Release build succeeded. |
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
## Summary - preserve the validated `returnUrl` when placeholder Auth0 config redirects `/Account/Login` to `/test/login` - teach the local test-login endpoint to honor the same safe relative return path - strengthen AppHost and architecture coverage for the fallback redirect contract ## Context - follow-up to merged PR #397 Closes #398 Working as Gandalf (Security / Auth) Co-authored-by: mpaulosky <mpaulosky@users.noreply.github.com> Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Summary
Closes #396
Working as Gandalf (Security Officer)
Problem
In Development/Testing with no real Auth0 credentials configured,
Program.csfalls back to the placeholder domaintest.auth0.com. Navigating to/Account/Loginthen triggers a real OIDC discovery call against that non-existent domain, producingIDX20803: Unable to obtain configuration from … test.auth0.comafter a full timeout.Fix
isPlaceholderAuth0Configflag — set totrueonly when placeholder values are active. Developers with real user-secrets configured are completely unaffected; the flag staysfalseand the Auth0 Universal Login flow is preserved./Account/Loginshort-circuit — when the flag istrue, the endpoint redirects immediately to/test/login(the existing cookie-based test login) instead of issuing aChallengeAsyncto the placeholder domain.existingOnTokenValidatednull guard — addedif (existingOnTokenValidated != null)before invoking the captured delegate (PR #2 audit finding — latentNullReferenceExceptionon every real login when Auth0 SDK leaves the delegate null).Files changed
src/Web/Program.cs/Account/Loginshort-circuitdocs/AUTH0_SETUP.md.squad/agents/gandalf/history.mdSecurity test scenarios for Gimli
GET /Account/LoginwhenisPlaceholderAuth0Config = true→ assert 302 to/test/login, no OIDC challenge issued.GET /Account/Loginwhen real Auth0 credentials are configured → assert OIDC challenge (302 to Auth0 domain),/test/loginNOT called.InvalidOperationExceptionis thrown.GET /test/loginin Development → assert cookie set, redirect to/, user authenticated.GET /test/loginin Production → assert 404 (endpoint not registered).OnTokenValidatedwith null prior handler → assert noNullReferenceException; role claims added normally.Pre-push gates
All 7 gates passed locally (build Release ✅, 420 unit tests ✅, 56 integration/E2E tests ✅).