agentsclimarketplace

Auth flow review

Skill tunahanaliozturk/secure-dotnet-skills/skills/auth-flow-review

Aegis — 12 judgment-style agent skills for secure, production-grade .NET on Azure (security, design, performance, concurrency, observability). Works with Claude Code, Codex, Cursor, Gemini.

Install
npx -y skills add tunahanaliozturk/secure-dotnet-skills --skill auth-flow-review

Assembled from the repository path, not quoted from the project. Check it against their README if it does not work.

One thing to look at

  • 1 stars1 stars. Stars are a popularity signal and not a quality one, but at this level it is likely that nobody has read this closely except its author, and you would be relying on your own review.

What its author says it does

Copied from the file, not written here

Use when reviewing authentication and authorization in an ASP.NET Core app — JWT / OIDC / Entra ID configuration, token validation, and scope/role enforcement.

SKILL.md

11.4 KB, as published. Nobody here has run it

Auth Flow Review

Directs the agent to audit the full authentication and authorization surface of an ASP.NET Core app: token validation completeness, policy granularity, default-deny posture, cookie hygiene, and lifetime/refresh handling — producing a severity-rated finding per gap with the concrete ASP.NET Core API that closes it.

When to use

  • A PR introduces or modifies AddAuthentication, AddJwtBearer, AddMicrosoftIdentityWebApi, cookie auth, or AddAuthorization registrations.
  • New protected endpoints are added and their policy coverage must be verified.
  • An incident or review flag suggests tokens may be accepted without audience, issuer, or lifetime validation.
  • Entra ID / OIDC integration is being wired for the first time or reconfigured.

Process

  1. Identify the auth scheme(s) and their registration sites. Locate every AddAuthentication(…) call and each .Add… scheme attached to it (AddJwtBearer, AddCookie, AddMicrosoftIdentityWebApi, AddOpenIdConnect). Note whether a default scheme is set and whether multiple schemes co-exist (and which is the challenge/forbid scheme).
  2. Verify token validation parameters end-to-end. For JWT bearer: confirm ValidateIssuer, ValidateAudience, ValidateLifetime, and ValidateIssuerSigningKey are all explicitly true in TokenValidationParameters. Confirm Authority / MetadataAddress uses HTTPS and RequireHttpsMetadata is true. For Entra ID (AddMicrosoftIdentityWebApi), confirm the AzureAd section supplies TenantId, ClientId (audience), and the correct Instance.
  3. Check authorization policies and their enforcement. Enumerate every named policy registered in AddAuthorization. For each policy: does it require both authentication (RequireAuthenticatedUser) and a meaningful claim assertion (scope/scp for delegated flows, roles for app-role/app-to-app flows)? Confirm fine-grained endpoints use [Authorize(Policy = "…")] rather than bare [Authorize].
  4. Hunt for gaps: anonymous exposure and missing default-deny. Verify FallbackPolicy is set to RequireAuthenticatedUser in the AddAuthorization options. Flag every [AllowAnonymous] and decide whether it is intentional (health checks, OIDC callbacks) or accidental (admin endpoints). Confirm no controller omits an [Authorize] attribute while the fallback is absent.
  5. Check token lifetime, refresh handling, and clock skew. Confirm ValidateLifetime = true (never false). Confirm ClockSkew is not set to an absurd value (the ASP.NET Core default of 5 minutes is acceptable; anything over 15 minutes is a flag). For cookie auth, verify that the session or sliding expiry matches business requirements and that refresh tokens are rotated on use (not long-lived and non-rotating).
  6. Check cookie auth flags and sign-out correctness. For AddCookie: verify HttpOnly = true, Secure = true, and SameSite is Strict or Lax (never None without Secure). Confirm SignOutAsync clears the auth cookie and — for Entra ID / OIDC — triggers a back-channel or front-channel sign-out so the identity provider session is also terminated.
  7. Output findings with fixes. Rate each gap Critical / High / Medium / Low. Pair each finding with the exact property name or method call to fix it. Re-check the same patterns across all scheme registrations and all protected controllers before closing.

.NET / Azure checks

  • AddAuthentication().AddJwtBearer — validation completeness. In the JwtBearerOptions.TokenValidationParameters block, all four flags must be explicitly true: ValidateIssuer = true, ValidateAudience = true, ValidateLifetime = true, ValidateIssuerSigningKey = true. Setting any of these to false is a deliberate weakening that must be justified in code comments and security sign-off. IssuerSigningKey must be populated from Key Vault or from the JWKS metadata endpoint, never hardcoded.
  • RequireHttpsMetadata and authority hygiene. JwtBearerOptions.RequireHttpsMetadata must be true in any non-development environment. The Authority must be the canonical HTTPS issuer URL (e.g. https://login.microsoftonline.com/{tenantId}/v2.0). Confirm the ValidIssuer or ValidIssuers matches what the identity provider actually puts in the iss claim — a mismatch silently accepts tokens from the wrong tenant.
  • Entra ID via Microsoft.Identity.Web (AddMicrosoftIdentityWebApi). Confirm services.AddAuthentication(JwtBearerDefaults.AuthenticationScheme).AddMicrosoftIdentityWebApi(configuration.GetSection("AzureAd")) is used rather than a hand-rolled AddJwtBearer with hardcoded signing keys. The AzureAd config section must supply Instance, TenantId, and ClientId; Audience should match the Application ID URI. AddMicrosoftIdentityWebApi validates issuer, audience, lifetime, and signing keys through the standard OIDC metadata endpoint. Note: audience validation is only meaningful when AzureAd:Audience (or AzureAd:ClientId) is present in configuration — without it the library cannot enforce which application the token was issued for.
  • Scope (scp) vs app-role (roles) claims — use the right claim and the right API for the flow. Delegated (on-behalf-of-user) tokens carry the scp claim; application (client-credentials / daemon) tokens carry roles. Do not conflate them — a policy that checks scp will silently fail for daemon callers, and vice versa. For app roles, use policy.RequireRole("Orders.Reader") (or [Authorize(Roles = "Orders.Reader")]); under Microsoft.Identity.Web the roles claim is mapped to ClaimTypes.Role, so RequireClaim("roles", "…") can fail against valid tokens. For delegated scopes, use Microsoft.Identity.Web's RequireScope("Orders.Read") policy helper or the [RequiredScope("Orders.Read")] attribute — do not use RequireClaim("scp", "Orders.Read") because the scp claim is a space-delimited string (e.g. "Orders.Read Orders.Write"), so an exact-value RequireClaim will deny tokens that carry additional scopes alongside the required one.
  • Authorization policies and [Authorize(Policy = "…")]. Every endpoint that gates on a specific permission must use [Authorize(Policy = "OrdersRead")] (or equivalent named policy), not bare [Authorize]. Bare [Authorize] only checks that a principal is authenticated — it does not enforce scope or role. Register policies in AddAuthorization(opts => { opts.AddPolicy("OrdersRead", p => p.RequireAuthenticatedUser().RequireScope("Orders.Read")); }) for delegated flows, or p.RequireAuthenticatedUser().RequireRole("Orders.Reader") for app-role flows.
  • Fallback policy = RequireAuthenticatedUser (default-deny). Confirm options.FallbackPolicy = new AuthorizationPolicyBuilder().RequireAuthenticatedUser().Build() (or the equivalent options.FallbackPolicy = options.DefaultPolicy) is set inside AddAuthorization. Without a fallback policy, any controller or minimal-API endpoint that omits [Authorize] is publicly reachable. Opt-out for genuinely public routes should be explicit [AllowAnonymous].
  • ClockSkew and lifetime strictness. The default ClockSkew of TimeSpan.FromMinutes(5) is acceptable for clock drift. Confirm it has not been raised to tens of minutes or TimeSpan.MaxValue to paper over a clock-sync problem. ValidateLifetime = false — even temporarily — means expired tokens are accepted indefinitely; treat this as a Critical finding.
  • Cookie auth: HttpOnly, SecurePolicy, SameSite, and sign-out. In AddCookie(opts => { opts.Cookie.HttpOnly = true; opts.Cookie.SecurePolicy = CookieSecurePolicy.Always; opts.Cookie.SameSite = SameSiteMode.Lax; }). Use Cookie.SecurePolicy = CookieSecurePolicy.Always (the CookieBuilder knob) — not a bare Secure = true boolean — so the middleware enforces HTTPS for the cookie in all environments. SameSite = None without SecurePolicy = Always is rejected by modern browsers and exposes the cookie to cross-site requests. On sign-out, call HttpContext.SignOutAsync(CookieAuthenticationDefaults.AuthenticationScheme) and, for OIDC federated sessions, HttpContext.SignOutAsync(OpenIdConnectDefaults.AuthenticationScheme) to trigger the end-session endpoint.

Red flags

SignalWhy it matters
ValidateAudience = false in TokenValidationParametersAny JWT issued by the same authority for any application is accepted — including tokens issued to other relying parties in the same tenant. A compromised client app's tokens become valid here.
ValidateIssuerSigningKey = falseThe middleware no longer verifies the token's signature. Any syntactically valid JWT, including attacker-crafted ones with arbitrary claims, is accepted as authentic.
RequireHttpsMetadata = false in a non-development environmentThe OIDC metadata endpoint (and the JWKS endpoint it references) is fetched over HTTP. An attacker who can intercept that response can substitute their own signing keys and issue tokens the app accepts.
Bare [Authorize] guarding an admin or elevated-privilege endpoint[Authorize] alone asserts only that the caller is authenticated, not that they hold the required scope or role. Any authenticated user — including low-privilege users — satisfies the check.
No FallbackPolicy in AddAuthorizationEvery controller or minimal-API handler that omits [Authorize] is publicly reachable. Adding a new endpoint without explicitly opting in to authentication silently exposes it.
ValidateLifetime = falseExpired tokens are accepted indefinitely. A stolen token remains valid forever, removing the window-of-opportunity constraint that short-lived tokens are designed to provide.
ClockSkew = TimeSpan.FromHours(1) or largerDramatically extends the validity window of expired tokens. A token valid for 15 minutes becomes valid for over an hour, negating the security benefit of short expiry.
SameSite = SameSiteMode.None without Cookie.SecurePolicy = CookieSecurePolicy.AlwaysThe cookie is sent on cross-site requests (CSRF vector) and the None attribute is rejected by browsers if the Secure flag is not also set, breaking authentication entirely in secure contexts. Use CookieSecurePolicy.Always (not a bare Secure = true boolean) so the middleware enforces HTTPS for the cookie.
Checking scp claim for a daemon / client-credentials flowDaemon tokens (issued via client-credentials grant) carry roles, not scp. A policy checking scp will fail open or deny all daemon callers depending on the fallback, masking the authorization gap.
[AllowAnonymous] on an endpoint that mutates privileged stateBypasses all authorization middleware including the fallback policy. Even if the intent is deliberate, it must be code-reviewed and documented — a mis-applied attribute here is a full auth bypass.

Example

See examples/auth-flow-review/.

Related skills

  • dotnet-security-review — use for a full security review covering injection, crypto, deserialization, and secrets beyond auth.
  • api-contract-review — use to review endpoint contracts including authorization requirements and error responses.

Keep looking

Skills are one crate of 328,083. Ordering is by how many stacks a row turns up in, so the top of any crate is what has actually been picked rather than what has the most stars.