Skip to content

Commit c67cbd0

Browse files
committed
WIP
1 parent debb78a commit c67cbd0

File tree

5 files changed

+52
-50
lines changed

5 files changed

+52
-50
lines changed

src/Components/Components/src/PersistentState/PersistentServicesRegistry.cs

Lines changed: 15 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -89,6 +89,7 @@ private static void PersistInstanceState(object instance, Type type, PersistentC
8989
{
9090
state.PersistAsJson(key, value, propertyType);
9191
}
92+
Console.WriteLine($"[Persist] type: {instance.GetType()}, propertyType: {propertyType}, key: {key}, result: {value}");
9293
}
9394
}
9495

@@ -136,6 +137,7 @@ private static void RestoreInstanceState(object instance, Type type, PersistentC
136137
var (setter, getter) = accessors.GetAccessor(key);
137138
setter.SetValue(instance, result!);
138139
}
140+
Console.WriteLine($"[Restore] type: {instance.GetType()}, propertyType: {propertyType}, key: {key}, result: {result}");
139141
}
140142
}
141143

@@ -211,8 +213,19 @@ private static string ComputeKey(Type keyType, string propertyName)
211213
// This happens once per type and property combo, so allocations are ok.
212214
var assemblyName = keyType.Assembly.FullName;
213215
var typeName = keyType.FullName;
214-
var input = Encoding.UTF8.GetBytes(string.Join(".", assemblyName, typeName, propertyName));
215-
return Convert.ToBase64String(SHA256.HashData(input));
216+
217+
// Internal classes can be bundled in different assemblies during prerendering and WASM rendering.
218+
bool isTypeInternal = (!keyType.IsPublic && !keyType.IsNested) || keyType.IsNestedAssembly;
219+
var inputString = isTypeInternal
220+
? string.Join(".", typeName, propertyName)
221+
: string.Join(".", assemblyName, typeName, propertyName);
222+
223+
var input = Encoding.UTF8.GetBytes(inputString);
224+
var hash = SHA256.HashData(input);
225+
var key = Convert.ToBase64String(hash);
226+
227+
Console.WriteLine($"[ComputeKey] inputString: {inputString}, key: {key}");
228+
return key;
216229
}
217230

218231
internal static IEnumerable<PropertyInfo> GetCandidateBindableProperties(

src/Components/WebAssembly/Server/src/AuthenticationStateSerializer.cs

Lines changed: 11 additions & 28 deletions
Original file line numberDiff line numberDiff line change
@@ -2,51 +2,34 @@
22
// The .NET Foundation licenses this file to you under the MIT license.
33

44
using Microsoft.AspNetCore.Components.Authorization;
5-
using Microsoft.AspNetCore.Components.Web;
65
using Microsoft.Extensions.Options;
76

87
namespace Microsoft.AspNetCore.Components.WebAssembly.Server;
98

10-
internal sealed class AuthenticationStateSerializer : IHostEnvironmentAuthenticationStateProvider, IDisposable
9+
internal sealed class AuthenticationStateSerializer : IHostEnvironmentAuthenticationStateProvider
1110
{
12-
// Do not change. This must match all versions of the server-side DeserializedAuthenticationStateProvider.PersistenceKey.
13-
internal const string PersistenceKey = $"__internal__{nameof(AuthenticationState)}";
14-
15-
private readonly PersistentComponentState _state;
1611
private readonly Func<AuthenticationState, ValueTask<AuthenticationStateData?>> _serializeCallback;
17-
private readonly PersistingComponentStateSubscription _subscription;
1812

19-
private Task<AuthenticationState>? _authenticationStateTask;
13+
[SupplyParameterFromPersistentComponentState]
14+
public AuthenticationStateData? CurrentAuthenticationState { get; set; }
2015

21-
public AuthenticationStateSerializer(PersistentComponentState persistentComponentState, IOptions<AuthenticationStateSerializationOptions> options)
16+
public AuthenticationStateSerializer(IOptions<AuthenticationStateSerializationOptions> options)
2217
{
23-
_state = persistentComponentState;
2418
_serializeCallback = options.Value.SerializationCallback;
25-
_subscription = persistentComponentState.RegisterOnPersisting(OnPersistingAsync, RenderMode.InteractiveWebAssembly);
26-
}
27-
28-
private async Task OnPersistingAsync()
29-
{
30-
if (_authenticationStateTask is null)
31-
{
32-
throw new InvalidOperationException($"{nameof(SetAuthenticationState)} must be called before the {nameof(PersistentComponentState)}.{nameof(PersistentComponentState.RegisterOnPersisting)} callback.");
33-
}
34-
35-
var authenticationStateData = await _serializeCallback(await _authenticationStateTask);
36-
if (authenticationStateData is not null)
37-
{
38-
_state.PersistAsJson(PersistenceKey, authenticationStateData);
39-
}
4019
}
4120

4221
/// <inheritdoc />
4322
public void SetAuthenticationState(Task<AuthenticationState> authenticationStateTask)
4423
{
45-
_authenticationStateTask = authenticationStateTask ?? throw new ArgumentNullException(nameof(authenticationStateTask));
24+
ArgumentNullException.ThrowIfNull(authenticationStateTask, nameof(authenticationStateTask));
25+
26+
// fire and forget, not good... This method can throw, especially on serialization.
27+
_ = SetAuthenticationStateAsync(authenticationStateTask);
4628
}
4729

48-
public void Dispose()
30+
private async Task SetAuthenticationStateAsync(Task<AuthenticationState> authenticationStateTask)
4931
{
50-
_subscription.Dispose();
32+
var authenticationState = await authenticationStateTask;
33+
CurrentAuthenticationState = await _serializeCallback(authenticationState);
5134
}
5235
}

src/Components/WebAssembly/Server/src/WebAssemblyRazorComponentsBuilderExtensions.cs

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,9 @@
44
using Microsoft.AspNetCore.Components;
55
using Microsoft.AspNetCore.Components.Authorization;
66
using Microsoft.AspNetCore.Components.Endpoints.Infrastructure;
7+
using Microsoft.AspNetCore.Components.Infrastructure;
78
using Microsoft.AspNetCore.Components.WebAssembly.Server;
9+
using Microsoft.AspNetCore.Components.Web;
810
using Microsoft.AspNetCore.Components.WebAssembly.Services;
911
using Microsoft.Extensions.DependencyInjection.Extensions;
1012

@@ -41,6 +43,8 @@ public static IRazorComponentsBuilder AddInteractiveWebAssemblyComponents(this I
4143
public static IRazorComponentsBuilder AddAuthenticationStateSerialization(this IRazorComponentsBuilder builder, Action<AuthenticationStateSerializationOptions>? configure = null)
4244
{
4345
builder.Services.TryAddEnumerable(ServiceDescriptor.Scoped<IHostEnvironmentAuthenticationStateProvider, AuthenticationStateSerializer>());
46+
builder.Services.TryAddScoped(sp => (AuthenticationStateSerializer)sp.GetRequiredService<IHostEnvironmentAuthenticationStateProvider>());
47+
RegisterPersistentComponentStateServiceCollectionExtensions.AddPersistentServiceRegistration<AuthenticationStateSerializer>(builder.Services, RenderMode.InteractiveAuto);
4448
if (configure is not null)
4549
{
4650
builder.Services.Configure(configure);
Lines changed: 16 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -1,40 +1,37 @@
11
// Licensed to the .NET Foundation under one or more agreements.
22
// The .NET Foundation licenses this file to you under the MIT license.
33

4-
using System.Diagnostics.CodeAnalysis;
54
using System.Security.Claims;
65
using Microsoft.AspNetCore.Components.Authorization;
76
using Microsoft.Extensions.Options;
8-
using static Microsoft.AspNetCore.Internal.LinkerFlags;
97

108
namespace Microsoft.AspNetCore.Components.WebAssembly.Authentication;
119

1210
internal sealed class DeserializedAuthenticationStateProvider : AuthenticationStateProvider
1311
{
14-
// Do not change. This must match all versions of the server-side AuthenticationStateSerializer.PersistenceKey.
15-
private const string PersistenceKey = $"__internal__{nameof(AuthenticationState)}";
12+
// restoring part is on DeserializedAuthenticationStateProvider but persisting part is on AuthenticationStateSerializer
13+
// how can we make the key the same if these are two different classes in different assemblies?
14+
// should we merge them and move to the Shared folder? Or should we allow passing a custom key to [SupplyParameterFromPersistentComponentState] attribute?
15+
private readonly Func<AuthenticationStateData?, Task<AuthenticationState>> _deserializeCallback;
16+
17+
[SupplyParameterFromPersistentComponentState]
18+
public AuthenticationStateData? CurrentAuthenticationState { get; set; }
1619

1720
private static readonly Task<AuthenticationState> _defaultUnauthenticatedTask =
1821
Task.FromResult(new AuthenticationState(new ClaimsPrincipal(new ClaimsIdentity())));
1922

20-
private readonly Task<AuthenticationState> _authenticationStateTask = _defaultUnauthenticatedTask;
23+
public DeserializedAuthenticationStateProvider(IOptions<AuthenticationStateDeserializationOptions> options)
24+
{
25+
_deserializeCallback = options.Value.DeserializationCallback;
26+
}
2127

22-
[UnconditionalSuppressMessage(
23-
"Trimming",
24-
"IL2026:Members annotated with 'RequiresUnreferencedCodeAttribute' require dynamic access otherwise can break functionality when trimming application code",
25-
Justification = $"{nameof(DeserializedAuthenticationStateProvider)} uses the {nameof(DynamicDependencyAttribute)} to preserve the necessary members.")]
26-
[DynamicDependency(JsonSerialized, typeof(AuthenticationStateData))]
27-
[DynamicDependency(JsonSerialized, typeof(IList<ClaimData>))]
28-
[DynamicDependency(JsonSerialized, typeof(ClaimData))]
29-
public DeserializedAuthenticationStateProvider(PersistentComponentState state, IOptions<AuthenticationStateDeserializationOptions> options)
28+
public override Task<AuthenticationState> GetAuthenticationStateAsync()
3029
{
31-
if (!state.TryTakeFromJson<AuthenticationStateData?>(PersistenceKey, out var authenticationStateData) || authenticationStateData is null)
30+
if (CurrentAuthenticationState is null)
3231
{
33-
return;
32+
return _defaultUnauthenticatedTask;
3433
}
35-
36-
_authenticationStateTask = options.Value.DeserializationCallback(authenticationStateData);
34+
var authenticationState = _deserializeCallback(CurrentAuthenticationState);
35+
return authenticationState;
3736
}
38-
39-
public override Task<AuthenticationState> GetAuthenticationStateAsync() => _authenticationStateTask;
4037
}

src/Components/WebAssembly/WebAssembly.Authentication/src/WebAssemblyAuthenticationServiceCollectionExtensions.cs

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,8 @@
55
using System.Reflection;
66
using Microsoft.AspNetCore.Components;
77
using Microsoft.AspNetCore.Components.Authorization;
8+
using Microsoft.AspNetCore.Components.Infrastructure;
9+
using Microsoft.AspNetCore.Components.Web;
810
using Microsoft.AspNetCore.Components.WebAssembly.Authentication;
911
using Microsoft.AspNetCore.Components.WebAssembly.Authentication.Internal;
1012
using Microsoft.Extensions.DependencyInjection.Extensions;
@@ -29,7 +31,10 @@ public static class WebAssemblyAuthenticationServiceCollectionExtensions
2931
public static IServiceCollection AddAuthenticationStateDeserialization(this IServiceCollection services, Action<AuthenticationStateDeserializationOptions>? configure = null)
3032
{
3133
services.AddOptions();
32-
services.TryAddScoped<AuthenticationStateProvider, DeserializedAuthenticationStateProvider>();
34+
services.TryAddSingleton<AuthenticationStateProvider, DeserializedAuthenticationStateProvider>();
35+
services.TryAddSingleton(sp => (DeserializedAuthenticationStateProvider)sp.GetRequiredService<AuthenticationStateProvider>());
36+
RegisterPersistentComponentStateServiceCollectionExtensions.AddPersistentServiceRegistration<DeserializedAuthenticationStateProvider>(services, RenderMode.InteractiveWebAssembly);
37+
3338
if (configure != null)
3439
{
3540
services.Configure(configure);

0 commit comments

Comments
 (0)