diff --git a/src/Components/WebAssembly/Server/src/AuthenticationStateSerializer.cs b/src/Components/WebAssembly/Server/src/AuthenticationStateSerializer.cs index 4c4a83e23196..7793601885dc 100644 --- a/src/Components/WebAssembly/Server/src/AuthenticationStateSerializer.cs +++ b/src/Components/WebAssembly/Server/src/AuthenticationStateSerializer.cs @@ -2,51 +2,37 @@ // The .NET Foundation licenses this file to you under the MIT license. using Microsoft.AspNetCore.Components.Authorization; -using Microsoft.AspNetCore.Components.Web; using Microsoft.Extensions.Options; namespace Microsoft.AspNetCore.Components.WebAssembly.Server; -internal sealed class AuthenticationStateSerializer : IHostEnvironmentAuthenticationStateProvider, IDisposable +internal sealed class AuthenticationStateSerializer : AuthenticationStateProvider, IHostEnvironmentAuthenticationStateProvider { // Do not change. This must match all versions of the server-side DeserializedAuthenticationStateProvider.PersistenceKey. internal const string PersistenceKey = $"__internal__{nameof(AuthenticationState)}"; - private readonly PersistentComponentState _state; private readonly Func> _serializeCallback; - private readonly PersistingComponentStateSubscription _subscription; private Task? _authenticationStateTask; - public AuthenticationStateSerializer(PersistentComponentState persistentComponentState, IOptions options) - { - _state = persistentComponentState; - _serializeCallback = options.Value.SerializationCallback; - _subscription = persistentComponentState.RegisterOnPersisting(OnPersistingAsync, RenderMode.InteractiveWebAssembly); - } + [SupplyParameterFromPersistentComponentState] + public AuthenticationStateData? CurrentAuthenticationState { get; set; } - private async Task OnPersistingAsync() + public AuthenticationStateSerializer(IOptions options) { - if (_authenticationStateTask is null) - { - throw new InvalidOperationException($"{nameof(SetAuthenticationState)} must be called before the {nameof(PersistentComponentState)}.{nameof(PersistentComponentState.RegisterOnPersisting)} callback."); - } - - var authenticationStateData = await _serializeCallback(await _authenticationStateTask); - if (authenticationStateData is not null) - { - _state.PersistAsJson(PersistenceKey, authenticationStateData); - } + _serializeCallback = options.Value.SerializationCallback; } /// - public void SetAuthenticationState(Task authenticationStateTask) + public async void SetAuthenticationState(Task authenticationStateTask) { _authenticationStateTask = authenticationStateTask ?? throw new ArgumentNullException(nameof(authenticationStateTask)); + + CurrentAuthenticationState = await _serializeCallback(await _authenticationStateTask); + NotifyAuthenticationStateChanged(_authenticationStateTask); } - public void Dispose() - { - _subscription.Dispose(); - } + /// + public override Task GetAuthenticationStateAsync() => + _authenticationStateTask ?? Task.FromResult(new AuthenticationState(new System.Security.Claims.ClaimsPrincipal())); } diff --git a/src/Components/WebAssembly/Server/src/WebAssemblyRazorComponentsBuilderExtensions.cs b/src/Components/WebAssembly/Server/src/WebAssemblyRazorComponentsBuilderExtensions.cs index d783bede44f5..d93981f4f531 100644 --- a/src/Components/WebAssembly/Server/src/WebAssemblyRazorComponentsBuilderExtensions.cs +++ b/src/Components/WebAssembly/Server/src/WebAssemblyRazorComponentsBuilderExtensions.cs @@ -4,6 +4,8 @@ using Microsoft.AspNetCore.Components; using Microsoft.AspNetCore.Components.Authorization; using Microsoft.AspNetCore.Components.Endpoints.Infrastructure; +using Microsoft.AspNetCore.Components.Infrastructure; +using Microsoft.AspNetCore.Components.Web; using Microsoft.AspNetCore.Components.WebAssembly.Server; using Microsoft.AspNetCore.Components.WebAssembly.Services; using Microsoft.Extensions.DependencyInjection.Extensions; @@ -41,6 +43,9 @@ public static IRazorComponentsBuilder AddInteractiveWebAssemblyComponents(this I public static IRazorComponentsBuilder AddAuthenticationStateSerialization(this IRazorComponentsBuilder builder, Action? configure = null) { builder.Services.TryAddEnumerable(ServiceDescriptor.Scoped()); + builder.Services.TryAddEnumerable(ServiceDescriptor.Scoped()); + builder.Services.TryAddScoped(sp => (AuthenticationStateSerializer)sp.GetRequiredService()); + RegisterPersistentComponentStateServiceCollectionExtensions.AddPersistentServiceRegistration(builder.Services, RenderMode.InteractiveAuto); if (configure is not null) { builder.Services.Configure(configure); diff --git a/src/Components/WebAssembly/WebAssembly.Authentication/src/Services/DeserializedAuthenticationStateProvider.cs b/src/Components/WebAssembly/WebAssembly.Authentication/src/Services/DeserializedAuthenticationStateProvider.cs index bff25cd51fbe..da37acc5a4cc 100644 --- a/src/Components/WebAssembly/WebAssembly.Authentication/src/Services/DeserializedAuthenticationStateProvider.cs +++ b/src/Components/WebAssembly/WebAssembly.Authentication/src/Services/DeserializedAuthenticationStateProvider.cs @@ -1,39 +1,27 @@ // Licensed to the .NET Foundation under one or more agreements. // The .NET Foundation licenses this file to you under the MIT license. -using System.Diagnostics.CodeAnalysis; using System.Security.Claims; using Microsoft.AspNetCore.Components.Authorization; using Microsoft.Extensions.Options; -using static Microsoft.AspNetCore.Internal.LinkerFlags; namespace Microsoft.AspNetCore.Components.WebAssembly.Authentication; internal sealed class DeserializedAuthenticationStateProvider : AuthenticationStateProvider { - // Do not change. This must match all versions of the server-side AuthenticationStateSerializer.PersistenceKey. - private const string PersistenceKey = $"__internal__{nameof(AuthenticationState)}"; - private static readonly Task _defaultUnauthenticatedTask = Task.FromResult(new AuthenticationState(new ClaimsPrincipal(new ClaimsIdentity()))); - private readonly Task _authenticationStateTask = _defaultUnauthenticatedTask; + private readonly Task _authenticationStateTask; - [UnconditionalSuppressMessage( - "Trimming", - "IL2026:Members annotated with 'RequiresUnreferencedCodeAttribute' require dynamic access otherwise can break functionality when trimming application code", - Justification = $"{nameof(DeserializedAuthenticationStateProvider)} uses the {nameof(DynamicDependencyAttribute)} to preserve the necessary members.")] - [DynamicDependency(JsonSerialized, typeof(AuthenticationStateData))] - [DynamicDependency(JsonSerialized, typeof(IList))] - [DynamicDependency(JsonSerialized, typeof(ClaimData))] - public DeserializedAuthenticationStateProvider(PersistentComponentState state, IOptions options) - { - if (!state.TryTakeFromJson(PersistenceKey, out var authenticationStateData) || authenticationStateData is null) - { - return; - } + [SupplyParameterFromPersistentComponentState] + public AuthenticationStateData? CurrentAuthenticationState { get; set; } - _authenticationStateTask = options.Value.DeserializationCallback(authenticationStateData); + public DeserializedAuthenticationStateProvider(IOptions options) + { + _authenticationStateTask = CurrentAuthenticationState is not null + ? options.Value.DeserializationCallback(CurrentAuthenticationState) + : _defaultUnauthenticatedTask; } public override Task GetAuthenticationStateAsync() => _authenticationStateTask; diff --git a/src/Components/WebAssembly/WebAssembly.Authentication/src/WebAssemblyAuthenticationServiceCollectionExtensions.cs b/src/Components/WebAssembly/WebAssembly.Authentication/src/WebAssemblyAuthenticationServiceCollectionExtensions.cs index 20cf1e0867f7..6d3c95e34145 100644 --- a/src/Components/WebAssembly/WebAssembly.Authentication/src/WebAssemblyAuthenticationServiceCollectionExtensions.cs +++ b/src/Components/WebAssembly/WebAssembly.Authentication/src/WebAssemblyAuthenticationServiceCollectionExtensions.cs @@ -5,6 +5,8 @@ using System.Reflection; using Microsoft.AspNetCore.Components; using Microsoft.AspNetCore.Components.Authorization; +using Microsoft.AspNetCore.Components.Infrastructure; +using Microsoft.AspNetCore.Components.Web; using Microsoft.AspNetCore.Components.WebAssembly.Authentication; using Microsoft.AspNetCore.Components.WebAssembly.Authentication.Internal; using Microsoft.Extensions.DependencyInjection.Extensions; @@ -29,7 +31,8 @@ public static class WebAssemblyAuthenticationServiceCollectionExtensions public static IServiceCollection AddAuthenticationStateDeserialization(this IServiceCollection services, Action? configure = null) { services.AddOptions(); - services.TryAddScoped(); + services.TryAddSingleton(); + RegisterPersistentComponentStateServiceCollectionExtensions.AddPersistentServiceRegistration(services, RenderMode.InteractiveWebAssembly); if (configure != null) { services.Configure(configure);