ASP.NET Core / Editorial solution guide
Diagnose 401,403, and Unexpected Authentication Outcomes
Trace the selected scheme, trusted principal, policy, resource rule, and HTTP result instead of treating every denial as a login failure.
Read-only knowledge base · No public comments
Recognize the symptoms
A protected API returns 401,403, or an unexpected login-page response, or an authenticated caller can access another user’s resource. These are different failure modes. Cookie behavior for known API endpoints changed in ASP.NET Core 10, so old advice about automatic redirects must not be applied universally. A valid token or cookie does not by itself grant resource authority.
Understand the likely causes
- The wrong/default scheme, missing middleware, untrusted claims, claim-type mapping, omitted authorization metadata, or an incomplete tenant/owner rule can be responsible. CORS is not authorization, and a ClaimsPrincipal constructed by a test does not validate any credential. Distinguish challenge, forbid, and application disclosure policy before widening access or changing status codes.
1. Identify the actual request and selected scheme
Record endpoint, framework version, auth scheme, credential type, and safe result context. Do not paste real bearer tokens, cookies, keys, or full identity payloads into logs or examples. Determine whether the request is anonymous, failed authentication, or authenticated but missing authority. A developer’s successful direct method call supplies none of this evidence.
2. Verify the authentication trust boundary
Review supported handler registration, default/explicit scheme selection, issuer/audience/lifetime/signature requirements, and relevant proxy/TLS configuration. Decoding token JSON is not validation. For cookies, inspect current endpoint/API metadata behavior and protection-key assumptions. Do not make every denial 200 or disable trust checks to make an integration example pass.
3. Isolate the policy with explicit synthetic principals
The complete program evaluates one real authorization policy against anonymous, authenticated-without-claim, and permitted fixtures. It confirms the policy’s requirements but not token validity or HTTP 401/403 mapping. That narrow test helps distinguish policy logic from scheme/pipeline behavior without contacting an identity provider or starting a server.
using System;
using System.Security.Claims;
using Microsoft.AspNetCore.Authorization;
using Microsoft.Extensions.DependencyInjection;
var services = new ServiceCollection();
services.AddLogging();
services.AddAuthorization(options => options.AddPolicy("Read", policy => policy.RequireAuthenticatedUser().RequireClaim("scope", "reports.read")));
using var provider = services.BuildServiceProvider();
var authorization = provider.GetRequiredService<IAuthorizationService>();
var anonymous = new ClaimsPrincipal(new ClaimsIdentity());
var missing = new ClaimsPrincipal(new ClaimsIdentity(authenticationType: "synthetic"));
var reader = new ClaimsPrincipal(new ClaimsIdentity(new[] { new Claim("scope", "reports.read") }, "synthetic"));
bool a = (await authorization.AuthorizeAsync(anonymous, null, "Read")).Succeeded;
bool m = (await authorization.AuthorizeAsync(missing, null, "Read")).Succeeded;
bool r = (await authorization.AuthorizeAsync(reader, null, "Read")).Succeeded;
Console.WriteLine($"anonymous={a} missingClaim={m} reader={r}");
anonymous=False missingClaim=False reader=True
Only the synthetic authenticated reader satisfies this policy; no credential or HTTP authentication flow executes.
4. Check policy attachment and resource ownership
Confirm the endpoint actually requires the policy and that fallback/anonymous metadata is intentional. Then check trusted tenant, owner, operation, and current resource state. A broad read claim does not authorize every report. If state can change before mutation, review transaction/concurrency handling too. Do not trust a tenant header or route ID as proof of authority.
5. Verify the configured HTTP behavior and safe errors
Challenge, forbid, redirect, not-found disclosure, and structured error output depend on the intended scheme/endpoint contract. Test them through a supported isolated request pipeline rather than inferring them from AuthorizationResult.Succeeded. Include malformed, expired, wrong-audience, missing-claim, cross-tenant, and permitted cases. Keep internal cause in controlled diagnostics and public details nonsecret.
Verify the fix
The local program must print anonymous=False missingClaim=False reader=True. For a real fix, verify authentication, policy selection, ownership, middleware order, and HTTP outcomes in the intended isolated application setup. A passing synthetic policy does not prove token trust, CORS/CSRF safety, endpoint protection, or deployment-key continuity. Record the exact layer tested and preserve negative cases.
Keep it from recurring
- Keep scheme/policy/resource maps, trusted claim contracts, explicit anonymous exceptions, and tenant-isolation tests. Review framework-version auth changes and deployment key/proxy settings. Separate browser-origin behavior from server authority, and never turn tests’ handcrafted principals into production identity construction.