Changelog
All notable changes to the Arc4u packages are documented in this file. The format is based on
Keep a Changelog 1.1.0. Since 8.0.0, Arc4u follows
semantic versioning; earlier versions used a
major.breaking.dotnet.minor scheme tied to the .NET version (see
Versioning).
Up to 8.3.2 the packages were named Arc4u.Standard.*; from 9.0.0 they are named Arc4u.*.
Dates are the dates the version was published on NuGet. Published versions without an entry had
no release notes.
Unreleased
Arc4u 9.0.0, developed on the develop/9.0.0 branch and published on NuGet as 9.0.0-previewNN
packages. It contains breaking changes: follow the
migration guide from 8.x to 9.
Added
Arc4u.Diagnostics: categories defined by the application.MessageCategoryRegistry.Register("Security", (MessageCategory)8)names a flag, andlogger.Category(category)starts an entry in any category, so an application can write its ownlogger.Security()extension method. The name is written in theCategoryproperty and read back byCategoryFilter,SimpleTextFormatter,MemoryLogDBandRealmDB.Arc4u.Dependency.Tool: source generators that turn[Export]attributes and theApplication.Dependencysection intoIServiceCollectionregistrations at compile time. Every[Export]attribute of a class or a record is registered, whatever its spelling (#256); open generic exports are supported (#257);Version=in a type name is matched against the referenced assembly, andappsettings.jsonmay contain comments and trailing commas (#258). Mistakes are reported as build diagnosticsARC4UDEP001toARC4UDEP007(#257, #258).Arc4u.OAuth2.Client.Authentication: OpenID Connect sign-in for desktop and mobile clients withDuende.IdentityModel.OidcClient(AddOidcClientAuthentication).Arc4u.AspNetCore.Versioning: API versioning helpers (AddServiceApiVersioning). Not published on NuGet yet.AddHybridAuthentication: OpenID Connect, cookies and JWT bearer in one registration.AddOidcAuthenticationnow registers OpenID Connect and cookies only.AuthenticationMethod(RedirectGetby default, orFormPost),NameClaimTypeandRoleClaimTypesettings for OpenID Connect.NameClaimTypeandRoleClaimTypealso apply toAddJwtAuthentication, soUser.Identity.Nameis set for a bearer token (#224).- Client tokens: the
Authentication:ClientTokenssection with theClientCredentials,UserPasswordandBasicscenarios, andClientCredentialsTokenProvider. - Redis Sentinel cache (
RedisSentinelCache,AddRedisSentinelCache, cache kindRedisSentinel), with password support. - Custom root certificate authority support:
AddCustomRootCA, and theConfigureLocalCaCertificateandConfigureLocalCaCertificateForGrpcextensions forHttpClientand gRPC clients. - gRPC error mapping:
ToRpcException()turns a failedResultinto anRpcException, andToResult()/ToProblemDetailError()read it back on the client. - Authorization:
PoliciesBuilder(returned byAddScopedOperationsPolicy) withAddAllOperations,AddAnyOperations,AddPolicyandConfigureAuthorization, and theAllScopedOperationsRequirementandAnyScopedOperationRequirementrequirements (all or any of the operations). - Blazor: server-side authentication state (
AppPrincipalServerAuthenticationStateProvider,ApplicationContextCircuitHandler), authentication state deserialization for the mixed server/WebAssembly mode, andAddAuthenticationCookie. - Results:
ValidationError.Create,WithCode,WithSeverity; implicit conversions fromValidationErrorandProblemDetailErrortoResult; mappers with an asynchronous function;ProblemDetails.WithInstance. IssuerinAuthorityOptions.EndSessionEndpointandGetEndSessionEndpointAsyncinAuthorityOptions: the sign-out endpoint of the identity provider, read from its metadata, ornullwhen the identity provider publishes none. The metadata is downloaded once.NullLoggerWrapperto test classes that use the Arc4u logging extensions.- Ahead-of-time compilation support: the packages are marked
IsAotCompatible, andJsonSerializationaccepts aJsonSerializerContext. The packages build without trimming or AOT analyzer warnings, and a warning is now a build error. TheArc4u.AotPublishTestsample references every AOT-compatible package and, published withPublishAot=true, fails on any warning in Arc4u code. Native AOT and trimming lists the status of each package and the APIs to use (#277). Arc4u.Configuration.Store:AddFromConfiguration(key, section => section.Get<T>()), which lets the application's configuration binding generator bind a persisted section.Add<T>(key)binds with reflection and is now markedRequiresUnreferencedCodeandRequiresDynamicCode.Arc4u.Configuration.Store: overloads that take aJsonTypeInfo<T>, so that a trimmed or Native AOT application serializes the values with source-generated metadata:Add(key, value, typeInfo),AddFromConfiguration(key, bind, typeInfo),SectionEntity.GetValue(typeInfo)andSetValue(value, typeInfo), andISectionStore.TryGetValueAsync(key, typeInfo, ct)andTrySetValueAsync(key, value, typeInfo, ct). The persisted format does not change (#277).Arc4u.Caching.Dapr:DaprCacheOption.JsonSerializerOptions, given to the Dapr client to serialize the values. Set it with aJsonSerializerContextin a trimmed or Native AOT application. It is not read from the configuration (#277).ConfigurationHelper.GetConfigurationFromResourceStream(Assembly, params string[]): reads the JSON resources of an assembly you pass, instead of loading it by name, which trimming cannot follow (#277).- Sign-out of the token providers:
ISignOutTokenProviderfor the providers that hold tokens for a user session,SignOutTokenProvidersAsync(signs out every registered one),AddTokenProvidersSignOut()andSignOutTokenProviderKeys(resolve each provider separately),SignOutGeneration(a token requested before a sign-out is not kept after it),TechnicalLoggerExtensions.LogTechnicalException(logs with or without an Arc4u logger) andOboTokenCacheIndex(#113). LoginTimeoutandSecureCacheTimeoutsettings of the OpenID Connect sign-in of desktop and mobile clients (OidcClientAuthenticationOptions) (#113).Arc4u.MongoDB:AddMongoDatabase<TContext>(configuration, connectionStringKey, options)completes theMongoClientSettingsread from the connection string with a delegate, for the settings a connection string cannot carry (TranslationOptionsof the LINQ3 provider,ServerApi,ClusterConfigurator) (#54).AddResultLogger(), which registersFluentLoggerand gives it to FluentResults when the host starts, andUseResultLogger()to set it earlier (#251).- A
SetFromErrorFactoryoverload whose function also receives the default translation, so a custom factory can fall back to it (#249). AddMemoryCacheKind(),AddRedisCacheKinds(),AddSqlCacheKind()andAddDaprCacheKind(), which register the keyedICacheimplementations thatICacheContextresolves byKind; aCacheContextconstructor that takes the section name, andCacheContext.NormalizeKind(#242).AddHttpContextLoggingProperties()(Arc4u.OAuth2.AspNetCore): the loggers of singletons add the identity of the current HTTP request to their entries. Call it beforeAddApplicationContext()orAddILogger()(#237).IAuthorization.IsAuthorized(string operation), so thatIsAuthorized("Name")compiles (#225).TryGetNamedSettings(Arc4u.OAuth2.Extensions.NamedSettingsExtensions), anIServiceProviderextension that reads the namedSimpleKeyValueSettingsoptions, used byAppPrincipalFactoryand theJwtHttpHandlerandOAuth2Interceptorconstructors that take a settings name. Such constructors are added toJwtHttpHandler<T>(Arc4u.OAuth2.AspNetCore.Authentication), the BlazorJwtHttpHandlerandOAuth2Interceptor<T>(#231).SectionEntity(string key, string value)constructor (Arc4u.Configuration.Store) (#245).MemoryCacheOption.SizeLimitInBytes(#241).Arc4u.OAuth2.AspNetCore:ManageExceptionsHandler(AddManageExceptionsHandlerwithapp.UseExceptionHandler()) answers exceptions thrown outside MVC actions with the same problem details asManageExceptionsFilter.ManageExceptionsFilterOptionsadds the exception details to the response (IncludeExceptionDetails, by default in theDevelopmentenvironment only) and logs the action arguments (LogActionArguments, off by default, truncated toMaxActionArgumentLength) (#31).Certificate.EncryptandDecrypt(Arc4u.Security.Cryptography), and so the secret decryptor, accept a certificate with an EC key (P-256, P-384 or P-521). The value is encrypted with ECIES: an ephemeral ECDH key agreement with the certificate key, HKDF-SHA256 and AES-GCM, in the form<ephemeralPublicKey>.<data>. The algorithm is chosen from the certificate key; values encrypted with an RSA certificate are unchanged.
Changed
- Packages are now licensed under
Apache-2.0in their NuGet metadata, matching theLICENSEfile; earlier versions declaredMIT. - Packages renamed from
Arc4u.Standard.*toArc4u.*(assembly names too). See Package renames. - Target frameworks:
net10.0andnet11.0(the previews up to9.0.0-preview37targetnet8.0,net9.0andnet10.0). See Target frameworks. - Dependency injection uses the keyed services of
Microsoft.Extensions.DependencyInjectioninstead ofIContainer. See IContainer replaced by keyed services. - Errors are returned as FluentResults
Resultvalues instead ofMessagesandAppException:ITokenProvider.GetTokenAsyncandICredentialTokenProvider.GetTokenAsyncreturnValueTask<Result<TokenInfo>>, andIAppPrincipalFactory.CreatePrincipalAsyncreturnsResult<AppPrincipal>. See Message and Messages replaced by ProblemDetails. - Authentication settings: the OpenID Connect settings are registered under the name
Cookies, theAuthenticationTypeand settings-key configuration entries are removed, events classes are registered in the service collection, andIClaimsFiller.GetAsynctakes only the identity. See Authentication settings refactoring. - Logging:
Technical(),Business()andMonitoring()return anILoggerWrapper<T>and use the standardLog…methods; the logging is based on source-generatedLoggerMessagemethods (#150). See Fluent logging API. JwtHttpHandleris generic (JwtHttpHandler<T>).OnFailedandOnFailedAsynccallbacks receive anIReadOnlyCollection<IError>; passing an asynchronous lambda to a synchronousOnSuccess,OnFailed,OnSuccessNullorOnSuccessNotNullis a compile error; the…Asyncmethods on a plainResultorResult<T>return aTaskinstead of blocking with.Wait().ValidateWithResultmoved toArc4u.FluentValidation(namespaceArc4u.Validation), andToFluentResultErrorsis renamedToResultErrors.UseForceOfOpenId(options)is replaced byAddForceOpenId(options)andUseForceOpenId().- A custom ticket store or cookie post-configuration is registered in the service collection
(
ITicketStore,IPostConfigureOptions<CookieAuthenticationOptions>) instead of being named in configuration (TicketStore,CookiesConfigureOptionsType). OidcAuthenticationOptions.AuthenticationTicketTTLis renamedAuthenticationTicketTtl, andCertificateis renamedDataProtectionCertificate.- The default
Authentication:TokenCache:MaxTimeis 50 minutes instead of 20. - Uses System.Text.Json only. See Newtonsoft.Json replaced by System.Text.Json.
- The authentication cookie expires after the shorter of
AuthenticationTicketTtlandRefreshTokenLifetime, and its maximum age isRefreshTokenLifetime. - Dapr packages updated to 1.18.
MemoryLogMessages(MemoryLogDbSink.LogMessages) is a thread-safeIReadOnlyCollection<LogMessage>instead of aList<LogMessage>: useAdd,Clear,CountandGetPage, and enumerate it to read a snapshot (#238).AddNotificationHandlersAsScopedno longer takes aServiceLifetime, which registered the services as scoped for any value butTransient. CallAddNotificationHandlersAsScoped()or the newAddNotificationHandlersAsTransient()(#260).IApplicationContext.Principalis anAppPrincipal?andSetPrincipalacceptsnullto clear the principal, so the compiler warns when the principal is used without a null check (#125).ITokenProvider.SignOutAsyncmoved to the newISignOutTokenProviderinterface, implemented only by the providers that hold tokens for a user session.RemoteClientSecretTokenProvider,ClientCredentialsTokenProvider,BootstrapContextTokenProvider,NullTokenProvider,OidcTokenProvider(its tokens are in the authentication cookie) andCredentialTokenCacheTokenProvider(a configured account, not a user session) no longer have a sign-out.SignOutUserAsyncofAppPrincipalFactoryand ofAppServicePrincipalFactorysigns the user out of every registeredISignOutTokenProvider, not only the provider of theProviderId, and logs their exceptions instead of rethrowing them;AppServicePrincipalFactoryonly removed the cached claims before.AddOidcAuthentication,AddHybridAuthentication,AddOnBehalfOf,AddOidcClientAuthenticationandAddAuthenticationCookiecallAddTokenProvidersSignOut(), so a provider that cannot be resolved does not prevent the sign-out of the others.AzureADOboTokenProviderkeeps anOboTokenCacheIndexin the cache: add it to yourJsonSerializerContextif the cache serializer uses one. See Other API changes (#113).BlazorMsalTokenProvidertakes the publicIAccessTokenProviderinstead ofIAccessTokenProviderAccessor, an internal API ofMicrosoft.AspNetCore.Components.WebAssembly.Authenticationthat can change in any release. Its registration does not change; code that creates it must pass anIAccessTokenProvider(#83).AddJwtAuthenticationand the bearer scheme ofAddHybridAuthenticationvalidate the token issuer (Authentication:ValidateAuthority,trueby default), and the OpenID Connect sign-in applies the same rule: the issuer must beAuthentication:DefaultAuthority:Issuerwhen it is set, or the issuer of the provider's metadata. The configuration overloads ofAddOidcAuthentication(as in 8.x) andAddHybridAuthenticationapplyValidateAudienceandValidateAuthorityfrom theAuthenticationsection (#220).- The
Authentication:TokenCachesection is optional forAddJwtAuthentication, for an API that only validates inbound tokens (#224). - The
Identitylog property is the email of the user profile, or the identity name when there is no email. A logger resolved in a scope uses theIAddPropertiesToLogregistered under the keyScoped, and a logger resolved from the root (for a singleton) the one under the keyTransient(#237). - The client
JwtHttpHandlerclasses andOAuth2Interceptorsend the request without a token instead of throwing when the settings have noProviderId, the provider is not registered, the provider returns no token (NullTokenProvider) or, for the interceptor, there is no current HTTP request.OAuth2Interceptorno longer blocks on.Resultfor asynchronous unary calls.OidcClientIdentityModelTokenProviderreturns a failed result when the login fails, and logs the user in again when the refresh fails, instead of throwing (#230). ICache:Putsaves the default value of a value type (0,false),GetandPutof every cache wrap an engine or serialization error in aDataCacheExceptionthat keeps the original exception as its inner exception, the Dapr cache also rejects anullvalue and itsTryGetValuereturnsfalsefor a missing key, and a memory cache that is full throws aDataCacheExceptioninstead of dropping the value (#243).ToActionCreatedResultAsync,ToHttpCreatedResultAsyncandToTypedCreatedResultAsyncon aResultwithout a value lost their unused type parameter, and the title of the generic500problem details is "A technical error occurred!" (the typo "occured" is fixed) (#252).- An authorization
OperationsorRolesentry without ascopethrows aConfigurationExceptionwhen the principal is created; use the empty string for the default scope (#226). AddApplicationConfig(configuration)throws aConfigurationExceptioninstead of aNullReferenceExceptionwhen the section is missing or empty (#248).Arc4u.Configuration.Store.EfCore: the static classServiceCollectionExtensionsis renamedDbContextSectionStoreServiceCollectionExtensions(#245).ManageExceptionsFilterlogs the request context (HTTP method, path, action and route values) with the exception. In theDevelopmentenvironment its responses now carry the exception details unlessManageExceptionsFilterOptions.IncludeExceptionDetailsis set tofalse(#31).AddApplicationContext()no longer registersDefaultLoggingProperties: register it withAddKeyedScoped<IAddPropertiesToLog, DefaultLoggingProperties>("Scoped")to add theIdentityproperty to the log entries (#237).- Compared with the previews, the
Arc4u.Dependency.Toolpackage contains the generator as an analyzer only: no assembly is copied to the output of the application (#259). - APIs that need reflection are now marked
RequiresUnreferencedCodeand, when they build code at run time,RequiresDynamicCode. A trimmed or Native AOT application gets anIL2026orIL3050warning where it calls them; the message names the API to use instead. Nothing changes at run time (#277):Arc4u.Configuration.Store:Add(key, value),AddFromConfiguration(key, bind)(the value is serialized with reflection, even when the binding is generated),SectionEntity.GetValue<T>()andSetValue(value), andISectionStore.TryGetValueAsync<T>andTrySetValueAsync. Use the overloads that take aJsonTypeInfo<T>.ConfigurationHelper.GetConfigurationFromResourceStream(params string[]): use the overload that takes anAssembly.Graph<T>: the constructor that takes paths, theIncludessetter andParse.Arc4u.EfCore: theGraphExtensionmethods.
Bound<T>andInterval<T>annotateTwithDynamicallyAccessedMembers(public and non-public fields), like theIntervalhelpers already did. Generic code that passes its own type parameter asTmust carry the same annotation in a trimmed application (#277).Arc4u.OAuth2.Client.Authentication:CacheTokensserializes the tokens, the claims and the strings stored by Arc4u with source-generated metadata. Another type is serialized with reflection, which throws aNotSupportedExceptionin a Native AOT application (#277).Arc4u.Caching.Dapr:AddDaprCache(name, configuration, sectionName)reads only theNameof the section, the only value it registered (#277).
Deprecated
Arc4u.Diagnostics:AddSystemMonitoring(),SystemResources,CpuDataandMonitoringMessages.LogMonitoring, which log the CPU and memory usage of the process every ten seconds. Collect theSystem.Runtimemeter with OpenTelemetry instead. See Log CPU and memory usage.
Removed
AddMonitoringTimeElapsed()andLogMonitoringTimeElapsedMiddleware(Arc4u.OAuth2.AspNetCore),AddGrpcMonitoringTimeElapsed()andLogGrpcMonitoringTimeElapsedMiddleware(Arc4u.AspNetCore.gRpc), and theLogTimeToCompleteCallandTimeToCompleteMethodCalllog messages: the server spans and thehttp.server.request.durationmetric of ASP.NET Core already record the duration of every request. See Request timing middleware removed.IApplicationContext.ActivityID: theActivityIdlog property is the trace id of the currentSystem.Diagnostics.Activity(OpenTelemetry), added by the logger when an activity exists (#222).Arc4u.Standard.NServiceBus,Arc4u.Standard.NServiceBus.Core,Arc4u.Standard.NServiceBus.RabbitMQandArc4u.Prism.DI.Wpf: no 9.0 version (#189). They are deprecated on NuGet.org (#272). In 9, use Dapr pub/sub andPrism.DryIoc.- .NET 8 and .NET 9 targets, and the .NET Standard 2.0 target of every package except
Arc4u.DependencyandArc4u.Dependency.Tool. - ADAL (
Arc4u.Standard.OAuth2.AspNetCore.Adal), Protobuf serializers (Arc4u.Standard.Serializer.Protobuf,Arc4u.Standard.Serializer.ProtobufV2) andArc4u.Standard.Diagnostics.TraceListeners. - MSAL desktop support (
Arc4u.Standard.OAuth2.Msal). See MSAL status. IContainer,IContainerRegistry,IContainerResolve,ComponentModelContainer,DependencyContextand theArc4u.Standard.Dependency.ComponentModelpackage.Message,Messages,MessageType,LocalizedMessageandAppException, and the FluentValidation helpersToMessagesandToMessageType.Arc4u.Standard.OAuth2.AspNetCore.Api(ServiceAspectAttributefor controllers).Authentication:ClientSecrets(AddSecretAuthentication,CredentialSecretTokenProvider), replaced byAuthentication:ClientTokens.LoggerContextand theLog()-terminated logging API.- The
DataContractSerializer,DataContractJsonSerializerandXmlSerializerstring helpers, andArc4u.Utils.Enum<T>/EnumUtil. - The
[DataContract],[DataMember]and[EnumMember]attributes ofTokenInfo,ClaimDto,MessageCategory,UserProfile,Authorization,Operation,ScopedRoles,ScopedOperations,Graph<T>,Bound<T>,Interval<T>,Period,BoundTypeandBoundDirection: these types are serialized with System.Text.Json, including with a source-generatedJsonSerializerContext(#278). See Newtonsoft.Json replaced by System.Text.Json. - The AutoMapper dependency of
Arc4u.Diagnostics.Serilog.Sinks.RealmDb. - The change-tracking concept, obsolete with a domain-centric approach
(#266): the
Arc4u.Standard.Datapackage (IdEntity,PersistEntity, the audit entities,EntitySet<TEntity>,EntityItem<TEntity>,RelationAttribute,Validate<T>,ValidateAll),IPersistEntityandPersistChange,ChangeGraphTrackerandPersisteChangeExtensioninArc4u.EfCore, and theIsInsert,IsUpdate,IsDelete,IsNonerules andValidatorPredicatesinArc4u.FluentValidation.Graph<T>andGraphExtensionare kept. See Arc4u.Data and change tracking removed. Arc4u.gRPC:ClientErrorInterceptor. WithGrpc.Net.Clientit converted almost nothing, since a call fails when it is awaited, after the interceptor returned. Catch theRpcExceptionand useToResult()to get the server's ProblemDetails back.UserProfile.Name(Domain\SamAccountName): useEmail,PrincipalName, orDomainandSamAccountName(#237).IScopedLogger<T>andScopedLoggerWrapper<T>, which existed in the previews only: injectILogger<T>(#237).CustomRootCaExtensions.CertificateOptionKeys: read the names fromIOptions<CARootOptionKeys>(#226).NameClaimType,RoleClaimTypeandValidateAuthorityofOidcClientAuthenticationOptionsandOidcClientAuthenticationSectionOptions(Arc4u.OAuth2.Client.Authentication), which were never read (#233).HybridAuthenticationSectionOptions.OAuth2SettingsKeyandJwtAuthenticationSectionOptions.ValidateAudience, which were never read: for the audience, useAuthentication:OAuth2.Settings:ValidateAudience(#220).
Fixed
Arc4u.Configuration.Store:Add<T>(key)crashed with a stack overflow when the section store was added to aConfigurationBuilder(not toWebApplicationBuilder.Configuration): reading the initial value built the section store again. It now reads the section from the providers registered before the section store.- Blazor: the login popup of
BlazorTokenProviderwaited forever when the user closed it; it now throws aTimeoutExceptionafter 5 minutes. When the popup cannot be opened (CreatePopupWindow.jsnot loaded), anInvalidOperationExceptionsays so instead of a bareJSException. - Signing out threw a
NotImplementedExceptionwithAzureADOboTokenProviderandClientTokenProvider.AzureADOboTokenProvidernow removes from the cache the tokens of every API obtained for the user, also with a user access token refreshed since: the index is kept per user (the registeredIUserObjectIdentifier, otherwise theoidorsubclaim) and application (aud, otherwiseazporappid), both read from the user token (#113). ClientTokenProviderforgets the token kept in memory at the sign-out, also a token requested before it, and renews a token expiring in less than one minute (#113).BlazorMsalTokenProviderclears the principal at the sign-out, andUsernamePasswordTokenProvideralso removes the cached user name (#113).OidcClientIdentityModelTokenProviderkept returning the access token after the sign-out. The sign-out now clears the tokens in memory, cancels a login in progress and does not keep the tokens of a refresh or login in progress. It opens the browser logout only when the user had a session, sends the identity token of the login asid_token_hint(kept in the newTokenRefreshInfo.IdentityToken), bounds the logout byLoginTimeout, and still logs the user out of the authority when the secure cache fails (#113).OidcClientIdentityModelTokenProvider: concurrent calls refresh the token (or log the user in) once, since with refresh token rotation a second refresh failed and started an interactive login. The calls waiting for a login get its result: when the user cancels it, they fail instead of opening the browser again.LoginTimeout(5 minutes) bounds a login the user does not complete and the calls waiting for it (#113).OidcClientIdentityModelTokenProvider: a network error during a refresh keeps the session (the access token is still returned until it expires); onlyinvalid_grant,invalid_scopeand the interaction errors log the user in again (#113).OidcClientIdentityModelTokenProviderand its secure cache: an entry that cannot be decoded is removed and the user logs in again, instead of making every call fail; a transient read failure fails the call and keeps the entry; an inaccessible cache, or a serializer that does not supportTokenRefreshInfo, keeps the entry and logs the user in; a failing write no longer fails the call that obtained the tokens.SecureCacheTimeout(30 seconds) bounds the secure-cache operations of a sign-out or a token write, and the failures are logged with an optional logger (#113).AddOidcClientAuthenticationwith an action threw anArgumentNullException(parameteroptions) at startup whenClaimsIdentifierOptionswas not set. It now uses the object identifier claim types (ObjectIdentifierandOIDofClaimTypes), the default of the configuration based overload.- The access token was not in the
BootstrapContextof the identity in a cookie session, so theBootstraptoken provider and code written for bearer tokens did not work withoutUseOpenIdBearerInjector().StandardCookieEventsnow puts it there on each request, after refreshing it when needed (#90, #158). - With
UseOpenIdBearerInjector()and on-behalf-of settings,AzureADOboTokenProviderused the on-behalf-of token that the injector puts in theBootstrapContextas the user's token: another on-behalf-of exchange in the same request sent a token whose audience is not the application, and the sign-out looked for the cached tokens of the user in another index, so the token of the injector stayed in the cache. In a cookie session it now uses the access token of the cookie (TokenRefreshInfo), and theBootstrapContextonly when there is none (#275). - Blazor Interactive Server: the
AppPrincipalof a circuit was set once, when the circuit connected, and never followed its authentication state. When the circuit reconnected with another user or without a user (for example after a sign-out in another tab), or a revalidating provider turned the state anonymous, the event handlers kept acting as the previous user.ApplicationContextCircuitHandlerandAppPrincipalServerAuthenticationStateProvidernow update theIApplicationContextof the circuit onAuthenticationStateChanged, and clear its principal for an anonymous user (#171). OidcTokenProviderandRefreshTokenProviderthrew aNullReferenceExceptionwhen the user was not logged in yet (theAccessTokenandRefreshTokenofTokenRefreshInfoare null).OidcTokenProvidernow returns a failed result, or refreshes when only the refresh token is there, andRefreshTokenProviderthrows anInvalidOperationException(#161).- JWT bearer events missing in hybrid authentication.
TimeZoneContextconversion from a specific local time.- Anonymous calls through service discovery.
- Scope injection in
ILogger<T>. FlagsEnum.TryPowerOfTwoandTryPowerOfTwoExponentaccepted values that are not powers of two, soFlagValues<T>()also returnedNoneand composite values (#261).MemoryLogStore.GetLogsnever matched a criteria against a message with capital letters; the comparison now ignores case. Reading the memory log store while the sink wrote to it could throwCollection was modified(#238).RealmLoggingDbCtx, the singletonILogStoreregistered byAddRealmDBLog, opened itsRealmin its constructor, soGetLogsandRemoveAllthrewRealm accessed from incorrect threadfrom any other thread (every ASP.NET Core request). It now opens aRealmfor each call. The constructor no longer takes the unusedILoggerFactory, and theRealmproperty is removed (#239).RealmDBSinkopened aRealmfor every batch and never closed it, so instances and their native handles piled up until the garbage collector finalized them, and it wrote each event in its own transaction. It now opens and closes oneRealmper batch, without a synchronization context, and writes the batch in one transaction (#267).StandardBearerEvents.Challengeanswered a 401 with aProblemDetailsbody saying"status": 403and theapplication/jsoncontent type; the body now says 401 and the content type isapplication/problem+json(#221).WriteTo.CategoryFilterforwarded no event, andSimpleTextFormatter,MemoryLogDBandRealmDBlabelled every entryTechnical: they read theCategoryproperty as a number while the logger writes it as a string. The newHelper.GetCategoryreads the enum, its name, a registered name or its number, and keeps a category defined by the application instead of replacing it withTechnical;SimpleTextFormatterwrites such a category instead of failing (#235).- The properties added with
Add(and the message template arguments) were kept by the logger and written again with later entries; they are now written with that entry only (#273). - The Arc4u logger dropped the
EventIdof every entry; it is now forwarded to the logging providers (#236). ILogger<T>always used theIAddPropertiesToLogregistered under the keyTransient, so theIdentityproperty ofDefaultLoggingProperties(keyScoped) was never added to the log entries (#237).FluentLoggerlogged the errors other than aValidationErroror an exceptional error at theErrorlevel whatever the level given toLogIfFailed, and nothing gave it to FluentResults (Result.Setup), soLogIfFailedlogged nothing;AddResultLoggerandUseResultLoggernow do it (#251).ToGenericMessageignoredunexpectedTypein three of its four overloads (#250).- A bearer token issued for another audience is answered with a
detailthat names the rejected audience, and the expected audiences are logged (#35). AddJwtAuthenticationand the bearer scheme ofAddHybridAuthentication: a token without the claim that identifies the user, when the claims filler is enabled, gave a 500 error instead of a 401 (#224).UseResourcesRightValidationFornever detected a missing section (#223).ServerPrincipalCache.Put(key, timeout, value, isSlided)cached theisSlidedflag instead of the principal (#219).AddContextToPrincipalMiddlewarethrew when no activity was running, for example in integration tests (#124).RemoteClientSecretTokenProvidersentuser:passwordwithout the Base64 encoding that theBasicscheme requires (#121).- The on-behalf-of token was sent in an
access_tokenheader instead ofAuthorization: Bearerwith the defaultAuthenticationType(#229). - The keyed
IKeyValueSettingswere never registered, so theJwtHttpHandlerconstructors that take a settings name (Arc4u.OAuth2.Client.Authentication,Arc4u.OAuth.Msal) always threw,AppPrincipalFactory.CreatePrincipalAsync()andCreatePrincipalAsync(settingsResolveName)always returned a failed result, andAppPrincipalFactory.SignOutUserAsync(CancellationToken)always threw (#231). - Token cache keys depended on the process (
string.GetHashCode), and theClientCredentialskeys ignored the extra parameters (#232). BlazorMsalTokenProvidersends the user to the login page when MSAL cannot renew the token silently, instead of failing until the browser is restarted, andBlazorTokenProviderno longer reuses a token up to 5 minutes after it expired (#86).ToProblemDetailscalled on a successful result no longer adds an error to that result,ValidationError.WithMetadatakeeps the first value of a duplicate key instead of throwing, and theIsUtcDateTimeandIsDateOnlyrules have a default message (#252).BlazorControllerredirected to the static asset path used before the package rename, andCreatePopupWindow.jsopenedauthenticate.htmlwhile the file isAuthenticate.html, so the page was not found on a case-sensitive file system (#227).- The default section of
ReadAuthenticationCookieSettingsOption(Arc4u.OAuth2.Blazor) isAuthentication:OAuth2.Settings, as forAddAuthenticationCookie, instead ofAuthentication:OAuth.Settings(#226). AddCacheContextmatchedKindcase-insensitively, but theCacheContextit registered resolved it case-sensitively and always read theCachingsection, whateversectionName; nothing registered theICacheof each kind, whichAddMemoryCacheKind(),AddRedisCacheKinds(),AddSqlCacheKind()andAddDaprCacheKind()now do (#242).- The first access to a cache configured with
IsAutoStart: falsethrew anInvalidOperationException(#240). - A memory cache declared without a
Settingssection had a size limit of 100 bytes instead of 100 MB;MemoryCacheOption.SizeLimitInMBnow always holds megabytes (#241). AddMongoDatabaselost the connection-string options that it did not copy one by one (for exampledirectConnectionorcompressors): the settings are now created from the connection string.DefaultMongoClientFactory.CreateClientthrows aMongoClientExceptionwhen no settings are registered for the database, instead of connecting tolocalhost:27017(#254).GraphExtension.ApplyReferences(graph, query, path)always threw, and matched include paths by prefix instead of by segment (#253).Graph<T>:ExistandRemoveGraphmatched include paths by prefix instead of by segment (DepartmentmatchedDepartments.Manager), andExistignored the casing thatGetGraphandRemoveGraphrespect.GetGraphon a collection property threw aMissingMemberExceptionwhen the graph had paths below it.Parsecrashed with a stack overflow on an object graph with back references, threw on a non-empty collection of strings or value types, and returned paths for properties declared only by a subclass, which then failed validation.Includebuilt wrong paths withWhere,OrderBy,TakeorFirst(Lines.Active.Product) and accepted value types insideSelect; these operators now keep the path of their collection, and value types throw aMemberAccessException.Parseno longer reads non-public or static properties and indexers, and a property hidden withnewno longer throws anAmbiguousMatchException.- Configuration store: after
ResetAsync, the first poll published an empty data set (#244); a concurrent insert of the initial sections by another instance is accepted, insert errors are no longer swallowed, and the section store service no longer needsAddILogger()(the host failed to start withBad Arc4u usage.) (#245). - The secret decryptor read the earlier configuration sources again, so stream sources failed and remote providers were loaded twice (#246).
- Secret decryption with an ECDSA certificate failed silently or with unrelated exceptions; a certificate
with neither an RSA nor an EC key now throws a
NotSupportedException(#247), and a private key that cannot be read gives an explicit error (#40). Arc4u.Dependency.Tool:[Shared]with[Scoped]gave different lifetimes in the two generators (#257); such a class is now registered as scoped and reported asARC4UDEP005. A project without a.csfile at its root got noRegisterTypesmethod (#258).
Security
- Vulnerable dependencies upgraded (#130).
UseResourcesRightValidationForandAddValidateSwaggerRightForonly protected the exact path, so a user without the right got the OpenAPI document by changing its extension: Swashbuckle serves it forswagger.yamlandswagger.yml, and up to 6.5 for any extension (swagger.jsonx). The same path with another extension is now protected too (#112).CypherCodecencrypts with AES-GCM and a random nonce for each value (GenerateKey,Encrypt,Decrypt), so a modified value or a wrong key is detected.GenerateKeyAndIV,EncodeClearText,DecodeCypherstringandCypherCodecConfig.IVare removed: theEncryptionRijndaelsection only has aKey. Long values encrypted with a certificate now have the form<key>.<data>. Values encrypted with 8.x must be encrypted again (#114).- The authentication cookie of
AddOidcAuthenticationandAddHybridAuthentication, with or without a ticket store, is alwaysSecure,HttpOnlyandSameSite=Lax. It was onlySecurewhen the request was in HTTPS, so not behind a proxy that ends TLS (#110). Arc4u.Caching.SqlServer:System.Security.Cryptography.Pkcs, a dependency ofMicrosoft.Data.SqlClient, raised from 9.0.18 to 9.0.21 (CVE-2026-84008, high; CVE-2026-70162, moderate; both denial of service).
8.3.2 - 2025-07-18
Added
RefreshTokenLifetime(default 90 days) inOidcAuthenticationOptionsandOidcAuthenticationSectionOptions.- Audience and authority validation in
TokenResponseReceived, with explicit error messages. - Configurable claims to exclude and expiration claim type in
ClaimsFillerOptions;AddClaimsFilleraddsDefaultClaimsToExcludeand falls back to the OpenID settings. LogMonitoringTimeElapsedMiddlewaresupports minimal APIs.
Changed
- Token expiration logic aligned between
StandardCookieEventsandRefreshTokenProvider;ITokenRefreshProvider,OidcTokenProviderandRefreshTokenProviderrefactored. StandardBearerEventsno longer returns a generic error description on challenge.StandardOpenIdConnectEventsusesIOptionsMonitorand reports clearerOnRemoteFailureerrors.OpenIdBearerInjectorMiddlewaresetsClaimsIdentity.BootstrapContext.- gRPC:
AuthorizationInterceptorno longer depends onGrpcMethodInfoandServiceAspectAttribute;OAuth2Interceptorinjects the token based on theAuthenticationTypeand the current identity. Bound<T>: better nullability, internal setters forType,DirectionandValue,ToString()showsBound.Infinityfor null values.Interval<T>.GetHashCode()simplified;Period.ToString()formatting updated.
Fixed
BlazorMsalTokenProviderno longer fails on a premature null check of the principal.
8.3.1 - 2025-05-05
Fixed
- The OpenID Connect metadata address is derived from
DefaultAuthoritywhen it is not set, instead of being read before it was initialized.
8.3.0 - 2025-05-04
Added
net9.0target.IAuditEntity<TAuditedBy, TAuditedOn>interface (#151).- Retry mechanism in
CredentialTokenProvider(#146). - Warning when a cache storage size limit is zero (#148).
Changed
BlazorControllerupdated for the new authentication model (#147).- Time measurements use
Stopwatch.GetTimestamp()andStopwatch.GetElapsedTime()(#145). - Validation errors are grouped by severity in
ValidationProblemDetails. DaprCacheandMemoryCache: time-to-live written with the invariant culture, nullable annotations and better error messages.- Dependencies updated to .NET 9 (
System.Text.Json,Microsoft.Extensions.Hosting,Microsoft.AspNetCore.Authorization9.0.0).
Removed
net6.0target.Arc4u.Standard.OAuth2.AspNetCore.Adal,Arc4u.Standard.Serializer.Protobuf,Arc4u.Standard.Serializer.ProtobufV2andArc4u.Standard.Diagnostics.TraceListenersare no longer published as stable versions; their last stable version is 8.2.1 (8.3.0 previews exist).
8.2.1 - 2024-11-10
Fixed
- OpenID Connect redirect URIs are forced to
https(excepthttp://localhost), for Kubernetes deployments where TLS ends at the ingress.
8.2.0 - 2024-08-16
Added
- Extension methods for FluentResults in
Arc4u.Standard.Results, and methods that return the matching HTTP result: Ok, NoContent, ProblemDetails, ValidationProblemDetails and Created (with or without a location). - Encryption and decryption of long texts (more than 62 characters) by the configuration decryptor.
ValidateAudiencesetting (defaulttrue) for the OpenID Connect and OAuth2 scenarios.- A specific certificate for OpenID Connect and OAuth2 through an
IX509CertificateLoaderinstance (#109). - Minimal API support.
Changed
- The OpenID Connect registration no longer adds the
openidandoffline_accessscopes: add them to the configuration yourself. JwtHttpHandler(Arc4u.Standard.OAuth2.AspNetCore.Authentication) no longer swallows exceptions.
Deprecated
- The methods that inject
IScopedServiceProviderAccessor: useIServiceProvider(#108).
Removed
net7.0target.
Security
- Dependencies updated:
System.IdentityModel.Tokens.Jwt(CVE-2024-21319, moderate),Microsoft.Identity.Client(CVE-2024-27086, low),System.Text.Json8.0.4.
8.1.0 - 2024-02-07
Added
- Extension methods in
Arc4u.Standard.Resultsfor FluentResults. - A
Settingssection with aNamefor the Dapr cache, to select the Dapr state store component. - A default authentication scheme for services, so that a missing right returns 403.
Changed
AddIflogging extension takes aFunc<T>for the value; the previous overload is obsolete.- The activity ID moved from
AppPrincipalto theIApplicationContextimplementation. RootCertificateExtractorfor gRPC refactored.- Code uses the
IX509CertificateLoaderinterface instead ofX509CertificateLoader. - Dependencies updated.
Fixed
- The JSON decryptor configuration file supports several decryptors and works outside ASP.NET Core.
8.0.0 - 2023-12-15
Added
net8.0target (the packages targetnetstandard2.0,net6.0,net7.0andnet8.0).
Changed
- Version numbers follow semantic versioning.
6.1.18.1 - 2023-08-18
This version replaces ADAL and MSAL on the server side with the standard ASP.NET Core OpenID Connect and JWT bearer handlers, so that any standard identity provider works (tested with Microsoft Entra ID, Azure AD B2C, ADFS, Keycloak and ForgeRock). The settings are simplified and reorganized.
Deprecated
Arc4u.Standard.OAuth2.AspNetCore.Adal,Arc4u.Standard.Serializer.ProtobufandArc4u.Standard.Serializer.ProtobufV2.
Removed
Arc4u.Standard.OAuth2.AspNetCore.Msal, replaced byArc4u.Standard.OAuth2.AspNetCore.Authentication.Arc4u.Standard.KubeMQandArc4u.Standard.KubeMQ.AspNetCore: use a message broker through Dapr.
6.0.14.2 - 2023-03-08
Added
- A custom
IX509CertificateLoadercan be injected in the configuration decryptor.
Fixed
- The certificate loader of the configuration decryptor when no parameters are specified.
6.0.14.1 - 2023-02-17
Added
- The configuration decryptor reads its certificate from PEM files (public and private key), for Kubernetes secrets.
6.0.13.1 - 2023-02-15
Changed
- Dependencies updated to the matching .NET version.
6.0.12.1 - 2023-02-15
Changed
- Dependencies updated to the matching .NET version.
6.0.11.2 - 2023-02-15
Added
Arc4u.Standard.Configuration.Decryptor: decrypts encrypted configuration values on the fly. See Configuration.- Code analysis rules (
.editorconfigof ASP.NET Core).
Changed
- Configuration values are converted independently of the culture.
- Dependencies updated (.NET 6.0.11 and 7.0.1 kept).
Fixed
- The Blazor client resolved its settings from
OAuthinstead ofOAuth2.
6.0.11.1 - 2023-01-11
Added
Arc4u.Standard.OAuth2.AspNetCore.Authentication: OpenID Connect and OAuth2 authentication based onMicrosoft.AspNetCore.Authentication.OpenIdConnectandMicrosoft.AspNetCore.Authentication.JwtBearer, tested with Azure AD, Azure AD B2C and Keycloak.- Support for .NET 6.0.11 and .NET 7.0.1.
Changed
OAuthConfigis removed.ITokenUserCacheConfigurationreads the claims that identify a user,IUserObjectIdentifierreturns the unique identifier used for the token cache keys, andKeyGeneratorFromIdentityusesIUserObjectIdentifier. The ADAL and MSAL packages use these interfaces.
6.0.9.1 - 2022-09-17
Changed
- Dependencies updated to .NET 6.0.9.
6.0.8.2 - 2022-09-16
Security
- Fixed the user rights check of
ServiceAspect(see 5.0.17.3).
5.0.17.3 - 2022-09-19
Security
- Fixed the user rights check of
ServiceAspect. Since .NET 5, attributes are created once and not per call, so anIApplicationContextinjected in the attribute held the context of the first user.
5.0.10.3 - 2021-09-30
Added
- FluentValidation rules
IsUtcDateTimeandIsDate.
5.0.10.2 - 2021-09-30
Changed
- The FluentValidation rules
IsInsert,IsUpdate,IsDeleteandIsNonework onIPersistEntity. IPersistEntityandPersistChangemoved fromArc4u.Standard.DatatoArc4u.Standard; the EF Core change tracker is based onIPersistEntity.
5.0.10.1 - 2021-09-28
Added
Arc4u.Standard.FluentValidationwith theIsInsert,IsUpdate,IsDeleteandIsNonerules.
Changed
- The Blazor authentication pop-up window is relative to the base path of the application.
5.0.8.2 - 2021-08-19
Fixed
- ADFS: the access token type returned by
CredentialTokenProvider.JwtHttpHandlerhandles theInjectauthentication type.
5.0.8.1 - 2021-08-11
Added
- Blazor: extension methods to set the culture and to add policies based on operations.
Changed
- Updated to .NET 5.0.8.
JwtHttpHandlerrefactored; its constructor takesIHttpContextAccessorfor the OAuth2 and OpenID Connect scenarios.- EF Core change tracker based on
PersistEntity. - KubeMQ uses the serializers of the framework.
Fixed
- gRPC
ClientErrorInterceptorduring server streaming.
5.0.6.1 - 2021-06-29
Added
JsonSerializationbased on System.Text.Json.OboTokenProviderandOboClientTokenProvider.- KubeMQ 2.2.8 features.
Changed
JwtHttpHandlerrefactored to work withIHttpClientFactory.
Removed
ISerializationFactory: inject theIObjectSerializationyou need.
5.0.6 - 2021-05-21
Changed
- Updated to ASP.NET Core 5.0.6.
5.0.5 - 2021-05-21
Added
- First release based on ASP.NET Core 5.0.5.