| name | auth-patterns |
| description | Apply project authentication and authorization rules for BFF token handling, JWT validation, claim extraction, and handler-level access checks. |
| type | pattern |
| enforcement | suggest |
| priority | critical |
Purpose
Use this skill for browser-to-BFF-to-API authentication flow, endpoint protection defaults, claim extraction, and resource authorization behavior. It keeps security logic consistent across controllers, handlers, and HAL-driven clients.
When to Load
- Keywords: auth, JWT, OIDC, Keycloak, authorize, claim, audience, authorized party, cookie forwarding.
- File patterns:
*Controller.cs, *Program.cs, **/Authorization/**/*.cs, Explore.API/**/*.cs, Explore.Blazor/**/*.cs.
- Intent IDs:
add-write-endpoint, add-get-endpoint, add-hal-link, blazor-component-affordance.
When NOT to Load
Must-Read Docs
Top 5 Invariants
- The browser never sees tokens because the BFF stores them in HttpOnly cookies and forwards a
Bearer token to the API.
- User ID extraction follows
sub then nameidentifier then sid, and a missing user identifier yields 401 Unauthorized.
- JWT validation checks both
aud and azp with a five-minute clock skew, and authorized audiences include islamu-event-api and islamu-event-blazor.
- Endpoint defaults are
GET with [AllowAnonymous], writes with [Authorize], and admin operations with [Authorize(Roles = "Admin")], while ownership checks stay in handlers through IAuthorizedRequest, [AuthorizeResource], or ISecureRequest.
- HATEOAS authorization follows the Candidate, Normalize, Batch, and Materialize pipeline and fails closed, making
_links the only client-side source of truth for action gating.
Top 5 Anti-Patterns
- Storing tokens in
localStorage or sessionStorage bypasses the BFF boundary and weakens browser-side security.
- Gating UI actions with role or claim inspection instead of HAL
_links drifts from the server authorization contract.
- Logging raw JWTs leaks secrets into traces, logs, and support artifacts.
- Disabling the
Tenant query filter during runtime request handling creates cross-tenant authorization and data-isolation bugs.
- Validating only
aud or only azp allows unauthorized clients to present otherwise valid tokens.
Minimal Examples
public static class ClaimsPrincipalExtensions
{
public static string GetRequiredUserId(this ClaimsPrincipal user)
{
string? userId = user.FindFirstValue("sub")
?? user.FindFirstValue(ClaimTypes.NameIdentifier)
?? user.FindFirstValue("sid");
return string.IsNullOrWhiteSpace(userId)
? throw new UnauthorizedAccessException("Missing user id claim.")
: userId;
}
}
public sealed record UpdateEventCommand(Guid EventId, string Title)
: IAuthorizedRequest<BaseCommandResponse<Guid>>;
public sealed class UpdateEventHandler(IEventRepository repository)
{
public async Task<BaseCommandResponse<Guid>> Handle(
UpdateEventCommand request,
CancellationToken cancellationToken)
{
Event entity = await repository.GetRequiredAsync(request.EventId, cancellationToken);
entity.Rename(request.Title);
await repository.UpdateAsync(entity, cancellationToken);
return new BaseCommandResponse<Guid>(entity.Id, true, "Updated");
}
}
Verification Hooks
dotnet test --project Event.Architecture.Tests/Event.Architecture.Tests.csproj --configuration Release --verbosity quiet --filter FullyQualifiedName~Event.Architecture.Tests.AuthorizationParityTests
dotnet test --project Event.API.IntegrationTests/Event.API.IntegrationTests.csproj --configuration Release --verbosity quiet
dotnet build --configuration Release --verbosity quiet
Related Skills