Audis.Authentication.Jwt
2.0.0
dotnet add package Audis.Authentication.Jwt --version 2.0.0
NuGet\Install-Package Audis.Authentication.Jwt -Version 2.0.0
<PackageReference Include="Audis.Authentication.Jwt" Version="2.0.0" />
<PackageVersion Include="Audis.Authentication.Jwt" Version="2.0.0" />
<PackageReference Include="Audis.Authentication.Jwt" />
paket add Audis.Authentication.Jwt --version 2.0.0
#r "nuget: Audis.Authentication.Jwt, 2.0.0"
#:package Audis.Authentication.Jwt@2.0.0
#addin nuget:?package=Audis.Authentication.Jwt&version=2.0.0
#tool nuget:?package=Audis.Authentication.Jwt&version=2.0.0
Audis.Authentication.Jwt
JWT-Mechanismus für Audis.Authentication. Policies, Modi und Rechte pro Client sind im Paket Audis.Authentication beschrieben.
Generische JWT-Validierung nach dem AUDIS-Token-Contract für Tokens, die eine Host-Anwendung ausstellt, in die AUDIS eingebettet ist, und die SPA als Authorization: Bearer <jwt> bzw. bei SignalR als access_token-Query sendet. Signaturprüfung und Token-Parsing übernehmen JsonWebTokenHandler und Microsoft.IdentityModel (keine eigene Signatur-Krypto, kein eigener JWT-Parser). Der Ablauf im Handler (Aussteller-Auswahl, Key-Cache, Audience, Scope-Claims) und die Prüfung der Public Keys (PEM, RSA-Größe und -Exponent, P-256-Kurvenpunkt) sind eigene Implementierungen. AUDIS-spezifische Teile (Audience audis:<tenantId>, Mapping von Mode und Knowledge) bleiben in der Anwendung.
builder.Services.AddAudisAuthentication(builder.Configuration)
.AddJwt()
.AddJwtAudienceResolver<TenantRouteAudienceResolver>(); // optional, Default: konfigurierte Audiences
app.MapGroup("/api/interrogation/{tenantId}")
.RequireAudisAuth(AudisAuthPolicies.Browser);
// Claims lesen (Werte nie loggen)
var mode = AudisJwtClaims.GetMode(httpContext.User); // "audis:mode"
var knowledge = AudisJwtClaims.GetKnowledgeClaims(httpContext.User); // "audis:knowledge.<id>" -> id/Wert
// Audience pro Request, z.B. aus der Route
internal sealed class TenantRouteAudienceResolver : IJwtAudienceResolver
{
public ValueTask<IReadOnlyCollection<string>> GetAudiencesAsync(JwtAudienceContext context, CancellationToken cancellationToken) =>
ValueTask.FromResult<IReadOnlyCollection<string>>(
context.HttpContext.GetRouteValue("tenantId") is string tenantId ? [$"audis:{tenantId}"] : []);
}
Konfiguration (appsettings.json bzw. Key Vault):
"Authentication": {
"Inbound": {
"Jwt": {
"Issuers": {
"mpad": {
"Issuer": "https://mpad.example.at", // erwarteter iss, eindeutig über alle Aussteller
"SigningKeys": [ // primär: Public Keys (PEM) aus Key Vault/Konfiguration
{ "KeyId": "mpad-2026-1", "PublicKeyPem": "-----BEGIN PUBLIC KEY-----\n..." },
{ "KeyId": "mpad-2026-2", "PublicKeyPem": "..." } // Rotation: mehrere Keys, Auswahl per kid
],
// "JwksUrl": "https://mpad.example.at/.well-known/jwks.json", // alternativ zu SigningKeys, nur https
"MaxTokenLifetimeMinutes": 120, // Default 60, erlaubt 1..1440
"ClockSkewSeconds": 60, // Default 60, erlaubt 0..300
// "AllowedTokenTypes": [ "at+jwt" ], // erlaubte typ-Header, Default at+jwt (RFC 9068)
"Audiences": [ ], // optional, übersteuert die Audiences des Mechanismus
"Policies": [ "Browser" ], // optional, Rechte aller Tokens dieses Ausstellers
"Tenants": [ ] // optional, leer = alle Mandanten
}
},
"Audiences": [ "audis:tenant-a" ], // nur für den Default-Audience-Resolver
"QueryTokenPaths": [ "/api/hub" ], // access_token-Query nur unter diesen Pfaden
"KeyRefreshIntervalMinutes": 60, // Default 60, erlaubt 1..1440
"UnknownKeyIdRefreshIntervalSeconds": 30 // Default 30, erlaubt 1..3600
},
"Policies": {
"Browser": { "Schemes": [ "Jwt" ], "Mode": "Enforce" } // Default: Disabled
}
}
}
Token-Contract (vom Paket erzwungen):
| Aspekt | Vorgabe |
|---|---|
| Signatur | nur RS256 oder ES256 (Allowlist). none, HS*, RS384, PS256 usw. werden abgewiesen. Symmetrische Keys werden nie verwendet, auch nicht aus JWKS oder einer eigenen Key-Quelle. RSA-Keys brauchen mindestens 2048 Bit, EC-Keys die Kurve P-256. |
typ (Header) |
Pflicht, muss (ohne Groß-/Kleinschreibung, application/-Präfix optional) einem Wert aus AllowedTokenTypes des Ausstellers entsprechen, Default at+jwt (JWT Access Token nach RFC 9068). Fehlt typ oder ist er z.B. JWT, wird das Token abgewiesen. Damit werden ID-Tokens oder andere JWTs, die der Aussteller mit denselben Keys signiert, nie als Access Token akzeptiert. Der Aussteller muss typ: at+jwt setzen (oder einen anderen Wert, der in AllowedTokenTypes konfiguriert ist). |
iss |
Pflicht, muss exakt (Groß-/Kleinschreibung) dem Issuer eines konfigurierten Ausstellers entsprechen. Nur dessen Keys werden verwendet. |
aud |
Pflicht, mindestens ein Wert muss einer Audience des IJwtAudienceResolver für den aktuellen Request entsprechen. |
exp, iat |
Pflicht. nbf wird beachtet, falls vorhanden. ClockSkewSeconds gilt für exp, nbf und iat. Alle drei werden gegen dieselbe Uhr geprüft, den registrierten TimeProvider (Default TimeProvider.System). |
| Lebensdauer | exp - iat ≤ MaxTokenLifetimeMinutes. iat darf nicht in der Zukunft liegen (sonst ließe sich die Obergrenze umgehen). |
| Claims | audis:mode und audis:knowledge.<id> werden unverändert übernommen (MapInboundClaims = false), AudisJwtClaims liest sie. Mapping und Whitelist sind Sache der Anwendung. audis:service, audis:policy und audis:tenant aus dem Token werden entfernt und durch den Namen des Ausstellers (audis:service, z.B. im Audit-Log EventId 1006) sowie seine Policies/Tenants ersetzt (Client-Rechte wie bei ApiKey und Hmac): Ein Token kann seine Rechte nicht selbst erweitern. Das gilt auch für einen in OnTokenValidated ersetzten Principal (mit oder ohne Success()): Jede authentifizierte Identity des endgültigen Principals erhält diese Claims. |
Ablauf beim Empfang (Scheme AudisJwt):
- Ohne Token liefert das Scheme kein Ergebnis, andere Schemes der Policy (z.B.
ApiKey) können den Request authentifizieren. ImAudit-Modus wird nur gewarnt. - Das Token kommt aus dem
Authorization: Bearer-Header. Nur wenn keinAuthorization-Header vorhanden ist und der Pfad unter einemQueryTokenPaths-Präfix liegt (segmentweise, z.B./api/hub), wird genau einaccess_token-Query-Wert verwendet. - Aussteller anhand von
issauswählen, Algorithmus prüfen, Keys des Ausstellers laden (Cache), Signatur,iss,exp/nbfmitJsonWebTokenHandlerund einer pro Request neu erstelltenTokenValidationParameters-Instanz prüfen. - Contract-Prüfungen:
audundiatvorhanden,iatnicht in der Zukunft, Lebensdauer-Obergrenze. - Audience:
IJwtAudienceResolverliefert die für diesen Request erwarteten Audiences. Der Default-Resolver liest sie aus demselben Konfigurationsstand wie Aussteller und Keys, auch wenn während des Key-Ladens ein Reload erfolgt. Die Prüfung erfolgt anhand des validierten Tokens (nicht des Principals) vorOnTokenValidated: Anwendungs-Events laufen nie für ein Token einer fremden Audience, ein inOnTokenValidatedersetzter Principal beeinflusst die Prüfung nicht. Abgewiesene Tokens lösenOnAuthenticationFailedaus. Liefert der Resolver keine Audience, wird das Token abgewiesen (fail closed, Warnung EventId 3007). Wirft der Resolver eine Exception (z.B. fehlgeschlagener Tenant-Lookup), wird das Token mit401statt500abgewiesen (Fehler EventId 3009, nur Exception-Typ); nur bei abgebrochenem Request wird die Cancellation weitergereicht. - Ungültige Tokens:
401mitWWW-Authenticate: Bearerohne Fehlerdetails.
Schlüssel:
- Quellen:
SigningKeys(PEMPUBLIC KEYmit RSA oder EC P-256, bzw.RSA PUBLIC KEY),JwksUrloder eigeneIJwtSigningKeySource-Implementierungen (AddJwtSigningKeySource<T>(), z.B. später ein Key-Vault-Key in einem eigenen Paket). Eigene Quellen werden zuerst gefragt, die erste mitSupports(issuer) == trueliefert die Keys. Keys mit privatem Anteil (privater RSA-/EC-Key, Zertifikat mit Private Key) werden aus jeder Quelle verworfen (Warnung EventId 3004). Eigene Quellen undIJwtAudienceResolvererhalten die aktuellen Aussteller-Optionen (JwtIssuerContext.Options) und dürfen sie nicht verändern: Änderungen würden bis zum nächsten Reload für alle Requests gelten. - Cache pro Replica: Nur Public Keys, jederzeit aus der Quelle neu aufbaubar. Nach
KeyRefreshIntervalMinuteswird im Hintergrund neu geladen (höchstens ein Ladevorgang pro Aussteller), Requests verwenden bis dahin die vorhandenen Keys und warten nicht auf einen langsamen JWKS-Host. Gewartet wird nur beim ersten Laden (noch keine Keys) und beim rate-limitierten Nachladen für einen unbekanntenkid. Neu geladen wird nachKeyRefreshIntervalMinutes, nach einer Änderung der Key-Quelle (andereJwksUrl, geänderteSigningKeys, geänderterIssuer; andere Konfigurationsänderungen behalten den Cache) und bei einem unbekanntenkid, dann höchstens einmal proUnknownKeyIdRefreshIntervalSecondsund Aussteller (Schutz der Quelle vor Tokens mit beliebigenkids). Tokens ohnekidlösen nie ein Nachladen aus. Wartet ein Request auf Keys, endet das Warten mit dem Abbruch des Requests. Das Laden selbst läuft weiter und steht danach allen Requests zur Verfügung: Ein Client kann es durch Abbruch weder verhindern noch erneut auslösen. Eigene Key-Quellen müssen die Dauer eines Ladevorgangs selbst begrenzen (die JWKS-Quelle nach 10 s). Schlägt das Laden fehl, bleiben die zuletzt geladenen Keys derselben Key-Quelle aktiv, Keys einer vorherigen Quelle werden nie weiterverwendet (Warnung EventId 3002, nur Exception-Typ). Liefert die Quelle erfolgreich, aber ohne unterstützten Key (z.B. leeres JWKS, nuruse: encoder unbrauchbare Keys wie ein RSA-Exponent kleiner 3 bzw. gerade oder ein EC-Punkt, der nicht auf P-256 liegt), gilt das ebenfalls als Fehlschlag (Warnung EventId 3010). In beiden Fällen wird nachUnknownKeyIdRefreshIntervalSecondserneut geladen. Ein per Reload geändertesKeyRefreshIntervalMinutesgilt sofort, auch für bereits geladene Keys. - JWKS: Download über den
HttpClientAudis.Authentication.Jwt.Jwks(Timeout 10 s, max. 512 KB, keine Redirects).httpist nur für Loopback-Hosts mitAllowInsecureLoopbackJwksUrl: trueerlaubt. Keys mit privatem Anteil,use≠sigoderktyoctwerden ignoriert (Warnung EventId 3004). - Rotation: Neuen Public Key mit neuem
KeyIdzusätzlich eintragen, Aussteller auf den neuen Key umstellen, alten Key nach Ablauf der maximalen Token-Lebensdauer entfernen. Konfigurations-Reloads (Key Vault) wirken ohne Neustart. Ein aus einem JWKS entfernter Key wird pro Replica bis zum nächsten Refresh weiter akzeptiert (höchstensKeyRefreshIntervalMinutes). Für einen sofortigen WiderrufKeyRefreshIntervalMinutesper Reload kurzzeitig senken (gilt sofort, auch für bereits geladene Keys). Ist die Konfiguration nach einem Reload ungültig, bleibt die letzte gültige aktiv (Fehler EventId 1002 bzw. 1003, ohne Key-Material, siehe Betrieb).
Validierung (Start und Reload): fehlender oder doppelter Issuer, keine oder zwei Key-Quellen, SigningKeys ohne oder mit doppeltem KeyId, PEM mit privatem Schlüssel, Zertifikat oder ungültigem Inhalt, RSA < 2048 Bit, EC-Kurve ≠ P-256, JwksUrl nicht absolut oder ohne https, Werte außerhalb der erlaubten Bereiche, leere AllowedTokenTypes bzw. leere Werte darin, ungültige QueryTokenPaths (z.B. /; ein abschließendes / wie in /api/hub/ ist erlaubt und wird ignoriert), keine Audience bei Default-Resolver, unbekannte Policies oder ungültige Tenants eines Ausstellers sowie eine aktive Policy mit Jwt ohne Aussteller verhindern den Start. Bei einem Reload der Policies (z.B. Umstellung von ApiKey auf Jwt) werden Aussteller und JWT-Settings desselben Reloads mitgeprüft: Ohne Aussteller oder mit ungültigen Settings (z.B. ungültigem PEM) wird der Reload nicht übernommen, die zuletzt gültigen Policies bleiben aktiv. Fehlermeldungen enthalten nie Key-Material.
Betrieb (Konfigurations-Reloads, z.B. Key Vault):
- Reload ganz oder gar nicht: Ein Reload wird nur übernommen, wenn die gesamte Konfiguration gültig ist. Enthält sie irgendeinen Fehler, auch bei einem anderen Aussteller oder einer Audience, bleibt die zuletzt gültige Konfiguration vollständig aktiv. Dann gelten auch entfernte bzw. widerrufene Keys und Aussteller weiter. Das ist bewusst so: Ein Tippfehler soll nicht alle Tokens abweisen (Totalausfall der Leitstelle). Geloggt wird auf
Errormit EventId 1002 (Validierungsfehler) bzw. 1003 (Konfiguration nicht bindbar, nur Exception-Typ), jeweils mit dem Hinweis, dass keine Änderung dieses Reloads wirksam ist. - Vor dem Rollout validieren: Konfigurationsänderungen (insbesondere in Key Vault) vor dem Ausrollen prüfen, z.B. durch Start einer Instanz mit der neuen Konfiguration in Staging.
- Key-Widerruf in den Logs verifizieren: Nach dem Entfernen eines kompromittierten Keys oder Ausstellers auf allen Replicas prüfen, dass kein EventId 1002/1003 geloggt wurde. Empfehlung: Alert auf diese EventIds.
- Neustart: Mit ungültiger Konfiguration schlägt der Start fehl (
ValidateOnStart). Eine neu gestartete Replica läuft also nie mit einer alten Konfiguration weiter. - Replicas: Jede Replica lädt die Konfiguration unabhängig (Key-Vault-Refresh-Intervall, File Watcher). Bis alle Replicas neu geladen haben, kann ein entfernter Key auf einem Teil der Replicas noch gelten. Für einen sofortigen Widerruf die Replicas neu starten (Rolling Restart).
Sicherheitshinweise:
- Statelessness: Validierung allein aus Token und Public Key. Kein serverseitiger Token-Cache, keine Session,
SaveToken = false. Jede Replica kann jeden Request validieren. - Logging: Token,
access_token-Wert und Claim-Werte werden vom Paket nie geloggt. Fehlermeldungen sind feste Texte, IdentityModel-Meldungen (die beiIdentityModelEventSource.ShowPII = truedas Token enthalten können) werden nicht weitergereicht. Abgewiesene Tokens werden mit Grund aufDebuggeloggt (EventId 3006). - Request-Logs des Hosts: ASP.NET Core loggt in
Microsoft.AspNetCore.Hosting.Diagnostics(Information, „Request starting/finished“) die vollständige URL inklusiveaccess_token-Query. Das liegt außerhalb des Pakets: Die Anwendung muss diese Kategorie aufWarningsetzen oder die Query maskieren, ebenso beiHttpLogging/W3C-Logging, Reverse Proxies und APM. - JwtBearerOptions: Token-Handler, Claim-Mapping,
SaveToken,IncludeErrorDetailsund Authority/Metadata des SchemesAudisJwtwerden per Post-Configure festgelegt und überschreiben Einstellungen der Anwendung; eigeneConfigure-Aufrufe für diese Werte sind wirkungslos und werden nicht unterstützt. EigeneEventsbzw.EventsTypebleiben erhalten. Dieaccess_token-Query liest der Handler selbst, sie funktioniert daher auch mitEventsType;OnMessageReceivedläuft danach und kann das Token ersetzen. Die Audience-Prüfung liegt im Handler und ist von den Events nicht betroffen. - Mehrere Modes im Token:
AudisJwtClaims.GetModewirft eineInvalidOperationException, wenn das Token mehrere unterschiedlicheaudis:mode-Werte enthält. Claims anderer Schemes werden vonAudisJwtClaimsignoriert.
Lizenz
MIT, © softaware gmbh.
| 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
- Audis.Authentication (>= 2.0.0)
- Microsoft.AspNetCore.Authentication.JwtBearer (>= 10.0.6)
NuGet packages
This package is not used by any NuGet packages.
GitHub repositories
This package is not used by any popular GitHub repositories.
| Version | Downloads | Last Updated |
|---|---|---|
| 2.0.0 | 121 | 11/4/2026 |