Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@
using CodeBeam.UltimateAuth.Core.Domain;
using CodeBeam.UltimateAuth.Core.Options;
using CodeBeam.UltimateAuth.Server.Options;
using Microsoft.Net.Http.Headers;

namespace CodeBeam.UltimateAuth.Server.Auth;

Expand Down Expand Up @@ -76,10 +77,23 @@ private static void ValidateDelivery(CredentialResponseOptions delivery)
if (delivery.Mode == TokenResponseMode.Body)
throw new NotSupportedException($"Body credential delivery is not supported for '{delivery.Kind}'.");

if (delivery.Mode == TokenResponseMode.Cookie && delivery.Cookie is null)
if (delivery.Mode != TokenResponseMode.Cookie)
return;

if (delivery.Cookie is null)
{
throw new InvalidOperationException($"Credential '{delivery.Kind}' is configured as Cookie but no cookie options were bound.");
}

try
{
var cookie = new SetCookieHeaderValue(delivery.Cookie.Name, "validation");
_ = cookie.ToString();
}
catch (ArgumentException ex)
{
throw new InvalidOperationException($"Invalid cookie name configured for '{delivery.Kind}': '{delivery.Cookie.Name}'.", ex);
}
}

private static EffectiveRedirectResponse ResolveRedirect(AuthFlowType flowType, UAuthResponseOptions bound)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,10 @@ public UAuthResponseOptions Adapt(UAuthResponseOptions template, UAuthClientProf
{
var configured = effectiveOptions.Options.AuthResponse;

ValidateConfiguredDelivery(configured.SessionIdDelivery);
ValidateConfiguredDelivery(configured.AccessTokenDelivery);
ValidateConfiguredDelivery(configured.RefreshTokenDelivery);

return new UAuthResponseOptions
{
SessionIdDelivery = AdaptCredential(template.SessionIdDelivery, GrantKind.Session, clientProfile),
Expand Down Expand Up @@ -77,4 +81,12 @@ private static LogoutRedirectOptions MergeLogout(LogoutRedirectOptions template,
AllowReturnUrlOverride = configured.AllowReturnUrlOverride
};
}

private static void ValidateConfiguredDelivery(CredentialResponseOptions delivery)
{
if (delivery.Mode == TokenResponseMode.Body)
{
throw new NotSupportedException($"Body credential delivery is not currently supported for '{delivery.Kind}'.");
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -72,8 +72,8 @@ public void MapEndpoints(RouteGroupBuilder rootGroup, UAuthServerOptions options
group.MapPost("/refresh", async ([FromServices] IRefreshEndpointHandler h, HttpContext ctx)
=> await h.RefreshAsync(ctx)).WithMetadata(new AuthFlowMetadata(AuthFlowType.RefreshSession));

group.MapPost("/reauth", async ([FromServices] IReauthEndpointHandler h, HttpContext ctx)
=> await h.ReauthAsync(ctx)).WithMetadata(new AuthFlowMetadata(AuthFlowType.Reauthentication));
//group.MapPost("/reauth", async ([FromServices] IReauthEndpointHandler h, HttpContext ctx)
// => await h.ReauthAsync(ctx)).WithMetadata(new AuthFlowMetadata(AuthFlowType.Reauthentication));


if (Enabled(UAuthActions.Flows.LogoutDeviceAdmin))
Expand Down Expand Up @@ -206,7 +206,7 @@ public void MapEndpoints(RouteGroupBuilder rootGroup, UAuthServerOptions options

if (Enabled(UAuthActions.Users.CreateAdmin))
adminUsers.MapPost("/create", async ([FromServices] IUserEndpointHandler h, HttpContext ctx)
=> await h.CreateAsync(ctx)).WithMetadata(new AuthFlowMetadata(AuthFlowType.UserManagement));
=> await h.CreateAdminAsync(ctx)).WithMetadata(new AuthFlowMetadata(AuthFlowType.UserManagement));

if (Enabled(UAuthActions.Users.ChangeStatusAdmin))
adminUsers.MapPost("/{userKey}/status", async ([FromServices] IUserEndpointHandler h, UserKey userKey, HttpContext ctx)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -36,6 +36,7 @@ public sealed class UAuthCookieOptions
SameSite = SameSite,
Path = Path,
MaxAge = MaxAge,
Lifetime = Lifetime.Clone()
Lifetime = Lifetime.Clone(),
Domain = Domain
};
}
Original file line number Diff line number Diff line change
Expand Up @@ -25,7 +25,7 @@ public UAuthAuthorizationClient(IUAuthRequestClient request, IUAuthClientEvents

public async Task<UAuthResult<AuthorizationResult>> CheckAsync(AuthorizationCheckRequest request)
{
var raw = await _request.SendJsonAsync(Url("/authorization/check"), request);
var raw = await _request.SendJsonAsync(Url("/me/authorization/check"), request);
return UAuthResultMapper.FromJson<AuthorizationResult>(raw);
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -149,7 +149,7 @@ public async Task<UAuthResult> DeleteUserAsync(UserKey userKey, DeleteUserIdenti

public async Task<UAuthResult<UserIdentifierAvailabilityResult>> CheckAvailabilityAsync(CheckUserIdentifierAvailabilityRequest request)
{
var raw = await _request.SendJsonAsync(Url("/users/identifiers/check-availability"), request);
var raw = await _request.SendJsonAsync(Url("/me/identifiers/check-availability"), request);
return UAuthResultMapper.FromJson<UserIdentifierAvailabilityResult>(raw);
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -641,6 +641,27 @@ await profileFactory
.Equal($"{prefix}-Delta", $"{prefix}-Echo");
}

[Fact]
public async Task AdminCreate_WhenAnonymous_ShouldReturnUnauthorized()
{
_factory.Clock.Reset();

using var client = CreateClient();

var response = await client.PostAsJsonAsync(
"/auth/admin/users/create",
new CreateUserRequest
{
UserName = $"admin-create-{Guid.NewGuid():N}",
Password = $"Test-{Guid.NewGuid():N}!",
DisplayName = "Unauthorized Creation"
});

response.StatusCode.Should()
.Be(HttpStatusCode.Unauthorized);
}


// -------------------------------------------------------
// Helpers
// -------------------------------------------------------
Expand Down
Loading