-
Notifications
You must be signed in to change notification settings - Fork 10.6k
Build in AuthenticationStateProviders from project templates #55821
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Changes from all commits
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +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.Security.Claims; | ||
|
|
||
| namespace Microsoft.AspNetCore.Components.Authorization; | ||
|
|
||
| /// <summary> | ||
| /// A JSON-serializable type that represents the data that is used to create an <see cref="AuthenticationState"/>. | ||
| /// </summary> | ||
| public class AuthenticationStateData | ||
| { | ||
| /// <summary> | ||
| /// The client-readable claims that describe the <see cref="AuthenticationState.User"/>. | ||
| /// </summary> | ||
| public IList<KeyValuePair<string, string>> Claims { get; set; } = []; | ||
|
|
||
| /// <summary> | ||
| /// Gets the value that identifies 'Name' claims. This is used when returning the property <see cref="ClaimsIdentity.Name"/>. | ||
| /// </summary> | ||
| public string NameClaimType { get; set; } = ClaimsIdentity.DefaultNameClaimType; | ||
|
|
||
| /// <summary> | ||
| /// Gets the value that identifies 'Role' claims. This is used when calling <see cref="ClaimsPrincipal.IsInRole"/>. | ||
| /// </summary> | ||
| public string RoleClaimType { get; set; } = ClaimsIdentity.DefaultRoleClaimType; | ||
| } |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -1 +1,9 @@ | ||
| #nullable enable | ||
| Microsoft.AspNetCore.Components.Authorization.AuthenticationStateData | ||
| Microsoft.AspNetCore.Components.Authorization.AuthenticationStateData.AuthenticationStateData() -> void | ||
| Microsoft.AspNetCore.Components.Authorization.AuthenticationStateData.Claims.get -> System.Collections.Generic.IList<System.Collections.Generic.KeyValuePair<string!, string!>>! | ||
| Microsoft.AspNetCore.Components.Authorization.AuthenticationStateData.Claims.set -> void | ||
| Microsoft.AspNetCore.Components.Authorization.AuthenticationStateData.NameClaimType.get -> string! | ||
| Microsoft.AspNetCore.Components.Authorization.AuthenticationStateData.NameClaimType.set -> void | ||
| Microsoft.AspNetCore.Components.Authorization.AuthenticationStateData.RoleClaimType.get -> string! | ||
| Microsoft.AspNetCore.Components.Authorization.AuthenticationStateData.RoleClaimType.set -> void |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -1,4 +1,8 @@ | ||
| #nullable enable | ||
| Microsoft.AspNetCore.Components.Routing.RazorComponentsEndpointHttpContextExtensions | ||
| Microsoft.AspNetCore.Components.Server.ServerAuthenticationStateProvider | ||
| Microsoft.AspNetCore.Components.Server.ServerAuthenticationStateProvider.ServerAuthenticationStateProvider() -> void | ||
| Microsoft.AspNetCore.Components.Server.ServerAuthenticationStateProvider.SetAuthenticationState(System.Threading.Tasks.Task<Microsoft.AspNetCore.Components.Authorization.AuthenticationState!>! authenticationStateTask) -> void | ||
| override Microsoft.AspNetCore.Components.Server.ServerAuthenticationStateProvider.GetAuthenticationStateAsync() -> System.Threading.Tasks.Task<Microsoft.AspNetCore.Components.Authorization.AuthenticationState!>! | ||
|
Comment on lines
+3
to
+6
Member
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Is this public because of layering?
Member
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Or because we want it to be extendable
Member
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I mention this in the PR description:
This change isn't strictly necessary for this PR, but I do think it helps with the general usability |
||
| static Microsoft.AspNetCore.Components.Endpoints.Infrastructure.ComponentEndpointConventionBuilderHelper.GetEndpointRouteBuilder(Microsoft.AspNetCore.Builder.RazorComponentsEndpointConventionBuilder! builder) -> Microsoft.AspNetCore.Routing.IEndpointRouteBuilder! | ||
| static Microsoft.AspNetCore.Components.Routing.RazorComponentsEndpointHttpContextExtensions.AcceptsInteractiveRouting(this Microsoft.AspNetCore.Http.HttpContext! context) -> bool | ||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,6 @@ | ||
| // Licensed to the .NET Foundation under one or more agreements. | ||
| // The .NET Foundation licenses this file to you under the MIT license. | ||
|
|
||
| using System.Runtime.CompilerServices; | ||
|
|
||
| [assembly: TypeForwardedTo(typeof(Microsoft.AspNetCore.Components.Server.ServerAuthenticationStateProvider))] |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -1,4 +1,11 @@ | ||
| #nullable enable | ||
| *REMOVED*Microsoft.AspNetCore.Components.Server.ServerAuthenticationStateProvider | ||
| *REMOVED*Microsoft.AspNetCore.Components.Server.ServerAuthenticationStateProvider.ServerAuthenticationStateProvider() -> void | ||
| *REMOVED*Microsoft.AspNetCore.Components.Server.ServerAuthenticationStateProvider.SetAuthenticationState(System.Threading.Tasks.Task<Microsoft.AspNetCore.Components.Authorization.AuthenticationState!>! authenticationStateTask) -> void | ||
| *REMOVED*override Microsoft.AspNetCore.Components.Server.ServerAuthenticationStateProvider.GetAuthenticationStateAsync() -> System.Threading.Tasks.Task<Microsoft.AspNetCore.Components.Authorization.AuthenticationState!>! | ||
|
Comment on lines
+2
to
+5
Member
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Isn't this breaking? (For libraries for example).
Member
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. The |
||
| Microsoft.AspNetCore.Components.Server.ServerAuthenticationStateProvider (forwarded, contained in Microsoft.AspNetCore.Components.Endpoints) | ||
| Microsoft.AspNetCore.Components.Server.ServerAuthenticationStateProvider.ServerAuthenticationStateProvider() -> void (forwarded, contained in Microsoft.AspNetCore.Components.Endpoints) | ||
| Microsoft.AspNetCore.Components.Server.ServerAuthenticationStateProvider.SetAuthenticationState(System.Threading.Tasks.Task<Microsoft.AspNetCore.Components.Authorization.AuthenticationState!>! authenticationStateTask) -> void (forwarded, contained in Microsoft.AspNetCore.Components.Endpoints) | ||
| Microsoft.AspNetCore.Components.Server.ServerComponentsEndpointOptions | ||
| Microsoft.AspNetCore.Components.Server.ServerComponentsEndpointOptions.ConfigureWebSocketAcceptContext.get -> System.Func<Microsoft.AspNetCore.Http.HttpContext!, Microsoft.AspNetCore.Http.WebSocketAcceptContext!, System.Threading.Tasks.Task!>? | ||
| Microsoft.AspNetCore.Components.Server.ServerComponentsEndpointOptions.ConfigureWebSocketAcceptContext.set -> void | ||
|
|
@@ -7,4 +14,5 @@ Microsoft.AspNetCore.Components.Server.ServerComponentsEndpointOptions.ContentSe | |
| Microsoft.AspNetCore.Components.Server.ServerComponentsEndpointOptions.DisableWebSocketCompression.get -> bool | ||
| Microsoft.AspNetCore.Components.Server.ServerComponentsEndpointOptions.DisableWebSocketCompression.set -> void | ||
| Microsoft.AspNetCore.Components.Server.ServerComponentsEndpointOptions.ServerComponentsEndpointOptions() -> void | ||
| override Microsoft.AspNetCore.Components.Server.ServerAuthenticationStateProvider.GetAuthenticationStateAsync() -> System.Threading.Tasks.Task<Microsoft.AspNetCore.Components.Authorization.AuthenticationState!>! (forwarded, contained in Microsoft.AspNetCore.Components.Endpoints) | ||
| static Microsoft.AspNetCore.Builder.ServerRazorComponentsEndpointConventionBuilderExtensions.AddInteractiveServerRenderMode(this Microsoft.AspNetCore.Builder.RazorComponentsEndpointConventionBuilder! builder, System.Action<Microsoft.AspNetCore.Components.Server.ServerComponentsEndpointOptions!>! configure) -> Microsoft.AspNetCore.Builder.RazorComponentsEndpointConventionBuilder! | ||
| Original file line number | Diff line number | Diff line change | ||||||||
|---|---|---|---|---|---|---|---|---|---|---|
| @@ -0,0 +1,73 @@ | ||||||||||
| // Licensed to the .NET Foundation under one or more agreements. | ||||||||||
| // The .NET Foundation licenses this file to you under the MIT license. | ||||||||||
|
|
||||||||||
| using System.Linq; | ||||||||||
| using System.Security.Claims; | ||||||||||
| using Microsoft.AspNetCore.Components.Authorization; | ||||||||||
|
|
||||||||||
| namespace Microsoft.AspNetCore.Components.WebAssembly.Server; | ||||||||||
|
|
||||||||||
| /// <summary> | ||||||||||
| /// Provides options for configuring the JSON serialization of the <see cref="AuthenticationState"/> provided by the server's <see cref="AuthenticationStateProvider"/> | ||||||||||
|
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Feel free to reject this but the sentence is too long and difficult to machine translate.
Suggested change
|
||||||||||
| /// to the WebAssembly client using <see cref="PersistentComponentState"/>. | ||||||||||
| /// </summary> | ||||||||||
| public class AuthenticationStateSerializationOptions | ||||||||||
| { | ||||||||||
| /// <summary> | ||||||||||
| /// Default constructor for <see cref="AuthenticationStateSerializationOptions"/>. | ||||||||||
| /// </summary> | ||||||||||
| public AuthenticationStateSerializationOptions() | ||||||||||
| { | ||||||||||
| SerializationCallback = SerializeAuthenticationStateAsync; | ||||||||||
| } | ||||||||||
|
|
||||||||||
| /// <summary> | ||||||||||
| /// If <see langword="true"/>, the default <see cref="SerializationCallback"/> will serialize all claims; otherwise, it will only serialize | ||||||||||
|
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
Suggested change
|
||||||||||
| /// the <see cref="ClaimsIdentity.NameClaimType"/> and <see cref="ClaimsIdentity.RoleClaimType"/> claims. | ||||||||||
| /// </summary> | ||||||||||
| public bool SerializeAllClaims { get; set; } | ||||||||||
|
|
||||||||||
| /// <summary> | ||||||||||
| /// Default implementation for converting the server's <see cref="AuthenticationState"/> to an <see cref="AuthenticationStateData"/> object | ||||||||||
| /// for JSON serialization to the client using <see cref="PersistentComponentState"/>."/> | ||||||||||
|
Comment on lines
+31
to
+32
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
Suggested change
|
||||||||||
| /// </summary> | ||||||||||
| public Func<AuthenticationState, ValueTask<AuthenticationStateData?>> SerializationCallback { get; set; } | ||||||||||
|
|
||||||||||
| private ValueTask<AuthenticationStateData?> SerializeAuthenticationStateAsync(AuthenticationState authenticationState) | ||||||||||
| { | ||||||||||
| AuthenticationStateData? data = null; | ||||||||||
|
|
||||||||||
| if (authenticationState.User.Identity?.IsAuthenticated ?? false) | ||||||||||
| { | ||||||||||
| data = new AuthenticationStateData(); | ||||||||||
|
|
||||||||||
| if (authenticationState.User.Identities.FirstOrDefault() is { } identity) | ||||||||||
| { | ||||||||||
| data.NameClaimType = identity.NameClaimType; | ||||||||||
| data.RoleClaimType = identity.RoleClaimType; | ||||||||||
| } | ||||||||||
|
|
||||||||||
| if (SerializeAllClaims) | ||||||||||
| { | ||||||||||
| foreach (var claim in authenticationState.User.Claims) | ||||||||||
| { | ||||||||||
| data.Claims.Add(new(claim.Type, claim.Value)); | ||||||||||
| } | ||||||||||
| } | ||||||||||
| else | ||||||||||
| { | ||||||||||
| if (authenticationState.User.FindFirst(data.NameClaimType) is { } nameClaim) | ||||||||||
| { | ||||||||||
| data.Claims.Add(new(nameClaim.Type, nameClaim.Value)); | ||||||||||
| } | ||||||||||
|
|
||||||||||
| foreach (var roleClaim in authenticationState.User.FindAll(data.RoleClaimType)) | ||||||||||
| { | ||||||||||
| data.Claims.Add(new(roleClaim.Type, roleClaim.Value)); | ||||||||||
| } | ||||||||||
| } | ||||||||||
| } | ||||||||||
|
|
||||||||||
| return ValueTask.FromResult(data); | ||||||||||
| } | ||||||||||
| } | ||||||||||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,52 @@ | ||
| // Licensed to the .NET Foundation under one or more agreements. | ||
| // 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 | ||
| { | ||
| // 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<AuthenticationState, ValueTask<AuthenticationStateData?>> _serializeCallback; | ||
| private readonly PersistingComponentStateSubscription _subscription; | ||
|
|
||
| private Task<AuthenticationState>? _authenticationStateTask; | ||
|
|
||
| public AuthenticationStateSerializer(PersistentComponentState persistentComponentState, IOptions<AuthenticationStateSerializationOptions> options) | ||
| { | ||
| _state = persistentComponentState; | ||
| _serializeCallback = options.Value.SerializationCallback; | ||
| _subscription = persistentComponentState.RegisterOnPersisting(OnPersistingAsync, RenderMode.InteractiveWebAssembly); | ||
| } | ||
|
|
||
| private async Task OnPersistingAsync() | ||
| { | ||
| 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); | ||
| } | ||
| } | ||
|
|
||
| /// <inheritdoc /> | ||
| public void SetAuthenticationState(Task<AuthenticationState> authenticationStateTask) | ||
| { | ||
| _authenticationStateTask = authenticationStateTask ?? throw new ArgumentNullException(nameof(authenticationStateTask)); | ||
| } | ||
|
|
||
| public void Dispose() | ||
| { | ||
| _subscription.Dispose(); | ||
| } | ||
| } |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -1,5 +1,12 @@ | ||
| #nullable enable | ||
| Microsoft.AspNetCore.Components.WebAssembly.Server.AuthenticationStateSerializationOptions | ||
| Microsoft.AspNetCore.Components.WebAssembly.Server.AuthenticationStateSerializationOptions.AuthenticationStateSerializationOptions() -> void | ||
| Microsoft.AspNetCore.Components.WebAssembly.Server.AuthenticationStateSerializationOptions.SerializationCallback.get -> System.Func<Microsoft.AspNetCore.Components.Authorization.AuthenticationState!, System.Threading.Tasks.ValueTask<Microsoft.AspNetCore.Components.Authorization.AuthenticationStateData?>>! | ||
| Microsoft.AspNetCore.Components.WebAssembly.Server.AuthenticationStateSerializationOptions.SerializationCallback.set -> void | ||
| Microsoft.AspNetCore.Components.WebAssembly.Server.AuthenticationStateSerializationOptions.SerializeAllClaims.get -> bool | ||
| Microsoft.AspNetCore.Components.WebAssembly.Server.AuthenticationStateSerializationOptions.SerializeAllClaims.set -> void | ||
| Microsoft.AspNetCore.Components.WebAssembly.Server.WebAssemblyComponentsEndpointOptions.ServeMultithreadingHeaders.get -> bool | ||
| Microsoft.AspNetCore.Components.WebAssembly.Server.WebAssemblyComponentsEndpointOptions.ServeMultithreadingHeaders.set -> void | ||
| Microsoft.AspNetCore.Components.WebAssembly.Server.WebAssemblyComponentsEndpointOptions.StaticAssetsManifestPath.get -> string? | ||
| Microsoft.AspNetCore.Components.WebAssembly.Server.WebAssemblyComponentsEndpointOptions.StaticAssetsManifestPath.set -> void | ||
| static Microsoft.Extensions.DependencyInjection.WebAssemblyRazorComponentsBuilderExtensions.AddAuthenticationStateSerialization(this Microsoft.Extensions.DependencyInjection.IRazorComponentsBuilder! builder, System.Action<Microsoft.AspNetCore.Components.WebAssembly.Server.AuthenticationStateSerializationOptions!>? configure = null) -> Microsoft.Extensions.DependencyInjection.IRazorComponentsBuilder! |
| Original file line number | Diff line number | Diff line change | ||||||||||||
|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
|
|
@@ -3,8 +3,10 @@ | |||||||||||||
|
|
||||||||||||||
| using Microsoft.AspNetCore.Builder; | ||||||||||||||
| using Microsoft.AspNetCore.Components; | ||||||||||||||
| using Microsoft.AspNetCore.Components.Authorization; | ||||||||||||||
| using Microsoft.AspNetCore.Components.Endpoints.Infrastructure; | ||||||||||||||
| using Microsoft.AspNetCore.Components.Web; | ||||||||||||||
| using Microsoft.AspNetCore.Components.WebAssembly.Server; | ||||||||||||||
| using Microsoft.AspNetCore.Http; | ||||||||||||||
| using Microsoft.AspNetCore.Routing; | ||||||||||||||
| using Microsoft.Extensions.DependencyInjection.Extensions; | ||||||||||||||
|
|
@@ -30,6 +32,25 @@ public static IRazorComponentsBuilder AddInteractiveWebAssemblyComponents(this I | |||||||||||||
| return builder; | ||||||||||||||
| } | ||||||||||||||
|
|
||||||||||||||
| /// <summary> | ||||||||||||||
| /// Serializes the <see cref="AuthenticationState"/> returned by the server-side <see cref="AuthenticationStateProvider"/> using <see cref="PersistentComponentState"/> | ||||||||||||||
| /// for use by interactive WebAssembly components via a deserializing client-side <see cref="AuthenticationStateProvider"/> which can be added by calling | ||||||||||||||
| /// AddAuthenticationStateDeserialization from the Microsoft.AspNetCore.Components.WebAssembly.Authentication package in the client project. | ||||||||||||||
|
Comment on lines
+36
to
+38
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
Suggested change
|
||||||||||||||
| /// </summary> | ||||||||||||||
| /// <param name="builder">The <see cref="IRazorComponentsBuilder"/>.</param> | ||||||||||||||
| /// <param name="configure">A callback to customize the serialization of the <see cref="AuthenticationState"/>.</param> | ||||||||||||||
| /// <returns>An <see cref="IRazorComponentsBuilder"/> that can be used to further customize the configuration.</returns> | ||||||||||||||
| public static IRazorComponentsBuilder AddAuthenticationStateSerialization(this IRazorComponentsBuilder builder, Action<AuthenticationStateSerializationOptions>? configure = null) | ||||||||||||||
| { | ||||||||||||||
| builder.Services.TryAddEnumerable(ServiceDescriptor.Scoped<IHostEnvironmentAuthenticationStateProvider, AuthenticationStateSerializer>()); | ||||||||||||||
| if (configure is not null) | ||||||||||||||
| { | ||||||||||||||
| builder.Services.Configure(configure); | ||||||||||||||
| } | ||||||||||||||
|
|
||||||||||||||
| return builder; | ||||||||||||||
| } | ||||||||||||||
|
|
||||||||||||||
| private class WebAssemblyEndpointProvider(IServiceProvider services) : RenderModeEndpointProvider | ||||||||||||||
| { | ||||||||||||||
| public override IEnumerable<RouteEndpointBuilder> GetEndpointBuilders(IComponentRenderMode renderMode, IApplicationBuilder applicationBuilder) | ||||||||||||||
|
|
||||||||||||||
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.