Time Of Check Time Of Use in Aspnet with Bearer Tokens
Time Of Check Time Of Use in Aspnet with Bearer Tokens — how this specific combination creates or exposes the vulnerability
Time Of Check Time Of Use (TOCTOU) is a class of race condition where the state of a resource is checked at one point in time and then used at a later point, but the state can change between the check and the use. In an ASP.NET application using Bearer Tokens for authentication, this typically occurs when authorization decisions are made based on claims or roles resolved at an earlier step and then relied upon later in the request pipeline without revalidation.
Consider an endpoint that first validates the Bearer Token and extracts a claim such as role or scope to perform an access check (the check). Later, the code uses that claim to decide whether to allow an action (the use). If an attacker can mutate the resource or the effective permissions between the check and the use—such as by revoking or swapping the token’s claims via token replacement, a side-channel, or a poorly cached identity—privilege escalation or unauthorized access can occur.
With Bearer Tokens, this risk is amplified when tokens are accepted without strict validation on every sensitive operation. For example, if an API validates a token once and caches the identity or role in an ambiguous context (e.g., per-request caching or thread-local state), and later parts of the code assume the cached data is authoritative, an attacker who can influence the token or the identity resolution may be able to bypass intended authorization boundaries.
ASP.NET’s default authentication mechanisms, such as JwtBearerEvents, can inadvertently contribute to TOCTOU when custom logic in events like OnTokenValidated sets HttpContext.User based on claims extracted once, and subsequent authorization code uses that cached principal without re-evaluating the latest token state or re-validating scopes.
Real-world patterns that can lead to authorization bypass include: resolving permissions at the controller action level after earlier middleware has already set user identity, using role-based checks early and then performing sensitive operations later without confirming current token validity, or failing to re-validate token scope for operations that require elevated privileges. These patterns are relevant to the BOLA/IDOR and Privilege Escalation checks that middleBrick runs in parallel, as they test whether authorization is re-evaluated on each operation rather than relying on initial checks.
Proper mitigation requires treating each sensitive operation as independent: validate the Bearer Token on every request, avoid relying on cached or early-resolved authorization claims, and ensure that authorization logic re-checks permissions immediately before performing the action. middleBrick’s LLM/AI Security and BOLA/IDOR checks help surface these timing and authorization gaps by correlating runtime behavior with spec-defined security expectations.
Bearer Tokens-Specific Remediation in Aspnet — concrete code fixes
Remediation focuses on ensuring that authorization decisions are made close to the operation and always re-validate the Bearer Token and its claims. Avoid caching user or role information in a way that can become stale within the request lifecycle. The following examples demonstrate secure patterns for ASP.NET Core using Bearer Tokens.
1. Always validate and build identity per request
Do not rely on an identity set earlier in the pipeline. Re-validate or re-construct principal immediately before authorization checks.
// Startup or Program.cs: ensure token validation is strict
builder.Services.AddAuthentication(JwtBearerDefaults.AuthenticationScheme)
.AddJwtBearer(options =>
{
options.Authority = "https://your-auth-provider";
options.Audience = "your-api-audience";
options.TokenValidationParameters = new TokenValidationParameters
{
ValidateIssuer = true,
ValidateAudience = true,
ValidateLifetime = true,
ValidateIssuerSigningKey = true
};
});
// In a minimal API or controller, re-fetch or re-validate when needed
app.MapGet("/resource/{id}", (HttpContext context, string id) =>
{
// Force re-validation or re-extract claims if needed
var principal = context.User; // current user from validated token
if (!UserHasAccess(principal, id))
{
Results.Forbid();
return;
}
// proceed safely
return Results.Ok();
});
2. Avoid early role/claim caching; re-check before use
Do not store resolved roles or permissions in a variable early in the request and reuse them later. Instead, check claims directly at the point of use.
// Instead of this:
// var role = User.FindFirst(ClaimTypes.Role)?.Value;
// if (role == "Admin") { ... }
// Do this at the point of action:
app.MapDelete("/resource/{id}", (HttpContext context, string id) =>
{
var hasAdminClaim = context.User.HasClaim(c =>
c.Type == ClaimTypes.Role && c.Value == "Admin");
if (!hasAdminClaim)
{
Results.Forbid();
return;
}
// perform deletion
return Results.NoContent();
});
3. Use policy-based authorization with fresh requirements
Define authorization policies that re-evaluate claims on each invocation and use IAuthorizationService to enforce them immediately before sensitive operations.
// Policy setup
builder.Services.AddAuthorizationBuilder()
.AddPolicy("DeleteResource", policy =>
policy.RequireAssertion(context =>
{
// This runs at authorization time with current principal
return context.User.HasClaim(ClaimTypes.Role, "Admin")
&& HasResourceOwnership(context.User, resourceId);
}));
// In endpoint
app.MapDelete("/resource/{id}", async (HttpContext context, IAuthorizationService auth, string id) =>
{
var result = await auth.AuthorizeAsync(context.User, id, "DeleteResource");
if (!result.Succeeded)
{
Results.Forbid();
return;
}
// safe to proceed
return Results.NoContent();
});
4. Validate token on each use for sensitive operations
For high-risk actions, explicitly validate the token again or re-query the identity provider if your threat model requires it, rather than relying on the already validated context.
// Example of explicit re-validation before critical action
app.MapPost("/transfer", async (HttpContext context, ITokenValidator validator) =>
{
var token = context.Request.Headers["Authorization"].ToString()?.Replace("Bearer ", "");
if (string.IsNullOrEmpty(token))
{
Results.Unauthorized();
return;
}
var validationResult = await validator.ValidateTokenAsync(token);
if (!validationResult.IsValid || !HasScope(validationResult.Claims, "transfer"))
{
Results.Forbid();
return;
}
// proceed with transfer
return Results.Ok();
});
These patterns ensure that authorization checks are tightly coupled with the operation and not dependent on stale or early-resolved identity states. By re-validating Bearer Token claims at the point of use and avoiding premature caching, you reduce the window for race conditions that could lead to privilege escalation or unauthorized access.
middleBrick’s CLI and GitHub Action can help enforce that sensitive endpoints re-validate tokens and do not rely on cached permissions by scanning your API definitions and runtime behavior for missing re-validation steps.