Skip to content

Commit 17e6d10

Browse files
committed
Complete Checking Server Options
1 parent c3b976f commit 17e6d10

File tree

38 files changed

+494
-170
lines changed

38 files changed

+494
-170
lines changed

samples/blazor-standalone-wasm/CodeBeam.UltimateAuth.Sample.BlazorStandaloneWasm/Program.cs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -15,7 +15,7 @@
1515
builder.Services.AddUltimateAuth();
1616
builder.Services.AddUltimateAuthClient(o =>
1717
{
18-
o.Endpoints.Authority = "https://localhost:6110";
18+
o.Endpoints.BasePath = "https://localhost:6110";
1919
});
2020

2121
//builder.Services.AddScoped<AuthenticationStateProvider, UAuthAuthenticationStateProvider>();

src/CodeBeam.UltimateAuth.Client/Components/UALoginForm.razor.cs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -130,7 +130,7 @@ private string ResolvedEndpoint
130130
? EffectiveEndpoint
131131
: Endpoint;
132132

133-
var baseUrl = UAuthUrlBuilder.Combine(Options.Value.Endpoints.Authority, loginPath);
133+
var baseUrl = UAuthUrlBuilder.Build(Options.Value.Endpoints.BasePath, loginPath, Options.Value.MultiTenant);
134134
var returnUrl = EffectiveReturnUrl;
135135

136136
if (string.IsNullOrWhiteSpace(returnUrl))
Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,8 @@
1+
namespace CodeBeam.UltimateAuth.Client.Contracts;
2+
3+
public enum TenantTransport
4+
{
5+
None,
6+
Header,
7+
Route
8+
}
Lines changed: 23 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,29 @@
1-
namespace CodeBeam.UltimateAuth.Client.Infrastructure;
1+
using CodeBeam.UltimateAuth.Client.Contracts;
2+
using CodeBeam.UltimateAuth.Client.Options;
3+
4+
namespace CodeBeam.UltimateAuth.Client.Infrastructure;
25

36
internal static class UAuthUrlBuilder
47
{
5-
public static string Combine(string authority, string relative)
8+
//public static string Combine(string authority, string relative)
9+
//{
10+
// return authority.TrimEnd('/') + "/" + relative.TrimStart('/');
11+
//}
12+
13+
public static string Build(string authority, string relativePath, UAuthClientMultiTenantOptions tenant)
614
{
7-
return authority.TrimEnd('/') + "/" + relative.TrimStart('/');
15+
var baseAuthority = authority.TrimEnd('/');
16+
17+
if (tenant.Enabled && tenant.Transport == TenantTransport.Route)
18+
{
19+
if (string.IsNullOrWhiteSpace(tenant.Tenant))
20+
{
21+
throw new InvalidOperationException("Tenant is enabled for route transport but no tenant value is provided.");
22+
}
23+
24+
baseAuthority = "/" + tenant.Tenant.Trim('/') + baseAuthority;
25+
}
26+
27+
return baseAuthority + "/" + relativePath.TrimStart('/');
828
}
929
}
Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,22 @@
1+
using CodeBeam.UltimateAuth.Client.Contracts;
2+
3+
namespace CodeBeam.UltimateAuth.Client.Options;
4+
5+
public sealed class UAuthClientMultiTenantOptions
6+
{
7+
/// <summary>
8+
/// Enables tenant propagation from client to server.
9+
/// </summary>
10+
public bool Enabled { get; set; }
11+
12+
/// <summary>
13+
/// Tenant identifier to propagate.
14+
/// Client does NOT resolve tenant, only carries it.
15+
/// </summary>
16+
public string? Tenant { get; set; }
17+
18+
/// <summary>
19+
/// Transport mechanism for tenant propagation.
20+
/// </summary>
21+
public TenantTransport Transport { get; set; } = TenantTransport.None;
22+
}

src/CodeBeam.UltimateAuth.Client/Options/UAuthClientOptions.cs

Lines changed: 4 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,7 @@ public sealed class UAuthClientOptions
88
public UAuthClientProfile ClientProfile { get; set; } = UAuthClientProfile.NotSpecified;
99
public bool AutoDetectClientProfile { get; set; } = true;
1010

11-
public AuthEndpointOptions Endpoints { get; set; } = new();
11+
public UAuthClientEndpointOptions Endpoints { get; set; } = new();
1212
public LoginFlowOptions Login { get; set; } = new();
1313

1414
/// <summary>
@@ -17,14 +17,15 @@ public sealed class UAuthClientOptions
1717
public PkceLoginOptions Pkce { get; set; } = new();
1818
public UAuthClientRefreshOptions Refresh { get; set; } = new();
1919
public ReauthOptions Reauth { get; init; } = new();
20+
public UAuthClientMultiTenantOptions MultiTenant { get; set; } = new();
2021
}
2122

22-
public sealed class AuthEndpointOptions
23+
public sealed class UAuthClientEndpointOptions
2324
{
2425
/// <summary>
2526
/// Base URL of UAuthHub (e.g. https://localhost:6110)
2627
/// </summary>
27-
public string Authority { get; set; } = "/auth";
28+
public string BasePath { get; set; } = "/auth";
2829

2930
public string Login { get; set; } = "/login";
3031
public string Logout { get; set; } = "/logout";

src/CodeBeam.UltimateAuth.Client/Services/UAuthAuthorizationClient.cs

Lines changed: 7 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -18,31 +18,29 @@ public UAuthAuthorizationClient(IUAuthRequestClient request, IOptions<UAuthClien
1818
_options = options.Value;
1919
}
2020

21+
private string Url(string path) => UAuthUrlBuilder.Build(_options.Endpoints.BasePath, path, _options.MultiTenant);
22+
2123
public async Task<UAuthResult<AuthorizationResult>> CheckAsync(AuthorizationCheckRequest request)
2224
{
23-
var url = UAuthUrlBuilder.Combine(_options.Endpoints.Authority, "/authorization/check");
24-
var raw = await _request.SendJsonAsync(url, request);
25+
var raw = await _request.SendJsonAsync(Url("/authorization/check"), request);
2526
return UAuthResultMapper.FromJson<AuthorizationResult>(raw);
2627
}
2728

2829
public async Task<UAuthResult<UserRolesResponse>> GetMyRolesAsync()
2930
{
30-
var url = UAuthUrlBuilder.Combine(_options.Endpoints.Authority, "/authorization/users/me/roles/get");
31-
var raw = await _request.SendFormForJsonAsync(url);
31+
var raw = await _request.SendFormForJsonAsync(Url("/authorization/users/me/roles/get"));
3232
return UAuthResultMapper.FromJson<UserRolesResponse>(raw);
3333
}
3434

3535
public async Task<UAuthResult<UserRolesResponse>> GetUserRolesAsync(UserKey userKey)
3636
{
37-
var url = UAuthUrlBuilder.Combine(_options.Endpoints.Authority, $"/admin/authorization/users/{userKey}/roles/get");
38-
var raw = await _request.SendFormForJsonAsync(url);
37+
var raw = await _request.SendFormForJsonAsync(Url($"/admin/authorization/users/{userKey}/roles/get"));
3938
return UAuthResultMapper.FromJson<UserRolesResponse>(raw);
4039
}
4140

4241
public async Task<UAuthResult> AssignRoleAsync(UserKey userKey, string role)
4342
{
44-
var url = UAuthUrlBuilder.Combine(_options.Endpoints.Authority, $"/admin/authorization/users/{userKey}/roles/post");
45-
var raw = await _request.SendJsonAsync(url, new AssignRoleRequest
43+
var raw = await _request.SendJsonAsync(Url($"/admin/authorization/users/{userKey}/roles/post"), new AssignRoleRequest
4644
{
4745
Role = role
4846
});
@@ -52,9 +50,7 @@ public async Task<UAuthResult> AssignRoleAsync(UserKey userKey, string role)
5250

5351
public async Task<UAuthResult> RemoveRoleAsync(UserKey userKey, string role)
5452
{
55-
var url = UAuthUrlBuilder.Combine(_options.Endpoints.Authority, $"/admin/authorization/users/{userKey}/roles/delete");
56-
57-
var raw = await _request.SendJsonAsync(url, new AssignRoleRequest
53+
var raw = await _request.SendJsonAsync(Url($"/admin/authorization/users/{userKey}/roles/delete"), new AssignRoleRequest
5854
{
5955
Role = role
6056
});

src/CodeBeam.UltimateAuth.Client/Services/UAuthCredentialClient.cs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -18,7 +18,7 @@ public UAuthCredentialClient(IUAuthRequestClient request, IOptions<UAuthClientOp
1818
_options = options.Value;
1919
}
2020

21-
private string Url(string path) => UAuthUrlBuilder.Combine(_options.Endpoints.Authority, path);
21+
private string Url(string path) => UAuthUrlBuilder.Build(_options.Endpoints.BasePath, path, _options.MultiTenant);
2222

2323
public async Task<UAuthResult<GetCredentialsResult>> GetMyAsync()
2424
{

src/CodeBeam.UltimateAuth.Client/Services/UAuthFlowClient.cs

Lines changed: 11 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -22,27 +22,25 @@ internal class UAuthFlowClient : IFlowClient
2222
private readonly UAuthClientDiagnostics _diagnostics;
2323
private readonly NavigationManager _nav;
2424

25-
public UAuthFlowClient(
26-
IUAuthRequestClient post,
27-
IOptions<UAuthClientOptions> options,
28-
UAuthClientDiagnostics diagnostics,
29-
NavigationManager nav)
25+
public UAuthFlowClient(IUAuthRequestClient post, IOptions<UAuthClientOptions> options, UAuthClientDiagnostics diagnostics, NavigationManager nav)
3026
{
3127
_post = post;
3228
_options = options.Value;
3329
_diagnostics = diagnostics;
3430
_nav = nav;
3531
}
3632

33+
private string Url(string path) => UAuthUrlBuilder.Build(_options.Endpoints.BasePath, path, _options.MultiTenant);
34+
3735
public async Task LoginAsync(LoginRequest request)
3836
{
39-
var url = UAuthUrlBuilder.Combine(_options.Endpoints.Authority, _options.Endpoints.Login);
37+
var url = Url(_options.Endpoints.Login);
4038
await _post.NavigateAsync(url, request.ToDictionary());
4139
}
4240

4341
public async Task LogoutAsync()
4442
{
45-
var url = UAuthUrlBuilder.Combine(_options.Endpoints.Authority, _options.Endpoints.Logout);
43+
var url = Url(_options.Endpoints.Logout);
4644
await _post.NavigateAsync(url);
4745
}
4846

@@ -53,7 +51,7 @@ public async Task<RefreshResult> RefreshAsync(bool isAuto = false)
5351
_diagnostics.MarkManualRefresh();
5452
}
5553

56-
var url = UAuthUrlBuilder.Combine(_options.Endpoints.Authority, _options.Endpoints.Refresh);
54+
var url = Url(_options.Endpoints.Refresh);
5755
var result = await _post.SendFormAsync(url);
5856
var refreshOutcome = RefreshOutcomeParser.Parse(result.RefreshOutcome);
5957
switch (refreshOutcome)
@@ -82,13 +80,13 @@ public async Task<RefreshResult> RefreshAsync(bool isAuto = false)
8280

8381
public async Task ReauthAsync()
8482
{
85-
var url = UAuthUrlBuilder.Combine(_options.Endpoints.Authority, _options.Endpoints.Reauth);
83+
var url = Url(_options.Endpoints.Reauth);
8684
await _post.NavigateAsync(_options.Endpoints.Reauth);
8785
}
8886

8987
public async Task<AuthValidationResult> ValidateAsync()
9088
{
91-
var url = UAuthUrlBuilder.Combine(_options.Endpoints.Authority, _options.Endpoints.Validate);
89+
var url = Url(_options.Endpoints.Validate);
9290
var raw = await _post.SendFormForJsonAsync(url);
9391

9492
if (!raw.Ok || raw.Body is null)
@@ -123,7 +121,7 @@ public async Task BeginPkceAsync(string? returnUrl = null)
123121
var verifier = CreateVerifier();
124122
var challenge = CreateChallenge(verifier);
125123

126-
var authorizeUrl = UAuthUrlBuilder.Combine(_options.Endpoints.Authority, _options.Endpoints.PkceAuthorize);
124+
var authorizeUrl = Url(_options.Endpoints.PkceAuthorize);
127125

128126
var raw = await _post.SendFormForJsonAsync(
129127
authorizeUrl,
@@ -161,7 +159,7 @@ public async Task CompletePkceLoginAsync(PkceLoginRequest request)
161159
if (request is null)
162160
throw new ArgumentNullException(nameof(request));
163161

164-
var url = UAuthUrlBuilder.Combine(_options.Endpoints.Authority, _options.Endpoints.PkceComplete);
162+
var url = Url(_options.Endpoints.PkceComplete);
165163

166164
var payload = new Dictionary<string, string>
167165
{
@@ -178,7 +176,7 @@ public async Task CompletePkceLoginAsync(PkceLoginRequest request)
178176

179177
private Task NavigateToHubLoginAsync(string authorizationCode, string codeVerifier, string returnUrl)
180178
{
181-
var hubLoginUrl = UAuthUrlBuilder.Combine(_options.Endpoints.Authority, _options.Endpoints.HubLoginPath);
179+
var hubLoginUrl = Url(_options.Endpoints.HubLoginPath);
182180

183181
var data = new Dictionary<string, string>
184182
{

src/CodeBeam.UltimateAuth.Client/Services/UAuthUserClient.cs

Lines changed: 10 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -18,59 +18,53 @@ public UAuthUserClient(IUAuthRequestClient request, IOptions<UAuthClientOptions>
1818
_options = options.Value;
1919
}
2020

21+
private string Url(string path) => UAuthUrlBuilder.Build(_options.Endpoints.BasePath, path, _options.MultiTenant);
22+
2123
public async Task<UAuthResult<UserViewDto>> GetMeAsync()
2224
{
23-
var url = UAuthUrlBuilder.Combine(_options.Endpoints.Authority, "/users/me/get");
24-
var raw = await _request.SendFormForJsonAsync(url);
25+
var raw = await _request.SendFormForJsonAsync(Url("/users/me/get"));
2526
return UAuthResultMapper.FromJson<UserViewDto>(raw);
2627
}
2728

2829
public async Task<UAuthResult> UpdateMeAsync(UpdateProfileRequest request)
2930
{
30-
var url = UAuthUrlBuilder.Combine(_options.Endpoints.Authority, "/users/me/update");
31-
var raw = await _request.SendJsonAsync(url, request);
31+
var raw = await _request.SendJsonAsync(Url("/users/me/update"), request);
3232
return UAuthResultMapper.FromStatus(raw);
3333
}
3434

3535
public async Task<UAuthResult<UserCreateResult>> CreateAsync(CreateUserRequest request)
3636
{
37-
var url = UAuthUrlBuilder.Combine(_options.Endpoints.Authority, "/users/create");
38-
var raw = await _request.SendJsonAsync(url, request);
37+
var raw = await _request.SendJsonAsync(Url("/users/create"), request);
3938
return UAuthResultMapper.FromJson<UserCreateResult>(raw);
4039
}
4140

4241
public async Task<UAuthResult<UserStatusChangeResult>> ChangeStatusSelfAsync(ChangeUserStatusSelfRequest request)
4342
{
44-
var url = UAuthUrlBuilder.Combine(_options.Endpoints.Authority, $"/users/me/status");
45-
var raw = await _request.SendJsonAsync(url, request);
43+
var raw = await _request.SendJsonAsync(Url("/users/me/status"), request);
4644
return UAuthResultMapper.FromJson<UserStatusChangeResult>(raw);
4745
}
4846

4947
public async Task<UAuthResult<UserStatusChangeResult>> ChangeStatusAdminAsync(ChangeUserStatusAdminRequest request)
5048
{
51-
var url = UAuthUrlBuilder.Combine(_options.Endpoints.Authority, $"/admin/users/{request.UserKey.Value}/status");
52-
var raw = await _request.SendJsonAsync(url, request);
49+
var raw = await _request.SendJsonAsync(Url($"/admin/users/{request.UserKey.Value}/status"), request);
5350
return UAuthResultMapper.FromJson<UserStatusChangeResult>(raw);
5451
}
5552

5653
public async Task<UAuthResult<UserDeleteResult>> DeleteAsync(DeleteUserRequest request)
5754
{
58-
var url = UAuthUrlBuilder.Combine(_options.Endpoints.Authority, "/users/delete");
59-
var raw = await _request.SendJsonAsync(url, request);
55+
var raw = await _request.SendJsonAsync(Url("/users/delete"));
6056
return UAuthResultMapper.FromJson<UserDeleteResult>(raw);
6157
}
6258

6359
public async Task<UAuthResult<UserViewDto>> GetProfileAsync(UserKey userKey)
6460
{
65-
var url = UAuthUrlBuilder.Combine(_options.Endpoints.Authority, $"/admin/users/{userKey}/profile/get");
66-
var raw = await _request.SendFormForJsonAsync(url);
61+
var raw = await _request.SendFormForJsonAsync(Url($"/admin/users/{userKey}/profile/get"));
6762
return UAuthResultMapper.FromJson<UserViewDto>(raw);
6863
}
6964

7065
public async Task<UAuthResult> UpdateProfileAsync(UserKey userKey, UpdateProfileRequest request)
7166
{
72-
var url = UAuthUrlBuilder.Combine(_options.Endpoints.Authority, $"/admin/users/{userKey}/profile/update");
73-
var raw = await _request.SendJsonAsync(url, request);
67+
var raw = await _request.SendJsonAsync(Url($"/admin/users/{userKey}/profile/update"), request);
7468
return UAuthResultMapper.FromStatus(raw);
7569
}
7670
}

0 commit comments

Comments
 (0)