Skip to content

Commit 44e8df2

Browse files
committed
Product Completion
1 parent 311e652 commit 44e8df2

53 files changed

Lines changed: 1007 additions & 25 deletions

File tree

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.
Lines changed: 29 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,37 @@
11
namespace CodeBeam.UltimateAuth.Client.Blazor;
22

3+
/// <summary>
4+
/// Declares UltimateAuth authorization requirements for a Blazor component or page.
5+
/// </summary>
6+
/// <remarks>
7+
/// <para>
8+
/// The attribute can be used to associate role and permission requirements with routable or authorization-aware Blazor components.
9+
/// </para>
10+
/// <para>
11+
/// Role and permission values are expressed as comma-separated lists. The effective
12+
/// authorization behavior is determined by the UltimateAuth authorization pipeline that consumes this metadata.
13+
/// </para>
14+
/// <para>
15+
/// This attribute describes authorization requirements only. It does not perform authorization by itself.
16+
/// </para>
17+
/// </remarks>
318
[AttributeUsage(AttributeTargets.Class)]
419
public sealed class UAuthAuthorizeAttribute : Attribute
520
{
21+
/// <summary>
22+
/// Gets or sets the comma-separated roles associated with the authorization requirement.
23+
/// </summary>
24+
/// <remarks>
25+
/// A <see langword="null"/>, empty, or whitespace value indicates that no explicit role requirement is declared by this property.
26+
/// </remarks>
627
public string? Roles { get; set; }
28+
29+
/// <summary>
30+
/// Gets or sets the comma-separated UltimateAuth permissions associated with the authorization requirement.
31+
/// </summary>
32+
/// <remarks>
33+
/// A <see langword="null"/>, empty, or whitespace value indicates that no explicit
34+
/// permission requirement is declared by this property.
35+
/// </remarks>
736
public string? Permissions { get; set; }
837
}

‎src/client/CodeBeam.UltimateAuth.Client.Blazor/CodeBeam.UltimateAuth.Client.Blazor.csproj‎

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,6 @@
22

33
<PropertyGroup>
44
<TargetFrameworks>net8.0;net9.0;net10.0</TargetFrameworks>
5-
<NoWarn>$(NoWarn);1591</NoWarn>
65

76
<PackageId>CodeBeam.UltimateAuth.Client.Blazor</PackageId>
87

‎src/client/CodeBeam.UltimateAuth.Client.Blazor/Components/Base/UAuthComponentBase.cs‎

Lines changed: 16 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -19,13 +19,18 @@ public abstract class UAuthComponentBase : ComponentBase, IDisposable
1919
/// <summary>
2020
/// Gets the Blazor navigation service.
2121
/// </summary>
22-
[Inject] protected NavigationManager Nav { get; set; } = default!;
22+
[Inject] protected NavigationManager Navigation { get; set; } = default!;
2323

2424
/// <summary>
2525
/// Automatically re-render when UAuthState changes. Can be overridden to disable.
2626
/// </summary>
2727
protected virtual bool AutoRefreshOnAuthStateChanged => true;
2828

29+
/// <summary>
30+
/// Called when the component's parameters have been set. This method ensures that the component is properly registered
31+
/// with the current <see cref="UAuthState"/> and evaluates authorization requirements.
32+
/// </summary>
33+
/// <exception cref="InvalidOperationException"></exception>
2934
protected override void OnParametersSet()
3035
{
3136
base.OnParametersSet();
@@ -46,6 +51,11 @@ protected override void OnParametersSet()
4651
EvaluateAuthorization();
4752
}
4853

54+
/// <summary>
55+
/// Called after the component has been rendered. This method sets the _rendered flag to true on the first render.
56+
/// </summary>
57+
/// <param name="firstRender"></param>
58+
/// <returns></returns>
4959
protected override async Task OnAfterRenderAsync(bool firstRender)
5060
{
5161
await base.OnAfterRenderAsync(firstRender);
@@ -117,17 +127,20 @@ private void EvaluateAuthorization()
117127
/// </summary>
118128
protected virtual void OnUnauthorized()
119129
{
120-
Nav.NavigateTo("/");
130+
Navigation.NavigateTo("/");
121131
}
122132

123133
/// <summary>
124134
/// Called when the current user is authenticated but does not satisfy the authorization requirements of the component.
125135
/// </summary>
126136
protected virtual void OnForbidden()
127137
{
128-
Nav.NavigateTo("/forbidden");
138+
Navigation.NavigateTo("/forbidden");
129139
}
130140

141+
/// <summary>
142+
/// Disposes of the component and unsubscribes from the <see cref="UAuthState.Changed"/> event to prevent memory leaks.
143+
/// </summary>
131144
public virtual void Dispose()
132145
{
133146
if (_previousState is not null)

‎src/client/CodeBeam.UltimateAuth.Client.Blazor/Components/Base/UAuthHubLayoutBase.cs‎

Lines changed: 58 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5,20 +5,67 @@
55

66
namespace CodeBeam.UltimateAuth.Client.Blazor;
77

8+
/// <summary>
9+
/// Base class for Blazor layouts that participate in an UltimateAuth Hub flow.
10+
/// </summary>
11+
/// <remarks>
12+
/// <para>
13+
/// The layout resolves the Hub session identifier from the current navigation URI
14+
/// and exposes the corresponding <see cref="HubFlowState"/> to derived layouts.
15+
/// </para>
16+
/// <para>
17+
/// Hub state is obtained through <see cref="IHubFlowReader"/>. An absent or invalid
18+
/// Hub session identifier results in no current Hub state.
19+
/// </para>
20+
/// <para>
21+
/// This type provides Hub flow state to the UI and does not itself authorize,
22+
/// complete, or otherwise make security decisions for the authentication flow.
23+
/// </para>
24+
/// </remarks>
825
public abstract class UAuthHubLayoutBase : LayoutComponentBase
926
{
27+
/// <summary>
28+
/// Gets the navigation service used to inspect the current URI.
29+
/// </summary>
1030
[Inject] protected NavigationManager Navigation { get; set; } = default!;
31+
32+
/// <summary>
33+
/// Gets the Hub flow reader used to retrieve the state of the current Hub session.
34+
/// </summary>
1135
[Inject] protected IHubFlowReader HubFlowReader { get; set; } = default!;
1236

37+
/// <summary>
38+
/// Gets the state associated with the current Hub session, when one can be resolved.
39+
/// </summary>
40+
/// <remarks>
41+
/// The value is <see langword="null"/> when the current URI does not contain a Hub
42+
/// session identifier, the identifier is invalid, or no state has been loaded.
43+
/// </remarks>
1344
protected HubFlowState? HubState { get; private set; }
1445

46+
/// <summary>
47+
/// Gets a value indicating whether the resolved Hub flow exists.
48+
/// </summary>
1549
protected bool HasHub => HubState?.Exists == true;
50+
51+
/// <summary>
52+
/// Gets a value indicating whether the resolved Hub flow exists and is active.
53+
/// </summary>
1654
protected bool IsHubActive => HasHub && HubState?.IsActive == true;
55+
56+
/// <summary>
57+
/// Gets a value indicating whether the resolved Hub flow has expired.
58+
/// </summary>
1759
protected bool IsExpired => HubState?.IsExpired == true;
60+
61+
/// <summary>
62+
/// Gets the error associated with the resolved Hub flow, if any.
63+
/// </summary>
1864
protected HubErrorCode? Error => HubState?.Error;
1965

2066
private string? _lastHubKey;
2167

68+
/// <inheritdoc />
2269
protected override async Task OnParametersSetAsync()
2370
{
2471
await base.OnParametersSetAsync();
@@ -46,6 +93,17 @@ protected override async Task OnParametersSetAsync()
4693
}
4794
}
4895

96+
/// <summary>
97+
/// Resolves the Hub session identifier associated with the current navigation URI.
98+
/// </summary>
99+
/// <returns>
100+
/// The raw Hub session identifier when present; otherwise, <see langword="null"/>.
101+
/// </returns>
102+
/// <remarks>
103+
/// The default implementation reads <see cref="UAuthConstants.Query.Hub"/> from
104+
/// the current query string. Derived layouts may override this method to provide
105+
/// the Hub session identifier from another source.
106+
/// </remarks>
49107
protected virtual string? ResolveHubKey()
50108
{
51109
var uri = Navigation.ToAbsoluteUri(Navigation.Uri);

‎src/client/CodeBeam.UltimateAuth.Client.Blazor/Components/Base/UAuthHubPageBase.cs‎

Lines changed: 53 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5,24 +5,77 @@
55

66
namespace CodeBeam.UltimateAuth.Client.Blazor;
77

8+
/// <summary>
9+
/// Base class for Blazor pages that participate in an UltimateAuth Hub flow.
10+
/// </summary>
11+
/// <remarks>
12+
/// <para>
13+
/// The page receives the Hub session identifier from the current query string
14+
/// and exposes the corresponding <see cref="HubFlowState"/> to derived pages.
15+
/// </para>
16+
/// <para>
17+
/// Hub state is obtained through <see cref="IHubFlowReader"/> and is automatically
18+
/// loaded when component parameters are processed. Derived pages can explicitly
19+
/// refresh the state by calling <see cref="ReloadStateAsync"/>.
20+
/// </para>
21+
/// <para>
22+
/// This type provides Hub flow state to the UI and does not itself authorize,
23+
/// complete, or otherwise make security decisions for the authentication flow.
24+
/// </para>
25+
/// </remarks>
826
public abstract class UAuthHubPageBase : UAuthComponentBase
927
{
28+
/// <summary>
29+
/// Gets the Hub flow reader used to retrieve the state of the current Hub session.
30+
/// </summary>
1031
[Inject] protected IHubFlowReader HubFlowReader { get; set; } = default!;
1132

33+
/// <summary>
34+
/// Gets or sets the raw Hub session identifier supplied by the current query string.
35+
/// </summary>
36+
/// <remarks>
37+
/// The value is supplied from <see cref="UAuthConstants.Query.Hub"/>.
38+
/// It is validated as a <see cref="HubSessionId"/> before Hub state is read.
39+
/// </remarks>
1240
[Parameter]
1341
[SupplyParameterFromQuery(Name = UAuthConstants.Query.Hub)]
1442
public string? HubKey { get; set; }
1543

44+
/// <summary>
45+
/// Gets the state associated with the current Hub session, when one can be resolved.
46+
/// </summary>
47+
/// <remarks>
48+
/// The value is <see langword="null"/> when no Hub session identifier is supplied
49+
/// or when the supplied identifier is invalid.
50+
/// </remarks>
1651
protected HubFlowState? HubState { get; private set; }
1752

53+
/// <summary>
54+
/// Gets a value indicating whether the resolved Hub flow exists and is active.
55+
/// </summary>
1856
protected bool IsHubActive => HubState is { Exists: true, IsActive: true };
1957

58+
/// <inheritdoc />
2059
protected override async Task OnParametersSetAsync()
2160
{
2261
await base.OnParametersSetAsync();
2362
await ReloadStateAsync();
2463
}
2564

65+
/// <summary>
66+
/// Reloads the Hub flow state associated with the current <see cref="HubKey"/>.
67+
/// </summary>
68+
/// <remarks>
69+
/// <para>
70+
/// When <see cref="HubKey"/> is missing, empty, or invalid, <see cref="HubState"/> is cleared.
71+
/// </para>
72+
/// <para>
73+
/// A valid Hub session identifier is resolved through <see cref="IHubFlowReader"/>.
74+
/// </para>
75+
/// </remarks>
76+
/// <returns>
77+
/// A task that represents the asynchronous reload operation.
78+
/// </returns>
2679
public async Task ReloadStateAsync()
2780
{
2881
if (string.IsNullOrWhiteSpace(HubKey))

‎src/client/CodeBeam.UltimateAuth.Client.Blazor/Components/Base/UAuthPageBase.cs‎

Lines changed: 19 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -45,11 +45,12 @@ public abstract class UAuthPageBase : UAuthComponentBase
4545
private string? _lastParsedUri;
4646
private bool _payloadConsumed;
4747

48+
/// <inheritdoc />
4849
protected override void OnParametersSet()
4950
{
5051
base.OnParametersSet();
5152

52-
var currentUri = Nav.Uri;
53+
var currentUri = Navigation.Uri;
5354

5455
if (string.Equals(_lastParsedUri, currentUri, StringComparison.Ordinal))
5556
return;
@@ -58,7 +59,7 @@ protected override void OnParametersSet()
5859

5960
_payloadConsumed = false;
6061

61-
var uri = Nav.ToAbsoluteUri(currentUri);
62+
var uri = Navigation.ToAbsoluteUri(currentUri);
6263
var query = QueryHelpers.ParseQuery(uri.Query);
6364

6465
ShouldFocus = query.TryGetValue(UAuthConstants.Query.Focus, out var focus) && focus == "1";
@@ -84,6 +85,7 @@ protected override void OnParametersSet()
8485
_needsClear = ClearUAuthQueryAfterParse && HasUAuthPageQuery(query);
8586
}
8687

88+
/// <inheritdoc />
8789
protected override async Task OnAfterRenderAsync(bool firstRender)
8890
{
8991
await base.OnAfterRenderAsync(firstRender);
@@ -99,8 +101,8 @@ protected override async Task OnAfterRenderAsync(bool firstRender)
99101
_needsClear = false;
100102
var cleanUri = BuildUriWithoutConsumedUAuthQuery();
101103

102-
if (!string.Equals(cleanUri, Nav.Uri, StringComparison.Ordinal))
103-
Nav.NavigateTo(cleanUri, replace: true);
104+
if (!string.Equals(cleanUri, Navigation.Uri, StringComparison.Ordinal))
105+
Navigation.NavigateTo(cleanUri, replace: true);
104106
}
105107
}
106108

@@ -135,12 +137,24 @@ protected bool TryConsumePayload(out AuthFlowPayload? payload)
135137
return true;
136138
}
137139

140+
/// <summary>
141+
/// Called when a new UltimateAuth flow payload is available for the current page.
142+
/// This method is called only once per parsed URL, and only when a valid payload is present.
143+
/// </summary>
144+
/// <param name="payload"></param>
145+
/// <returns></returns>
138146
protected virtual Task OnUAuthPayloadAsync(AuthFlowPayload payload) => Task.CompletedTask;
147+
148+
/// <summary>
149+
/// Called when a focus request is present for the current page.
150+
/// This method is called only once per parsed URL, and only when a focus request is present.
151+
/// </summary>
152+
/// <returns></returns>
139153
protected virtual Task OnFocusRequestedAsync() => Task.CompletedTask;
140154

141155
private string BuildUriWithoutConsumedUAuthQuery()
142156
{
143-
var uri = Nav.ToAbsoluteUri(Nav.Uri);
157+
var uri = Navigation.ToAbsoluteUri(Navigation.Uri);
144158
var query = QueryHelpers.ParseQuery(uri.Query);
145159

146160
var remaining = query

‎src/client/CodeBeam.UltimateAuth.Client.Blazor/Components/UAuthApp.razor.cs‎

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -111,11 +111,13 @@ public partial class UAuthApp
111111
[Parameter]
112112
public EventCallback OnReauthRequired { get; set; }
113113

114+
/// <inheritdoc />
114115
protected override async Task OnInitializedAsync()
115116
{
116117
Coordinator.ReauthRequired += HandleReauthRequired;
117118
}
118119

120+
/// <inheritdoc />
119121
protected override async Task OnAfterRenderAsync(bool firstRender)
120122
{
121123
if (firstRender)
@@ -190,6 +192,10 @@ private IEnumerable<Assembly> GetAdditionalAssemblies()
190192
return Enumerable.Empty<Assembly>();
191193
}
192194

195+
/// <summary>
196+
/// Disposes the component and stops the session coordinator if it was started.
197+
/// </summary>
198+
/// <returns></returns>
193199
public async ValueTask DisposeAsync()
194200
{
195201
StateManager.State.Changed -= OnStateChanged;

0 commit comments

Comments
 (0)