-
Notifications
You must be signed in to change notification settings - Fork 10.4k
Components auth: basic services and components #10227
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
eeda567
031ba22
aa07dbc
296e1a4
0143c3b
1fd4c08
8a21d3b
cfdab41
cfa81b9
0bd49e6
553f9f4
d9e5c4e
b05a7d3
3679217
99ee6f7
3d20454
5f95d3d
f78d427
12bd326
d93774b
dcc7e11
4eef170
4c78ded
3f1143f
12a2f36
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 |
---|---|---|
|
@@ -3,6 +3,34 @@ | |
|
||
namespace Microsoft.AspNetCore.Components | ||
{ | ||
public partial class AuthenticationState | ||
{ | ||
public AuthenticationState(System.Security.Claims.ClaimsPrincipal user) { } | ||
public System.Security.Claims.ClaimsPrincipal User { [System.Runtime.CompilerServices.CompilerGeneratedAttribute]get { throw null; } } | ||
} | ||
public delegate void AuthenticationStateChangedHandler(System.Threading.Tasks.Task<Microsoft.AspNetCore.Components.AuthenticationState> task); | ||
public abstract partial class AuthenticationStateProvider | ||
{ | ||
protected AuthenticationStateProvider() { } | ||
public event Microsoft.AspNetCore.Components.AuthenticationStateChangedHandler AuthenticationStateChanged { add { } remove { } } | ||
public abstract System.Threading.Tasks.Task<Microsoft.AspNetCore.Components.AuthenticationState> GetAuthenticationStateAsync(); | ||
protected void NotifyAuthenticationStateChanged(System.Threading.Tasks.Task<Microsoft.AspNetCore.Components.AuthenticationState> task) { } | ||
} | ||
public partial class AuthorizeView : Microsoft.AspNetCore.Components.ComponentBase | ||
{ | ||
public AuthorizeView() { } | ||
[Microsoft.AspNetCore.Components.ParameterAttribute] | ||
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. @SteveSandersonMS - something else I thought of late (sorry). These need to go into the manually maintained section of the ref assembly. The reason why is that ref assemblies don't preserve the setter if it's non-public. You can use cc1b294 as an example. 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. Great reminder - thanks. This should be covered in today's PR in this commit: ad9ac55 |
||
public Microsoft.AspNetCore.Components.RenderFragment<Microsoft.AspNetCore.Components.AuthenticationState> Authorized { [System.Runtime.CompilerServices.CompilerGeneratedAttribute]get { throw null; } } | ||
[Microsoft.AspNetCore.Components.ParameterAttribute] | ||
public Microsoft.AspNetCore.Components.RenderFragment Authorizing { [System.Runtime.CompilerServices.CompilerGeneratedAttribute]get { throw null; } } | ||
[Microsoft.AspNetCore.Components.ParameterAttribute] | ||
public Microsoft.AspNetCore.Components.RenderFragment<Microsoft.AspNetCore.Components.AuthenticationState> ChildContent { [System.Runtime.CompilerServices.CompilerGeneratedAttribute]get { throw null; } } | ||
[Microsoft.AspNetCore.Components.ParameterAttribute] | ||
public Microsoft.AspNetCore.Components.RenderFragment NotAuthorized { [System.Runtime.CompilerServices.CompilerGeneratedAttribute]get { throw null; } } | ||
protected override void BuildRenderTree(Microsoft.AspNetCore.Components.RenderTree.RenderTreeBuilder builder) { } | ||
[System.Diagnostics.DebuggerStepThroughAttribute] | ||
protected override System.Threading.Tasks.Task OnParametersSetAsync() { throw null; } | ||
} | ||
[Microsoft.AspNetCore.Components.BindElementAttribute("select", null, "value", "onchange")] | ||
[Microsoft.AspNetCore.Components.BindElementAttribute("textarea", null, "value", "onchange")] | ||
[Microsoft.AspNetCore.Components.BindInputElementAttribute("checkbox", null, "checked", "onchange")] | ||
|
@@ -57,6 +85,15 @@ public static partial class BindMethods | |
public static System.Action<Microsoft.AspNetCore.Components.UIEventArgs> SetValueHandler(System.Action<string> setter, string existingValue) { throw null; } | ||
public static System.Action<Microsoft.AspNetCore.Components.UIEventArgs> SetValueHandler<T>(System.Action<T> setter, T existingValue) { throw null; } | ||
} | ||
public partial class CascadingAuthenticationState : Microsoft.AspNetCore.Components.ComponentBase, System.IDisposable | ||
{ | ||
public CascadingAuthenticationState() { } | ||
[Microsoft.AspNetCore.Components.ParameterAttribute] | ||
public Microsoft.AspNetCore.Components.RenderFragment ChildContent { [System.Runtime.CompilerServices.CompilerGeneratedAttribute]get { throw null; } } | ||
protected override void BuildRenderTree(Microsoft.AspNetCore.Components.RenderTree.RenderTreeBuilder builder) { } | ||
protected override void OnInit() { } | ||
void System.IDisposable.Dispose() { } | ||
} | ||
[System.AttributeUsageAttribute(System.AttributeTargets.Property, AllowMultiple=false, Inherited=false)] | ||
public sealed partial class CascadingParameterAttribute : System.Attribute | ||
{ | ||
|
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,28 @@ | ||
// Copyright (c) .NET Foundation. All rights reserved. | ||
// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. | ||
|
||
using System; | ||
using System.Security.Claims; | ||
|
||
namespace Microsoft.AspNetCore.Components | ||
{ | ||
/// <summary> | ||
/// Provides information about the currently authenticated user, if any. | ||
/// </summary> | ||
public class AuthenticationState | ||
{ | ||
/// <summary> | ||
/// Gets a <see cref="ClaimsPrincipal"/> that describes the current user. | ||
/// </summary> | ||
public ClaimsPrincipal User { get; } | ||
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. nit: fields -> constructors -> properties 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. OK. Rather than go through another multi-hour CI process to make this tweak, I'm going to include it in my next auth PR. |
||
|
||
/// <summary> | ||
/// Constructs an instance of <see cref="AuthenticationState"/>. | ||
/// </summary> | ||
/// <param name="user">A <see cref="ClaimsPrincipal"/> representing the user.</param> | ||
public AuthenticationState(ClaimsPrincipal user) | ||
{ | ||
User = user ?? throw new ArgumentNullException(nameof(user)); | ||
} | ||
} | ||
} |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,49 @@ | ||
// Copyright (c) .NET Foundation. All rights reserved. | ||
// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. | ||
|
||
using System; | ||
using System.Threading.Tasks; | ||
|
||
namespace Microsoft.AspNetCore.Components | ||
{ | ||
/// <summary> | ||
/// Provides information about the authentication state of the current user. | ||
/// </summary> | ||
public abstract class AuthenticationStateProvider | ||
{ | ||
/// <summary> | ||
/// Gets an <see cref="AuthenticationState"/> instance that describes | ||
/// the current user. | ||
/// </summary> | ||
/// <returns>An <see cref="AuthenticationState"/> instance that describes the current user.</returns> | ||
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. technically not correct, it returns "a task that blah blah blah" 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. OK. Rather than go through another multi-hour CI process to make this tweak, I'm going to include it in my next auth PR. |
||
public abstract Task<AuthenticationState> GetAuthenticationStateAsync(); | ||
|
||
/// <summary> | ||
/// An event that provides notification when the <see cref="AuthenticationState"/> | ||
/// has changed. For example, this event may be raised if a user logs in or out. | ||
/// </summary> | ||
#pragma warning disable 0067 // "Never used" (it's only raised by subclasses) | ||
public event AuthenticationStateChangedHandler AuthenticationStateChanged; | ||
#pragma warning restore 0067 | ||
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 actually needed now that 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. Well spotted. Rather than go through another multi-hour CI process to make this tweak, I'm going to include it in my next auth PR. |
||
|
||
/// <summary> | ||
/// Raises the <see cref="AuthenticationStateChanged"/> event. | ||
/// </summary> | ||
/// <param name="task">A <see cref="Task"/> that supplies the updated <see cref="AuthenticationState"/>.</param> | ||
protected void NotifyAuthenticationStateChanged(Task<AuthenticationState> task) | ||
{ | ||
if (task == null) | ||
{ | ||
throw new ArgumentNullException(nameof(task)); | ||
} | ||
|
||
AuthenticationStateChanged?.Invoke(task); | ||
} | ||
} | ||
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. Food for thought. It appears that we want to (and support) raising this event on any thread. Do we want to document that as supported? Or do we want to try and make it enforced that you raise this on the sync context. 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 don’t think we want that. The callback should be responsible for calling Invoke or InvokeAsync when handling the event. I’m wondering how good our support story is when a component calls Invoke 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. It's an interesting question. We could make the However, the So, since in common usage patterns this won't be an issue anyway, I'd prefer to keep the |
||
|
||
/// <summary> | ||
/// A handler for the <see cref="AuthenticationStateProvider.AuthenticationStateChanged"/> event. | ||
/// </summary> | ||
/// <param name="task">A <see cref="Task"/> that supplies the updated <see cref="AuthenticationState"/>.</param> | ||
public delegate void AuthenticationStateChangedHandler(Task<AuthenticationState> task); | ||
} |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,68 @@ | ||
@namespace Microsoft.AspNetCore.Components | ||
|
||
@if (currentAuthenticationState == null) | ||
{ | ||
@Authorizing | ||
} | ||
else if (IsAuthorized()) | ||
{ | ||
@((Authorized ?? ChildContent)?.Invoke(currentAuthenticationState)) | ||
} | ||
else | ||
{ | ||
@NotAuthorized | ||
} | ||
SteveSandersonMS marked this conversation as resolved.
Show resolved
Hide resolved
|
||
|
||
@functions { | ||
private AuthenticationState currentAuthenticationState; | ||
|
||
[CascadingParameter] private Task<AuthenticationState> AuthenticationState { get; set; } | ||
|
||
/// <summary> | ||
/// The content that will be displayed if the user is authorized. | ||
/// </summary> | ||
[Parameter] public RenderFragment<AuthenticationState> ChildContent { get; private set; } | ||
|
||
/// <summary> | ||
/// The content that will be displayed if the user is not authorized. | ||
/// </summary> | ||
[Parameter] public RenderFragment NotAuthorized { get; private set; } | ||
|
||
/// <summary> | ||
/// The content that will be displayed if the user is authorized. | ||
/// If you specify a value for this parameter, do not also specify a value for <see cref="ChildContent"/>. | ||
/// </summary> | ||
[Parameter] public RenderFragment<AuthenticationState> Authorized { get; private set; } | ||
|
||
/// <summary> | ||
/// The content that will be displayed while asynchronous authorization is in progress. | ||
/// </summary> | ||
[Parameter] public RenderFragment Authorizing { get; private set; } | ||
|
||
protected override async Task OnParametersSetAsync() | ||
SteveSandersonMS marked this conversation as resolved.
Show resolved
Hide resolved
|
||
{ | ||
// We allow 'ChildContent' for convenience in basic cases, and 'Authorized' for symmetry | ||
// with 'NotAuthorized' in other cases. Besides naming, they are equivalent. To avoid | ||
// confusion, explicitly prevent the case where both are supplied. | ||
if (ChildContent != null && Authorized != null) | ||
SteveSandersonMS marked this conversation as resolved.
Show resolved
Hide resolved
|
||
{ | ||
throw new InvalidOperationException($"When using {nameof(AuthorizeView)}, do not specify both '{nameof(Authorized)}' and '{nameof(ChildContent)}'."); | ||
} | ||
|
||
// First render in pending state | ||
// If the task has already completed, this render will be skipped | ||
currentAuthenticationState = null; | ||
|
||
// Then render in completed state | ||
currentAuthenticationState = await AuthenticationState; | ||
} | ||
|
||
private bool IsAuthorized() | ||
{ | ||
// TODO: Support various authorization condition parameters, equivalent to those offered | ||
// by the [Authorize] attribute, e.g., "Roles" and "Policy". This is on hold until we're | ||
// able to reference the policy evaluator APIs from this package. | ||
|
||
return currentAuthenticationState.User?.Identity?.IsAuthenticated == true; | ||
} | ||
} |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,36 @@ | ||
@namespace Microsoft.AspNetCore.Components | ||
@implements IDisposable | ||
@inject AuthenticationStateProvider AuthenticationStateProvider | ||
|
||
<CascadingValue T="Task<AuthenticationState>" Value="@_currentAuthenticationStateTask" ChildContent="@ChildContent" /> | ||
|
||
@functions { | ||
private Task<AuthenticationState> _currentAuthenticationStateTask; | ||
|
||
/// <summary> | ||
/// The content to which the authentication state should be provided. | ||
/// </summary> | ||
[Parameter] public RenderFragment ChildContent { get; private set; } | ||
|
||
protected override void OnInit() | ||
{ | ||
AuthenticationStateProvider.AuthenticationStateChanged += OnAuthenticationStateChanged; | ||
|
||
_currentAuthenticationStateTask = AuthenticationStateProvider | ||
.GetAuthenticationStateAsync(); | ||
} | ||
|
||
private void OnAuthenticationStateChanged(Task<AuthenticationState> newAuthStateTask) | ||
{ | ||
Invoke(() => | ||
{ | ||
_currentAuthenticationStateTask = newAuthStateTask; | ||
StateHasChanged(); | ||
}); | ||
} | ||
|
||
void IDisposable.Dispose() | ||
{ | ||
AuthenticationStateProvider.AuthenticationStateChanged -= OnAuthenticationStateChanged; | ||
} | ||
} |
Uh oh!
There was an error while loading. Please reload this page.