diff --git a/samples/UAuthHub/CodeBeam.UltimateAuth.Sample.UAuthHub.EFCore/uauthhub.db-shm b/samples/UAuthHub/CodeBeam.UltimateAuth.Sample.UAuthHub.EFCore/uauthhub.db-shm index 3b286565..3490745a 100644 Binary files a/samples/UAuthHub/CodeBeam.UltimateAuth.Sample.UAuthHub.EFCore/uauthhub.db-shm and b/samples/UAuthHub/CodeBeam.UltimateAuth.Sample.UAuthHub.EFCore/uauthhub.db-shm differ diff --git a/samples/UAuthHub/CodeBeam.UltimateAuth.Sample.UAuthHub.EFCore/uauthhub.db-wal b/samples/UAuthHub/CodeBeam.UltimateAuth.Sample.UAuthHub.EFCore/uauthhub.db-wal index db39603e..d3a275a6 100644 Binary files a/samples/UAuthHub/CodeBeam.UltimateAuth.Sample.UAuthHub.EFCore/uauthhub.db-wal and b/samples/UAuthHub/CodeBeam.UltimateAuth.Sample.UAuthHub.EFCore/uauthhub.db-wal differ diff --git a/samples/blazor-server/CodeBeam.UltimateAuth.Sample.BlazorServer.EFCore/Components/Pages/AuthorizedTestPage.razor b/samples/blazor-server/CodeBeam.UltimateAuth.Sample.BlazorServer.EFCore/Components/Pages/AuthorizedTestPage.razor index 5dc5d8aa..7218a9c1 100644 --- a/samples/blazor-server/CodeBeam.UltimateAuth.Sample.BlazorServer.EFCore/Components/Pages/AuthorizedTestPage.razor +++ b/samples/blazor-server/CodeBeam.UltimateAuth.Sample.BlazorServer.EFCore/Components/Pages/AuthorizedTestPage.razor @@ -1,5 +1,6 @@ @page "/authorized-test" -@attribute [Authorize] +@attribute [UAuthAuthorize] +@inherits UAuthFlowPageBase diff --git a/samples/blazor-server/CodeBeam.UltimateAuth.Sample.BlazorServer.EFCore/Components/Pages/Home.razor b/samples/blazor-server/CodeBeam.UltimateAuth.Sample.BlazorServer.EFCore/Components/Pages/Home.razor index 76de9054..74cb1b79 100644 --- a/samples/blazor-server/CodeBeam.UltimateAuth.Sample.BlazorServer.EFCore/Components/Pages/Home.razor +++ b/samples/blazor-server/CodeBeam.UltimateAuth.Sample.BlazorServer.EFCore/Components/Pages/Home.razor @@ -1,5 +1,5 @@ @page "/home" -@attribute [Authorize] +@attribute [UAuthAuthorize] @inherits UAuthFlowPageBase @inject IUAuthClient UAuthClient diff --git a/samples/blazor-server/CodeBeam.UltimateAuth.Sample.BlazorServer/Components/Pages/AuthorizedTestPage.razor b/samples/blazor-server/CodeBeam.UltimateAuth.Sample.BlazorServer/Components/Pages/AuthorizedTestPage.razor index 5dc5d8aa..d0a06c06 100644 --- a/samples/blazor-server/CodeBeam.UltimateAuth.Sample.BlazorServer/Components/Pages/AuthorizedTestPage.razor +++ b/samples/blazor-server/CodeBeam.UltimateAuth.Sample.BlazorServer/Components/Pages/AuthorizedTestPage.razor @@ -1,5 +1,6 @@ @page "/authorized-test" -@attribute [Authorize] +@attribute [UAuthAuthorize] +@inherits UAuthFlowPageBase @@ -13,7 +14,7 @@ - Go Profile + Go Home Page diff --git a/samples/blazor-server/CodeBeam.UltimateAuth.Sample.BlazorServer/Components/Pages/Home.razor b/samples/blazor-server/CodeBeam.UltimateAuth.Sample.BlazorServer/Components/Pages/Home.razor index b71e9282..02cb0f28 100644 --- a/samples/blazor-server/CodeBeam.UltimateAuth.Sample.BlazorServer/Components/Pages/Home.razor +++ b/samples/blazor-server/CodeBeam.UltimateAuth.Sample.BlazorServer/Components/Pages/Home.razor @@ -1,5 +1,5 @@ @page "/home" -@attribute [Authorize] +@attribute [UAuthAuthorize] @inherits UAuthFlowPageBase @inject IUAuthClient UAuthClient diff --git a/samples/blazor-standalone-wasm/CodeBeam.UltimateAuth.Sample.BlazorStandaloneWasm/Pages/AuthorizedTestPage.razor b/samples/blazor-standalone-wasm/CodeBeam.UltimateAuth.Sample.BlazorStandaloneWasm/Pages/AuthorizedTestPage.razor index e5554c4e..2dd294b2 100644 --- a/samples/blazor-standalone-wasm/CodeBeam.UltimateAuth.Sample.BlazorStandaloneWasm/Pages/AuthorizedTestPage.razor +++ b/samples/blazor-standalone-wasm/CodeBeam.UltimateAuth.Sample.BlazorStandaloneWasm/Pages/AuthorizedTestPage.razor @@ -1,5 +1,6 @@ @page "/authorized-test" -@attribute [Authorize] +@attribute [UAuthAuthorize] +@inherits UAuthFlowPageBase diff --git a/samples/blazor-standalone-wasm/CodeBeam.UltimateAuth.Sample.BlazorStandaloneWasm/Pages/Home.razor b/samples/blazor-standalone-wasm/CodeBeam.UltimateAuth.Sample.BlazorStandaloneWasm/Pages/Home.razor index beac4f94..4db6dfcf 100644 --- a/samples/blazor-standalone-wasm/CodeBeam.UltimateAuth.Sample.BlazorStandaloneWasm/Pages/Home.razor +++ b/samples/blazor-standalone-wasm/CodeBeam.UltimateAuth.Sample.BlazorStandaloneWasm/Pages/Home.razor @@ -1,5 +1,5 @@ @page "/home" -@attribute [Authorize] +@attribute [UAuthAuthorize] @inherits UAuthFlowPageBase @inject IUAuthClient UAuthClient diff --git a/samples/int-wasm/CodeBeam.UAuth.Sample.IntWasm/CodeBeam.UAuth.Sample.IntWasm.Client/Pages/AuthorizedTestPage.razor b/samples/int-wasm/CodeBeam.UAuth.Sample.IntWasm/CodeBeam.UAuth.Sample.IntWasm.Client/Pages/AuthorizedTestPage.razor index e5554c4e..9d78b597 100644 --- a/samples/int-wasm/CodeBeam.UAuth.Sample.IntWasm/CodeBeam.UAuth.Sample.IntWasm.Client/Pages/AuthorizedTestPage.razor +++ b/samples/int-wasm/CodeBeam.UAuth.Sample.IntWasm/CodeBeam.UAuth.Sample.IntWasm.Client/Pages/AuthorizedTestPage.razor @@ -1,5 +1,6 @@ @page "/authorized-test" -@attribute [Authorize] +@attribute [UAuthAuthorize] +@inherits UAuthFlowPageBase @@ -13,7 +14,7 @@ - Go Profile + Go Home Page diff --git a/samples/int-wasm/CodeBeam.UAuth.Sample.IntWasm/CodeBeam.UAuth.Sample.IntWasm.Client/Pages/Home.razor b/samples/int-wasm/CodeBeam.UAuth.Sample.IntWasm/CodeBeam.UAuth.Sample.IntWasm.Client/Pages/Home.razor index d1a9096c..b2aba719 100644 --- a/samples/int-wasm/CodeBeam.UAuth.Sample.IntWasm/CodeBeam.UAuth.Sample.IntWasm.Client/Pages/Home.razor +++ b/samples/int-wasm/CodeBeam.UAuth.Sample.IntWasm/CodeBeam.UAuth.Sample.IntWasm.Client/Pages/Home.razor @@ -1,6 +1,5 @@ @page "/home" -@* To make Authorize attribute to work, add ResourceApi in Program.cs, but it affects performance significantly *@ -@* @attribute [Authorize] *@ +@attribute [UAuthAuthorize] @inherits UAuthFlowPageBase @inject IUAuthClient UAuthClient diff --git a/src/client/CodeBeam.UltimateAuth.Client.Blazor/Attributes/UAuthAuthorizeAttribute.cs b/src/client/CodeBeam.UltimateAuth.Client.Blazor/Attributes/UAuthAuthorizeAttribute.cs new file mode 100644 index 00000000..971364ac --- /dev/null +++ b/src/client/CodeBeam.UltimateAuth.Client.Blazor/Attributes/UAuthAuthorizeAttribute.cs @@ -0,0 +1,8 @@ +namespace CodeBeam.UltimateAuth.Client.Blazor; + +[AttributeUsage(AttributeTargets.Class)] +public sealed class UAuthAuthorizeAttribute : Attribute +{ + public string? Roles { get; set; } + public string? Permissions { get; set; } +} diff --git a/src/client/CodeBeam.UltimateAuth.Client.Blazor/Components/Base/UAuthFlowPageBase.cs b/src/client/CodeBeam.UltimateAuth.Client.Blazor/Components/Base/UAuthFlowPageBase.cs index 8f805211..c1305221 100644 --- a/src/client/CodeBeam.UltimateAuth.Client.Blazor/Components/Base/UAuthFlowPageBase.cs +++ b/src/client/CodeBeam.UltimateAuth.Client.Blazor/Components/Base/UAuthFlowPageBase.cs @@ -1,5 +1,4 @@ using CodeBeam.UltimateAuth.Core.Contracts; -using Microsoft.AspNetCore.Components; using Microsoft.AspNetCore.WebUtilities; using System.Text; using System.Text.Json; @@ -8,8 +7,6 @@ namespace CodeBeam.UltimateAuth.Client.Blazor; public abstract class UAuthFlowPageBase : UAuthReactiveComponentBase { - [Inject] protected NavigationManager Nav { get; set; } = default!; - protected AuthFlowPayload? UAuthPayload { get; private set; } protected string? ReturnUrl { get; private set; } protected bool ShouldFocus { get; private set; } diff --git a/src/client/CodeBeam.UltimateAuth.Client.Blazor/Components/Base/UAuthReactiveComponentBase.cs b/src/client/CodeBeam.UltimateAuth.Client.Blazor/Components/Base/UAuthReactiveComponentBase.cs index fb67afe8..38d8ade6 100644 --- a/src/client/CodeBeam.UltimateAuth.Client.Blazor/Components/Base/UAuthReactiveComponentBase.cs +++ b/src/client/CodeBeam.UltimateAuth.Client.Blazor/Components/Base/UAuthReactiveComponentBase.cs @@ -5,10 +5,13 @@ namespace CodeBeam.UltimateAuth.Client.Blazor; public abstract class UAuthReactiveComponentBase : ComponentBase, IDisposable { private UAuthState? _previousState; + private bool _rendered; [CascadingParameter] protected UAuthState AuthState { get; set; } = default!; + [Inject] protected NavigationManager Nav { get; set; } = default!; + /// /// Automatically re-render when UAuthState changes. /// Can be overridden to disable. @@ -31,12 +34,24 @@ protected override void OnParametersSet() AuthState.Changed += OnAuthStateChanged; _previousState = AuthState; } + + EvaluateAuthorization(); + } + + protected override async Task OnAfterRenderAsync(bool firstRender) + { + await base.OnAfterRenderAsync(firstRender); + + if (firstRender) + _rendered = true; } private void OnAuthStateChanged(UAuthStateChangeReason reason) { HandleAuthStateChanged(reason); + EvaluateAuthorization(); + if (AutoRefreshOnAuthStateChanged) _ = InvokeAsync(StateHasChanged); } @@ -48,6 +63,54 @@ protected virtual void HandleAuthStateChanged(UAuthStateChangeReason reason) { } + private void EvaluateAuthorization() + { + var attr = GetType() + .GetCustomAttributes(typeof(UAuthAuthorizeAttribute), true) + .FirstOrDefault() as UAuthAuthorizeAttribute; + + if (attr is null) + return; + + if (_rendered && !AuthState.IsAuthenticated) + { + OnUnauthorized(); + return; + } + + if (_rendered && !string.IsNullOrEmpty(attr.Roles)) + { + var roles = attr.Roles.Split(','); + + if (!roles.Any(r => AuthState.IsInRole(r.Trim()))) + { + OnForbidden(); + return; + } + } + + if (_rendered && !string.IsNullOrEmpty(attr.Permissions)) + { + var permissions = attr.Permissions.Split(','); + + if (!permissions.Any(p => AuthState.HasPermission(p.Trim()))) + { + OnForbidden(); + return; + } + } + } + + protected virtual void OnUnauthorized() + { + Nav.NavigateTo("/"); + } + + protected virtual void OnForbidden() + { + Nav.NavigateTo("/forbidden"); + } + public virtual void Dispose() { if (_previousState is not null)