Changelog
All notable changes to ArturRios.Util.WebApi are recorded in this file.
The format is based on Keep a Changelog, and this project adheres to Semantic Versioning.
Unreleased
5.2.0 - 2026-10-29
Added
AuthenticationOptions.ValidateIssuerandValidateAudience(both defaultfalse). When on,JwtTokenValidatoralso requires the app JWT’sissto equalJwtConfiguration.Issuerand itsaudto includeJwtConfiguration.Audience(ordinal), failing with"Invalid token issuer"/"Invalid token audience". Turning one on while the matchingJwtConfigurationvalue is blank makes the application fail at startup with anInvalidOperationException. With both off, only the signature is checked, as before.
Changed
- The package’s project URL is the documentation site’s new address,
https://docs.artur-rios.tech/dotnet-webapi-util, since the site moved from GitHub Pages.
Fixed
WebApiSwaggerOptions.JwtAuthenticationnow makes the generated document require theBearerscheme. The security requirement was written as an empty object ("security": [{}]), which tells Swagger UI the operations need no credentials, so the token entered under “Authorize” was never sent.- A malformed bearer token (bad base64url, a segment that is not JSON) is answered with 401 when Google
authentication is enabled.
GoogleTokenVerifierlet the decoder’sFormatException/JsonExceptionescape, soExceptionMiddlewareanswered 500. ExceptionMiddlewareanswers aBadHttpRequestException(e.g. a request body overMaxRequestBodySize) with the exception’s own status code (413, 400, …) and its reason phrase, logged at Information, instead of a 500 logged as an error.
Security
- Without the new
ValidateIssuer/ValidateAudience, any token signed with an accepted key is accepted whatever issuer or audience it names. Turn them on when the signing key is shared with another issuer or audience (see the Security docs, “Issuer and audience”).
5.1.0 - 2026-10-07
Added
TraceActivityOptionswithLogClientIpandTagClientAddress, exposed onWebApiStartupOptionsasTraceActivity, to stopTraceActivityMiddlewarelogging the client IP or taggingclient.address. Both default totrue, so existing behavior is unchanged.
5.0.0 - 2026-10-05
Added
output.ToActionResult(...)extensions forDataOutput<T>,PaginatedOutput<T>andProcessOutput.- Every
WebApiStartupstep is a public extension usable on a plainWebApplicationBuilder:LoadConfiguration,AddWebApiSwagger,AddInvalidModelStateEnvelope,UseStandardMiddlewares,UseWebApiMiddlewaresandUseWebApiSwagger. WebApiStartupOptions(Swagger, invalid-model-state envelope, CORS policy, extra middlewares) andWebApiSwaggerOptions(allowed environments, JWT authentication, generator callback).WebApiParameters.ToWebApplicationOptions()andIsSwaggerAllowed(...).AddTracePropagation()onIHttpClientBuilderto addTracePropagationHandlerto a typed client.TraceActivityMiddlewarecontinues an incomingtraceparent/tracestatewhen there is no ambient activity, logs the client IP with each request and tags it on the activity asclient.address.TracePropagationHandleralso forwardstracestate.[AllowAnonymous]can be placed on a whole controller, and ASP.NET Core’s own[AllowAnonymous]is honored too.
Changed
- Breaking:
WebApiStartupis a fixed template with one abstract method,ConfigureServices;Build()returns theWebApplication. - Breaking: error envelopes written by the middlewares are camelCase, like MVC results.
[Authorize]returns the standard failed envelope, and[RoleRequirement]returns 401 when no user is authenticated. - Breaking:
UseWebApiMiddlewaresthrows for types that do not derive fromWebApiMiddlewareinstead of skipping them. WebApiParameterskeys are case-insensitive, andUseAppSettingsis accepted alongsideUseAppSetting.AuthenticateAndAuthorizeAsyncthrowsWebApiClientExceptionwhen the response is not valid or carries no token.- Extensions are written as C# 14 extension blocks.
Removed
- Breaking:
ResponseResolver(useoutput.ToActionResult()) andTokenExtractor(usecontext.ExtractToken()). - Breaking: the empty
WebApiStartuphooks,ConfigureApp,BuildApp,BuildAndRun, theAppfield and the startup helper methods, replaced by the extensions above.
4.0.0 - 2026-08-24
Changed
- Breaking:
ExceptionMiddlewareenvelopes its 500 response with errors rather than messages, so it no longer comes back as"success": true. - The middlewares serialize with
System.Text.Jsonrather than an undeclared, transitively resolvedNewtonsoft.Json. ArturRios.Configuration,ArturRios.JwtandArturRios.Utilupdated to 1.2.0, 1.2.0 and 2.1.0;Google.Apis.Authupdated to 1.76.0.
Removed
- Breaking: the
Microsoft.AspNetCore.Mvc.Testingdependency. Nothing in the package used it; a consumer that relied on getting it transitively must now reference it directly.
Fixed
[EndpointToggle]no longer keeps per-request state on the shared attribute instance, where concurrent requests to the same action overwrote each other’s context.- The exception form of
[EndpointToggle]throws with the configured disabled message rather than the default one.
3.3.0 - 2026-08-19
Changed
ArturRios.Configurationupdated from 1.0.0 to 1.1.0 andArturRios.Utilfrom 1.5.0 to 2.0.0. The public API is unchanged.
3.2.0 - 2026-08-17
Added
- Signing key rotation: tokens are validated against
JwtConfiguration.Keyswhen it has any, so a token signed with a key that is no longer the signing key stays valid until that key is withdrawn. A configuration with noKeysstill validates againstSecret.
Changed
ArturRios.Jwtupdated to 1.1.0.
3.1.0 - 2026-08-12
Changed
- Dependencies updated:
ArturRios.Util1.5.0,Microsoft.AspNetCore.Authentication.JwtBearer10.0.11 andMicrosoft.OpenApi2.7.5.
3.0.0 - 2026-07-30
Added
IAuthenticatedUser(Guid Id,int RoleId), so a consuming app can authenticate users of its own type, read back withHttpContext.GetUser<TUser>().IAuthenticatedUserMapper, withDefaultAuthenticatedUserMapper, to decide which claims a token carries and how a user is rebuilt from them.TokenClaimsReaderto read a JWT’s claims.
Changed
- Breaking: users are identified by a
Guidid and anintrole id:AuthenticatedUser(Guid Id, int RoleId),TokenClaimKeys.RoleId,GetAuthenticatedUserById(Guid), andIAuthenticationProviderandTokenValidationResultspeakIAuthenticatedUser. Tokens issued by 2.x carry an integeridclaim and are rejected after the upgrade. See Upgrading from 2.x to 3.0.
Removed
- Breaking:
AuthenticatedUserFactoryandToTokenClaims(), replaced byTokenClaimsReaderand the mapper.
Fixed
TokenClaimsReader.Readreturnsnullinstead of throwing for a token whose header cannot be decoded.
Upgrading from 2.x to 3.0
3.0.0 makes the authenticated-user type and the token’s claim keys caller-defined. Where the library
used to hard-code AuthenticatedUser(int Id, int Role), it now only knows IAuthenticatedUser (Guid Id, int RoleId) — implement your own, or keep using the library’s AuthenticatedUser(Guid Id, int RoleId).
| Before | After |
|---|---|
AuthenticatedUser(int Id, int Role) | AuthenticatedUser(Guid Id, int RoleId), or your own IAuthenticatedUser |
user.Role | user.RoleId |
TokenClaimKeys.Role | TokenClaimKeys.RoleId — the claim string is still "role" |
GetAuthenticatedUserById(int id) returning AuthenticatedUser? | GetAuthenticatedUserById(Guid id) returning IAuthenticatedUser? |
GetAuthenticatedUserByEmail(string email) returning AuthenticatedUser? | same parameter, now returning IAuthenticatedUser? |
TokenValidationResult(AuthenticatedUser?, string?) | TokenValidationResult(IAuthenticatedUser?, string?) — affects custom ITokenValidator implementations |
user.ToTokenClaims() | mapper.ToClaims(user) |
AuthenticatedUserFactory.FromToken(token) | TokenClaimsReader.Read(token) then mapper.FromClaims(claims) |
(AuthenticatedUser?)HttpContext.Items["User"] | HttpContext.GetUser<MyUser>() |
Two things to plan around before you deploy this upgrade:
Tokens issued by 2.x are rejected after the upgrade. The claim key strings are unchanged, so the
claims still read — but DefaultAuthenticatedUserMapper requires the id claim to parse as a Guid,
and 2.x wrote an integer there. Every unexpired access token therefore fails with a 401
("Could not retrieve user from token", or "Could not retrieve user id from token" in Revalidate
mode). This is fail-closed and safe, but it logs every user out at the moment of deployment. Plan for
it: drain the old tokens before switching over, accept the forced re-authentication, or ship a mapper
that accepts both an integer and a Guid in the id claim for one release.
User ids must become Guids. If your store keys users by integer, this is a data migration, not
just a compile fix.
2.1.0 - 2026-07-23
Added
ResponseResolver.Resolvetakes a status-code map to override the default status per output.
Changed
- Dependencies updated, including
ArturRios.Util1.3.0 andGoogle.Apis.Auth1.75.0.
2.0.0 - 2026-07-22
Added
- Token sources:
AuthenticationMiddlewarereads the token from theAuthorizationheader, a cookie, or either. - Google ID token authentication alongside the app’s own JWT, configured with
AddTokenAuthenticationandAuthenticationOptions. IAuthenticationProvider.GetAuthenticatedUserByEmail, with caching inCachedAuthenticationProvider.ITokenValidatorandTokenValidationResultcontracts for custom validators.
Changed
- Breaking:
JwtMiddlewareis replaced byAuthenticationMiddleware.
Removed
- Breaking:
JwtAuthenticationOptions, replaced byAuthenticationOptions. - Breaking:
AddLogging/AddCustomLoggingand theArturRios.Loggingdependency; set up logging in the derivedWebApiStartup.
Security
- Google ID tokens whose email is missing or unverified are rejected before the user lookup.
1.1.0 - 2026-07-14
Added
[EndpointToggle]to enable or disable a single endpoint from a compile-time flag or a configuration value.
1.0.0 - 2026-07-09
Added
WebApiStartupandWebApiParametersto bootstrap a web API (configuration, Swagger, middleware pipeline).- JWT authentication middleware with stateless (
ClaimsOnly) or revalidating user resolution,CachedAuthenticationProvider, and[Authorize],[AllowAnonymous]and[RoleRequirement]. ExceptionMiddleware,TraceActivityMiddlewareandTracePropagationHandler.BaseWebApiClientandBaseWebApiClientRoutefor typed HTTP clients, andWebApiClientException.ResponseResolverto turnArturRios.Outputenvelopes intoActionResults.CredentialsValidatorfor email format and password length.- XML documentation across the public API. The configuration and client types live in the
ArturRios.Util.WebApi.ConfigurationandArturRios.Util.WebApi.Clientnamespaces.