Concierge.Auth.Client.AuthGuard
1.0.0
See the version list below for details.
dotnet add package Concierge.Auth.Client.AuthGuard --version 1.0.0
NuGet\Install-Package Concierge.Auth.Client.AuthGuard -Version 1.0.0
<PackageReference Include="Concierge.Auth.Client.AuthGuard" Version="1.0.0" />
<PackageVersion Include="Concierge.Auth.Client.AuthGuard" Version="1.0.0" />
<PackageReference Include="Concierge.Auth.Client.AuthGuard" />
paket add Concierge.Auth.Client.AuthGuard --version 1.0.0
#r "nuget: Concierge.Auth.Client.AuthGuard, 1.0.0"
#:package Concierge.Auth.Client.AuthGuard@1.0.0
#addin nuget:?package=Concierge.Auth.Client.AuthGuard&version=1.0.0
#tool nuget:?package=Concierge.Auth.Client.AuthGuard&version=1.0.0
Concierge.Auth.Client.AuthGuard
Verifies AuthService-issued human JWTs on incoming requests (contract §5) and WebSocket
handshakes (§7). The only Concierge.Auth.Client.* package that references ASP.NET Core
(FrameworkReference, never a Microsoft.AspNetCore.* PackageReference) or StackExchange.Redis.
The two-step check, in this exact order (contract §5)
- Signature/JWKS. RS256 only, via
Concierge.Auth.Client.Keys'sIJwksKeyProvider— this package never fetches JWKS itself.ClockSkewis hardcoded to 5 seconds (contract §5 item 3); ASP.NET's own default is 5 minutes, and that default is never reachable here — there is no option to change it.JsonWebTokenHandler, not the legacyJwtSecurityTokenHandler. Issuer and audience validation are off, because the token carries noiss/audclaim to validate against (contract §3.2: exactly{sub, jti, iat, exp}) — there is nothing to check. jtideny-list — MANDATORY, and only ever reached if step 1 already succeeded.EXISTS denylist:jti:<jti>inredis-cache(contract §3.4). Present → rejected. There is no in-memory fallback and no option to skip this check. If Redis is unreachable at request time, the request is rejected (fail closed) — never treated as "not denied". An in-memory alternative across multiple instances would silently fail to log a user out everywhere but one instance, which is worse than no deny-list at all because it looks like it works.
On success, exactly { userId, jti } (ConciergeRequestContext) is attached — never roles,
scopes, permissions, tenant, email, or displayName. That is PolicyService's data, resolved
separately from the cached assignment set (contract §3.2; integration-guide invariant 11). This
package has no authorization opinion and no PolicyService dependency.
Install and register
services.AddConciergeAuthClient(configuration, db => db.UseNpgsql(cs, npgsql =>
npgsql.MigrationsHistoryTable("__EFMigrationsHistory", "concierge"))); // base package — required first
services.AddConciergeAuthClientKeys(configuration); // Keys package — supplies IJwksKeyProvider
services.AddConciergeAuthTokenValidation(configuration); // binds "Concierge:AuthClient:TokenValidation"
Fails closed at this call, synchronously (decision D8): if
ConciergeTokenValidationOptions.RedisConnectionString is not configured,
AddConciergeAuthTokenValidation throws OptionsValidationException immediately — an operator
sees the misconfiguration at registration time, not on the first request that needed the
deny-list. There is no way to configure this package without a deny-list store.
// appsettings.json
{
"Concierge": {
"AuthClient": {
"TokenValidation": {
"RedisConnectionString": "localhost:6379",
"RedisDatabase": 0
}
}
}
}
HTTP middleware
app.UseConciergeAuthentication();
Rejects with 401 on a missing/malformed Authorization: Bearer <token> header, an invalid
token, or a deny-listed jti. On success, attaches the context; read it downstream with:
ConciergeRequestContext? ctx = httpContext.GetConciergeRequestContext();
This middleware authenticates unconditionally wherever it is registered — it has no per-route
opt-out mechanism. Scope it to the routes that need it with app.Map(...)/branching if some
routes must stay public.
WebSocket handshake (contract §7)
The token arrives explicitly in the handshake payload (auth: { token }), not a header — extract
it with whatever WebSocket library the host uses, then:
var result = await webSocketAuthenticator.AuthenticateHandshakeAsync(token, cancellationToken);
if (result.IsFailure)
{
// reject the handshake — same failure semantics as the HTTP path
}
// after accepting the socket:
await webSocketAuthenticator.RunUntilExpiryAsync(socket, result.Value.ExpiresAt, cancellationToken);
RunUntilExpiryAsync closes the socket once the token's exp passes — a long-lived connection
must not outlive the credential that opened it.
Failure codes
TOKEN_INVALID— malformed, wrong key, wrong/disallowed algorithm (includingnoneand HS256), or expired/not-yet-valid (outside the 5s clock skew).TOKEN_DENIED— signature was valid, but thejtiis on the deny-list.DENYLIST_UNAVAILABLE— the deny-list store could not be reached; the request is rejected.
Out of scope
Any authorization decision, any PolicyService call, profile operations, credential rotation (see
Concierge.Auth.Client.Secrets), JWKS fetching (see Concierge.Auth.Client.Keys).
| Product | Versions Compatible and additional computed target framework versions. |
|---|---|
| .NET | net10.0 is compatible. net10.0-android was computed. net10.0-browser was computed. net10.0-ios was computed. net10.0-maccatalyst was computed. net10.0-macos was computed. net10.0-tvos was computed. net10.0-windows was computed. |
-
net10.0
- Concierge.Auth.Client (>= 1.0.0)
- Concierge.Auth.Client.Keys (>= 1.0.0)
- Microsoft.IdentityModel.JsonWebTokens (>= 8.22.0)
- Microsoft.IdentityModel.Tokens (>= 8.22.0)
- StackExchange.Redis (>= 2.8.22)
NuGet packages
This package is not used by any NuGet packages.
GitHub repositories
This package is not used by any popular GitHub repositories.