Authio.AspNetCore
0.8.0
See the version list below for details.
dotnet add package Authio.AspNetCore --version 0.8.0
NuGet\Install-Package Authio.AspNetCore -Version 0.8.0
<PackageReference Include="Authio.AspNetCore" Version="0.8.0" />
<PackageVersion Include="Authio.AspNetCore" Version="0.8.0" />
<PackageReference Include="Authio.AspNetCore" />
paket add Authio.AspNetCore --version 0.8.0
#r "nuget: Authio.AspNetCore, 0.8.0"
#:package Authio.AspNetCore@0.8.0
#addin nuget:?package=Authio.AspNetCore&version=0.8.0
#tool nuget:?package=Authio.AspNetCore&version=0.8.0
Authio.AspNetCore
Protect an ASP.NET Core API (.NET 8 and .NET 10) with Authio:
- Access-token validation with the standard JWT bearer handler, configured from the realm's discovery document:
issuer, explicit audiences, the client's access token algorithm (asymmetric by default;
HS256/HS384/HS512with the client secret when named), small clock skew, Authio's claim names kept as they are; access tokens Authio encrypts to the client's own key (nested JWE, RSA-OAEP or RSA-OAEP-256) decrypted withAuthio:ClientEncryptionKey. No client id needed to validate locally. - Introspection, optional: confirm on every request (cached per
jti) that the token was not revoked and the session did not end, or validate opaque tokens, and HMAC tokens of another client, purely by introspection. - RFC 9728, opt-in: the API's protected resource metadata at
/.well-known/oauth-protected-resource{path}andresource_metadatain every401challenge. - Authio roles: the
*_resource_accessclaims are flattened intoroleandpermissionclaims, and the full model is one call away. - Authorization: scope, role, permission (Authio's exact rule), organization, client, caller kind and step-up
(
acr,amr, recency) requirements, as policy-builder helpers, minimal API helpers and controller attributes, with RFC 6750 errors, the RFC 9470 step-up challenge and optional problem details. - A typed caller:
User.GetAuthioUser(): who called (a person, a client as itself, a client's consumer or an account credential, and with which credential), subject, client, session, scopes, roles, permissions, organization, account,acr/amr, impersonation and delegation chain, profile. - Webhooks: a signed-delivery receiver with secret rotation, timestamp tolerance, de-duplication and typed handlers.
- Downstream calls: forward the caller's token, or exchange it on behalf of the user (token exchange), cached.
It builds on Authio.Client; machine-to-machine calls use its
AddAuthioClientCredentials.
dotnet add package Authio.AspNetCore
Quick start
{
"Authio": {
"Authority": "https://auth.example.com",
"Realm": "acme",
"Audiences": [ "https://orders.example.com" ]
}
}
using Authio.AspNetCore.Defaults;
using Authio.AspNetCore.Extensions;
var builder = WebApplication.CreateBuilder(args);
builder.Services
.AddAuthentication(AuthioJwtBearerDefaults.AuthenticationScheme)
.AddAuthioJwtBearer(builder.Configuration.GetSection("Authio"));
builder.Services.AddAuthioAuthorization();
var app = builder.Build();
app.UseAuthentication();
app.UseAuthorization();
app.MapGet("/orders", (HttpContext context) => context.GetAuthioUser())
.RequireAuthioScope("orders:read");
app.Run();
Token validation
AddAuthioJwtBearer registers the scheme AuthioBearer (AuthioJwtBearerDefaults.AuthenticationScheme, or any name
with the (scheme, configuration, configure) overload). It reads the realm from AuthioOptions (Authority +
Realm, or Issuer) and its own settings from AuthioJwtBearerOptions; the configuration overload binds both from
the same section. With the Action<AuthioJwtBearerOptions> overload, configure AuthioOptions through
services.AddAuthioClient(...).
What the handler checks:
| Check | Value |
|---|---|
| Metadata | {issuer}/.well-known/openid-configuration through the Authio named HttpClient; keys from its jwks_uri |
| Issuer | exactly {Authority}/realms/{Realm} (a different issuer in the discovery document is not trusted) |
| Audience | any of Audiences (required) |
| Algorithms | AccessTokenSignedResponseAlg; left out, RS256/384/512, PS256/384/512, ES256/384/512, never HS* (see Access token algorithm) |
| Lifetime | exp/nbf required, ClockSkew default 30 s |
| Token type | the typ claim must be Bearer: ID and refresh tokens are refused (RejectNonAccessTokens) |
| Claims | MapInboundClaims = false: sub, scope, azp… keep their names |
| Name | preferred_username, or sub when the token has none (client-credentials tokens) |
| Roles | role claims flattened from *_resource_access (MapAuthorizationClaims) so IsInRole works |
Access token algorithm
Authio:AccessTokenSignedResponseAlg mirrors the client's access token signing setting in Authio, its second one (the
ID token has its own; the refresh token is always signed with the server's key). It takes one algorithm or several:
"AccessTokenSignedResponseAlg": "RS256"
"AccessTokenSignedResponseAlg": [ "RS256", "HS256" ]
Left out, the asymmetric algorithms are accepted and HS* never is. A token signed with an algorithm outside the list
is refused before any key is tried; an HS* one, when none is listed, with a description that says what to do:
WWW-Authenticate: Bearer error="invalid_token", error_description="The token is signed with the client secret (HS*): set Authio:AccessTokenSignedResponseAlg and Authio:ClientSecret to validate it locally or validate it with introspection (Authio:Introspection = IntrospectionOnly)."
When to use HS*. HS256, HS384 and HS512 are verified with the client secret (its UTF-8 octets, at least
32, 48 or 64 bytes, RFC 7518 §3.2), so only an API that holds that secret validates such a token locally: the client's
own API, deployed with it. List the algorithm and give the API Authio:ClientId and Authio:ClientSecret (or
ClientSecretProvider, read on every token so a rotation is picked up). Any other API must not receive the secret:
it validates the client's HS* tokens with Introspection = IntrospectionOnly. When in doubt, keep the client on an
asymmetric algorithm, which every API verifies with the realm's public keys and no secret.
A secret shorter than the algorithm needs, an HS* algorithm without a secret, or an algorithm Authio does not sign
with stops the API at start-up; a secret from ClientSecretProvider is measured when a token arrives, and a short one
fails the request with InvalidOperationException (a configuration error, not the caller's).
An API that only validates locally needs no Authio:ClientId; introspection, on-behalf-of and the client-credentials
handler authenticate the client and do.
Several realms
Each scheme validates against its own realm. Its AuthioOptions are named after the scheme: they start as a copy of
the shared ones (AddAuthioClient, or the default scheme's section), then take the scheme's own section, then
services.Configure<AuthioOptions>(scheme, ...) registered after AddAuthioJwtBearer. A scheme that names no realm
validates against the client's.
builder.Services
.AddAuthentication(AuthioJwtBearerDefaults.AuthenticationScheme)
.AddAuthioJwtBearer(builder.Configuration.GetSection("Authio"))
.AddAuthioJwtBearer("Partners", builder.Configuration.GetSection("AuthioPartners"), null);
app.MapGet("/catalog", List).RequireAuthorization(policy => policy
.AddAuthenticationSchemes(AuthioJwtBearerDefaults.AuthenticationScheme, "Partners")
.RequireAuthenticatedUser());
Only the default scheme binds its section into the shared AuthioOptions. The Authio client — and with it
introspection and on-behalf-of — stays on that one realm, so Introspection other than None on a scheme of another
realm stops the application at startup.
Audiences
Authio sets aud to the RFC 8707 resource identifier when the client asked for a registered resource server
(resource=https://orders.example.com on the authorize or token request), and otherwise to the audience configured
on the client that requested it, its own client id unless changed: that token is addressed to the client, not to
an API. So register your API as a resource server in Authio and accept its identifier:
"Audiences": [ "https://orders.example.com" ]
The application that calls the API has to ask Authio for a token for that resource: resource=https://orders.example.com
on its authorize or token request, which with Authio.AspNetCore.Bff is Authio:Bff:Resource for the session's
token, or Resource on a remote API or typed client (exchanged for it). A token without it is addressed to the
client that requested it, and the API refuses it with 401:
WWW-Authenticate: Bearer error="invalid_token", error_description="The token is not addressed to this API: its audience is not 'https://orders.example.com'. Request the token for that resource (RFC 8707)."
The token's own audience is named too when the validation reports it, as IntrospectionOnly does.
Options
Key (Authio:) |
Default | Meaning |
|---|---|---|
Audiences |
— (required unless Resource) |
Accepted audiences |
Resource |
— | The API's own resource identifier: accepted as an audience, and the resource of its RFC 9728 metadata |
PublishResourceMetadata |
false |
resource_metadata in 401 challenges (needs Resource); map the document with app.MapAuthioProtectedResourceMetadata() |
ResourceScopes, ResourceName, ResourceDocumentation |
— | scopes_supported, resource_name and resource_documentation of the metadata |
ClientEncryptionKey / ClientEncryptionKeyPath |
— | The client's private encryption key, for access tokens Authio encrypts to it |
AllowLegacyEncryptionAlgorithms |
false |
Decrypt JWEs wrapped with RSA1_5 |
Introspection |
None |
None, ActiveCheck or IntrospectionOnly |
IntrospectionCacheDuration |
00:00:30 |
How long an active answer is reused (never past exp); 0 disables |
ClockSkew |
00:00:30 |
Lifetime tolerance |
AccessTokenSignedResponseAlg |
asymmetric list, never HS* |
The client's access token algorithm(s); HS* with ClientSecret |
RequireHttpsMetadata |
Authio:RequireHttps |
HTTPS for discovery and keys |
MapAuthorizationClaims |
true |
Add role / permission claims |
RejectNonAccessTokens |
true |
Refuse tokens whose typ is not Bearer |
SaveToken |
true |
Keep the token in the authentication properties |
Anything else of JwtBearerOptions can be changed with services.Configure<JwtBearerOptions>(scheme, ...); an
OnTokenValidated you set there runs after the Authio step.
Introspection
A JWT stays valid until it expires even after a logout, a revocation, a suspension or a withdrawn delegation.
Authio's back-channel logout tells the client a person signed in to, not the APIs that accept its tokens, so two
modes ask Authio's introspection endpoint instead. Both need
services.AddAuthioClient(...) with a confidential client allowed to introspect the API's tokens (in Authio, the
client the resource server names as its introspection client): Authio:ClientId and Authio:ClientSecret, or, with
Authio:TokenEndpointAuthMethod = PrivateKeyJwt, its private key (Authio:ClientPrivateKeyPath or
Authio:ClientPrivateKey, plus Authio:ClientKeyId) so no secret is sent; see
Authio.Client. On-behalf-of
and the client-credentials handler authenticate the same way.
ActiveCheck: after local validation, the token must beactive. Active answers are cached perjtiforIntrospectionCacheDuration, which bounds how long a revoked token is still accepted. If Authio cannot be reached the request is refused (fail closed).IntrospectionOnly: for reference tokens, and for the HMAC-signed tokens of a client whose secret this API does not hold (the client's own API validates them locally, see Access token algorithm). (An encrypted access token no longer needs it: withAuthio:ClientEncryptionKeyit is decrypted and validated locally, see Encrypted access tokens.) The token is not read locally; the answer must beactive, from the configured issuer, for an accepted audience and not expired. The principal is built from the answer (sub,azpfromclient_id,scope,preferred_usernamefromusername,aud,jti,exp…) plus the claims Authio adds to an active answer: the*_resource_accessrole maps,acr,amr,org_id/org_alias,act,sidandtyp. Every requirement works as it does with local validation, andtoken_typeis checked liketyp, so refresh and ID tokens are refused. Needs an Authio server that writes those claims (October 2026 onwards); an older one answers without them, and role, permission, sign-in and organization requirements then refuse.
"Authio": {
"Authority": "https://auth.example.com",
"Realm": "acme",
"ClientId": "orders-api",
"ClientSecret": "<secret>",
"Audiences": [ "https://orders.example.com" ],
"Introspection": "ActiveCheck"
}
builder.Services.AddAuthioClient(builder.Configuration.GetSection("Authio"));
builder.Services
.AddAuthentication(AuthioJwtBearerDefaults.AuthenticationScheme)
.AddAuthioJwtBearer(builder.Configuration.GetSection("Authio"));
Who can introspect a token
Authio answers an introspection request according to who asks, as RFC 7662 lets an authorization server do
(§2.2: a protected resource that "is not allowed to introspect this particular token" gets active: false; §4: the
server authorizes which protected resources may introspect):
| The token was requested… | Its aud |
Introspected by the API's client (the resource server's introspection client) | Introspected by the client that requested it |
|---|---|---|---|
with resource=https://orders.example.com |
the resource | active: true |
active: false, by design |
| without a resource | the requesting client | active: false |
active: true |
- The API is the token's audience, so the API is the one that introspects it: that is what
ActiveCheckandIntrospectionOnlydo, with the client the resource server names in Authio. - To the client that requested it, an access token for a resource is opaque (RFC 6749 §1.4): the client sends it and
does not read it. Its own introspection answers
active: false, which says nothing about whether the API accepts the token. A worker that wants to see its token introspected asks for one without a resource. - An
active: falseanswer is refused with401anderror_description="Authio reports the token as not active to this API's client. …": it was revoked, it expired, its session ended, or the API'sAuthio:ClientIdis not the introspection client of the token's resource server.
Encrypted access tokens
A token encrypted to the client's own key is a nested JWE: the bearer handler decrypts it with
Authio:ClientEncryptionKey (the private JWK Authio hands over, or a PEM; or ClientEncryptionKeyPath, read again
when the file changes) and validates the signed token inside like any other. Key management RSA-OAEP and
RSA-OAEP-256, content A128CBC-HS256, A192CBC-HS384 or A256CBC-HS512; RSA1_5 only with
Authio:AllowLegacyEncryptionAlgorithms = true. A token no key opens, or one with a refused algorithm, answers 401
with an error_description saying so.
Protected resource metadata (RFC 9728)
{ "Authio": { "Resource": "https://orders.example.com", "PublishResourceMetadata": true, "ResourceScopes": [ "orders:read" ] } }
app.MapAuthioProtectedResourceMetadata(); // GET /.well-known/oauth-protected-resource
The document names the resource, the realm's issuer as its authorization server and header as the bearer method;
every 401 answers WWW-Authenticate: Bearer resource_metadata="https://orders.example.com/.well-known/oauth-protected-resource", …,
so a client (an MCP client, for one) discovers where to sign in and which resource to ask for.
The caller
app.MapGet("/me", (HttpContext context) =>
{
var user = context.GetAuthioUser(); // or User.GetAuthioUser() in a controller
var token = context.GetAuthioAccessToken(); // the raw bearer token
var access = context.GetAuthioResourceAccess(); // roles model, parsed once per request
return user;
}).RequireAuthorization();
AuthioUser holds Kind (User, Client, Consumer or AccountCredential), CredentialForm (client_secret,
personal_access_token, api_key or consumer_api_key, for machines), ConsumerName, CredentialId,
CredentialName, OwnerUserId / OwnerUsername (who a personal access token or an account credential belongs to;
read from owner_username, or from the consumer_name older Authio servers wrote it to), CredentialRealm /
CredentialClient (what an account credential is limited to), Impersonated + Impersonator (Subject, Name),
Delegated + DelegationId + ActorChain (the clients acting for the user, outermost first), Subject,
ClientId (azp), SessionId (sid), TokenId (jti), Audiences, Scopes, Roles, Permissions
(resource → methods), ResourceAccess (with the source of each role), OrganizationId / OrganizationAlias,
AccountId, Acr / AuthenticationLevel, AuthenticationMethods (amr), IdentityProvider (idp),
AuthenticatedAt, Actor (the raw act chain), Email, EmailVerified, Name and PreferredUsername. Every
Authio library derives these the same way (conformance/callers.json).
Roles and permissions are only in tokens issued with the roles scope. Other principal helpers:
GetAuthioScopes(), HasAuthioScope(scope), HasAuthioRole(role), HasAuthioPermission(resource, method),
GetAuthioAuthenticationLevel(), GetAuthioAuthenticationMethods(), GetAuthioActor().
For Authio principals that come from another scheme (for example a cookie session signed in with OpenID Connect),
services.AddAuthioClaimsTransformation() adds the same role / permission claims; it is idempotent.
Authorization
Every requirement is available three ways.
// Policies
builder.Services.AddAuthorization(options =>
{
options.AddPolicy("orders.write", policy => policy
.RequireAuthioScope("orders:write")
.RequireAuthioPermission("orders", "write"));
});
// Minimal APIs (endpoints and route groups)
app.MapGet("/orders", List).RequireAuthioScope("orders:read", "orders:admin");
app.MapGroup("/admin").RequireAuthioRole("admin").RequireAuthioAuthenticationMethod("mfa");
// Controllers (needs services.AddAuthioAuthorization())
[AuthioScope("orders:read")]
[AuthioPermission("orders", "write")]
public IActionResult Create() => ...;
| Requirement | Helper / attribute | Passes when |
|---|---|---|
| Scope | RequireAuthioScope([match,] scopes) / [AuthioScope(..., RequireAll = true)] |
any (default) or all scopes are in scope, compared exactly |
| Role | RequireAuthioRole([match,] [sources,] roles) / [AuthioRole(..., RequireAll = true, Sources = ...)] |
any or all roles are keys of a *_resource_access claim (or role claims), case-insensitive; with sources, only roles from those claims count (Client for client_resource_access, Realm, User, Plan, Consumer, Secret, System) |
| Permission | RequireAuthioPermission(resource, method[, sources]) / [AuthioPermission(..., Sources = ...)] |
Authio's ResourceAccessRules.Grants: any role lists the method under the resource, case-insensitive; with sources, only roles from those claims count (e.g. System to reserve an action for platform roles) |
| Organization | RequireAuthioOrganization(organization?) / [AuthioOrganization] |
org_id is present; with an argument, org_alias or org_id equals it (case-insensitive) |
| Step-up level | RequireAuthioAuthenticationLevel(min[, maxAge]) / [AuthioAuthenticationLevel(min, MaxAgeSeconds = …)] |
numeric acr ≥ min (and, with maxAge, the sign-in at most that old) |
| Step-up recency | RequireAuthioRecentAuthentication(maxAge) / [AuthioRecentAuthentication(seconds)] |
auth_time is at most maxAge ago |
| Step-up method | RequireAuthioAuthenticationMethod([match,] methods) / [AuthioAuthenticationMethod(..., RequireAll = true)] |
any or all methods are in amr (pwd, otp, swk, mca, fed, mfa…); fed is a sign-in through an external identity provider, counted as one factor, and comes with an idp claim holding the provider's alias (google, github) |
| Client | RequireAuthioClient(clientIds) / [AuthioClient] |
azp is one of the clients |
| Caller kind | RequireAuthioCallerKind(kinds) / [AuthioCallerKind(AuthioCallerKind.User)] |
GetAuthioUser().Kind is one of the kinds |
Several attributes on one action all apply. The attributes generate policy names (Authio:scope.any:...) that the
AuthioAuthorizationPolicyProvider registered by AddAuthioAuthorization() turns into policies; every other policy
name falls back to the policies you registered.
Refusals
With AddAuthioAuthorization():
- a missing scope answers
403withWWW-Authenticate: Bearer error="insufficient_scope", error_description="...", scope="orders:read orders:write"(RFC 6750 §3.1); - a failed step-up (level, method or recency) answers
401withWWW-Authenticate: Bearer error="insufficient_user_authentication", error_description="...", acr_values="2 3", max_age=300(RFC 9470 §3):acr_valueslists the levels that would do (from the minimum level, or the level the methods bring:otp/mfa/mca2,swk/hwk/user3) andmax_agethe recency asked for. The client signs the user in again with them (IAuthioLoginClient.CreateStepUpAuthorizationAsync) and still checks the new token, because Authio issues the level the sign-in actually reached. WithWriteBearerErrors = falsethe refusal stays a plain403; - other refusals are a plain
403; an invalid or missing token is the bearer handler's401witherror="invalid_token"and anerror_descriptionthat says why: the bearer handler's own (expired, signature, issuer) or Authio's, for a token addressed to another audience, one that is not an access token, or one introspection reports as not active or cannot confirm. A refusal your ownOnTokenValidatedmakes keeps its message out of the header;JwtBearerOptions.IncludeErrorDetails = falseleaves every description out.
builder.Services.AddAuthioAuthorization(options =>
{
options.WriteBearerErrors = true; // default
options.WriteProblemDetails = true; // application/problem+json body on 401/403 (default false)
});
Webhooks
"Authio": {
"Webhooks": {
"Secret": "whsec_...",
"Secrets": [ "whsec_previous..." ],
"Tolerance": "00:05:00",
"Deduplication": "Memory",
"DeduplicationWindow": "1.00:00:00",
"MaxBodySize": 1048576
}
}
builder.Services
.AddAuthioWebhooks(builder.Configuration.GetSection("Authio:Webhooks"))
.AddHandler<AuditTrailHandler>() // IAuthioWebhookHandler: every event
.AddHandler<AuthioWebhookUserData, UserSyncHandler>() // IAuthioWebhookHandler<AuthioWebhookUserData>
.On(AuthioWebhookEventTypes.KeysRotated, (evt, services, ct) => ...)
.On<AuthioWebhookTokenRevokedData>(AuthioWebhookEventTypes.TokenRevoked, (evt, data, services, ct) => ...)
.OnAny((evt, services, ct) => ...);
app.MapAuthioWebhooks(); // POST /webhooks/authio, anonymous (the signature authenticates)
The receiver reads the raw body, verifies X-Authio-Signature (HMAC-SHA256 over {t}.{body} with the full
whsec_ secret, constant-time) against every configured secret, so a rotation can list the old and new secrets,
checks the timestamp against Tolerance, parses the event and skips an X-Authio-Event-Id it has already handled.
| Answer | When |
|---|---|
200 |
handled, a duplicate, or ping |
400 |
signed, but not an Authio event |
401 |
missing, wrong or stale signature |
413 |
body over MaxBodySize |
500 |
a handler threw; the event id is forgotten so Authio's retry is handled again |
Typed handlers receive the data shape of their events: AuthioWebhookUserData (user_created, user_updated,
user_deleted), AuthioWebhookLoginData (login_succeeded, login_failed), AuthioWebhookUserLockedOutData,
AuthioWebhookTokenRevokedData, AuthioWebhookKeysRotatedData, AuthioWebhookTokenReuseDetectedData,
AuthioWebhookAdminResourceData (admin_resource_*), AuthioWebhookUserSuspensionData (user_suspended,
user_suspension_ended) and AuthioWebhookPingData. Handlers run before Authio gets its answer: keep them short and
enqueue long work. De-duplication is Memory (one instance), Distributed (register an IDistributedCache; the
check and the write are two calls) or None; replace IAuthioWebhookDeduplicationStore for anything else.
Downstream calls
// Same audience downstream: forward the caller's token as is.
builder.Services.AddHttpClient<ProfileApi>(c => c.BaseAddress = new("https://profile.example.com"))
.AddAuthioTokenForwarding();
// Another API, as the user: token exchange (RFC 8693). The delegated token keeps the user as sub,
// names this API in act, and has the downstream resource as aud.
builder.Services.AddAuthioClient(builder.Configuration.GetSection("Authio"));
builder.Services.AddHttpClient<BillingApi>(c => c.BaseAddress = new("https://billing.example.com"))
.AddAuthioOnBehalfOf(options =>
{
options.Resource = "https://billing.example.com";
options.Scopes = ["invoices:read"];
});
// Another API, as this API itself (no user): client credentials, from Authio.Client.
builder.Services.AddHttpClient<ReportsApi>(c => c.BaseAddress = new("https://reports.example.com"))
.AddAuthioClientCredentials(options => options.Resource = "https://reports.example.com");
On-behalf-of needs AddAuthioClient with this API's confidential client, allowed to exchange tokens in Authio. The
delegated token is cached per incoming token (jti), resource and scopes until Authio:TokenRefreshSkew before it
expires (concurrent requests share one exchange); on a downstream 401 it is dropped and the call retried once.
IAuthioOnBehalfOfTokenCache can also be used directly.
Testing
The Authio.Testing package is an
in-memory Authio (discovery, JWKS, every grant, introspection, JWE to a client's key…); route the Authio named client
to it with services.AddFakeAuthioBackchannel(fake) and mint tokens with
fake.Tokens.CreateAccessToken(...).WithAudience(...) (WithAuthTime, WithActor, WithImpersonator, …).
License
MIT.
| Product | Versions Compatible and additional computed target framework versions. |
|---|---|
| .NET | net8.0 is compatible. net8.0-android was computed. net8.0-browser was computed. net8.0-ios was computed. net8.0-maccatalyst was computed. net8.0-macos was computed. net8.0-tvos was computed. net8.0-windows was computed. net9.0 was computed. net9.0-android was computed. net9.0-browser was computed. net9.0-ios was computed. net9.0-maccatalyst was computed. net9.0-macos was computed. net9.0-tvos was computed. net9.0-windows was computed. 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
- Authio.Client (>= 0.8.0)
- Microsoft.AspNetCore.Authentication.JwtBearer (>= 10.0.10)
-
net8.0
- Authio.Client (>= 0.8.0)
- Microsoft.AspNetCore.Authentication.JwtBearer (>= 8.0.23)
NuGet packages (1)
Showing the top 1 NuGet packages that depend on Authio.AspNetCore:
| Package | Downloads |
|---|---|
|
Authio.Mcp.AspNetCore
Protects an ASP.NET Core MCP server (OAuth 2.1 resource server) with Authio, on top of Authio.AspNetCore: JWT, JWE and opaque token validation, RFC 9728 protected resource metadata and the MCP-ready WWW-Authenticate challenge, scope / role / permission / caller-kind requirements per tool with HTTP 403 and step-up 401 challenges, and the typed caller. |
GitHub repositories
This package is not used by any popular GitHub repositories.