Skip to content

Commit 52b0d4e

Browse files
committed
Fixed Login Failure Message Flow
1 parent e979710 commit 52b0d4e

File tree

10 files changed

+62
-33
lines changed

10 files changed

+62
-33
lines changed

samples/UAuthHub/CodeBeam.UltimateAuth.Sample.UAuthHub/CodeBeam.UltimateAuth.Sample.UAuthHub.csproj

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -10,7 +10,7 @@
1010

1111
<ItemGroup>
1212
<PackageReference Include="CodeBeam.MudBlazor.Extensions" Version="9.0.0" />
13-
<PackageReference Include="MudBlazor" Version="9.0.0-rc.1" />
13+
<PackageReference Include="MudBlazor" Version="9.0.0" />
1414
</ItemGroup>
1515

1616
<ItemGroup>

samples/blazor-server/CodeBeam.UltimateAuth.Sample.BlazorServer/Components/Pages/Login.razor.cs

Lines changed: 3 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,6 @@
22
using CodeBeam.UltimateAuth.Client.Runtime;
33
using CodeBeam.UltimateAuth.Core.Contracts;
44
using CodeBeam.UltimateAuth.Core.Domain;
5-
using CodeBeam.UltimateAuth.Users.Contracts;
65
using Microsoft.AspNetCore.Components;
76
using Microsoft.AspNetCore.Components.Authorization;
87
using MudBlazor;
@@ -16,7 +15,7 @@ public partial class Login
1615
private string? _password;
1716
private ClaimsPrincipal? _aspNetCoreState;
1817
private UAuthClientProductInfo? _productInfo;
19-
private MudTextField<string> _usernameField;
18+
private MudTextField<string> _usernameField = default!;
2019

2120
[CascadingParameter]
2221
public UAuthState AuthState { get; set; } = default!;
@@ -93,9 +92,9 @@ private void ShowLoginError(string code)
9392
{
9493
var message = code switch
9594
{
96-
"invalid" => "Invalid username or password.",
95+
"invalid_credentials" => "Invalid username or password.",
9796
"locked" => "Your account is locked.",
98-
"mfa" => "Multi-factor authentication required.",
97+
"mfa_required" => "Multi-factor authentication required.",
9998
_ => "Login failed."
10099
};
101100

samples/blazor-standalone-wasm/CodeBeam.UltimateAuth.Sample.BlazorStandaloneWasm/CodeBeam.UltimateAuth.Sample.BlazorStandaloneWasm.csproj

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
<Project Sdk="Microsoft.NET.Sdk.BlazorWebAssembly">
1+
<Project Sdk="Microsoft.NET.Sdk.BlazorWebAssembly">
22

33
<PropertyGroup>
44
<TargetFramework>net10.0</TargetFramework>
@@ -13,7 +13,7 @@
1313
<PackageReference Include="Microsoft.AspNetCore.Components.WebAssembly.DevServer" Version="10.0.1" PrivateAssets="all" />
1414
<PackageReference Include="Microsoft.AspNetCore.WebUtilities" Version="10.0.1" />
1515
<PackageReference Include="Microsoft.Extensions.Http" Version="10.0.1" />
16-
<PackageReference Include="MudBlazor" Version="9.0.0-rc.1" />
16+
<PackageReference Include="MudBlazor" Version="9.0.0" />
1717
</ItemGroup>
1818

1919
<ItemGroup>

src/CodeBeam.UltimateAuth.Core/Domain/Principals/AuthFailureReason.cs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -9,5 +9,6 @@ public enum AuthFailureReason
99
SessionRevoked,
1010
TenantDisabled,
1111
Unauthorized,
12+
ReauthenticationRequired,
1213
Unknown
1314
}

src/CodeBeam.UltimateAuth.Server/Auth/Response/EffectiveRedirectResponse.cs

Lines changed: 8 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -31,14 +31,14 @@ public EffectiveRedirectResponse(
3131
public static readonly EffectiveRedirectResponse Disabled = new(false, null, null, null, null, false);
3232

3333
public static EffectiveRedirectResponse FromLogin(LoginRedirectOptions login)
34-
=> new(
35-
login.RedirectEnabled,
36-
login.SuccessRedirect,
37-
login.FailureRedirect,
38-
login.FailureQueryKey,
39-
login.FailureCodes,
40-
login.AllowReturnUrlOverride
41-
);
34+
=> new(
35+
login.RedirectEnabled,
36+
login.SuccessRedirect,
37+
login.FailureRedirect,
38+
login.FailureQueryKey,
39+
login.FailureCodes,
40+
login.AllowReturnUrlOverride
41+
);
4242

4343
public static EffectiveRedirectResponse FromLogout(LogoutRedirectOptions logout)
4444
=> new(
Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,20 @@
1+
using CodeBeam.UltimateAuth.Core.Domain;
2+
3+
namespace CodeBeam.UltimateAuth.Server.Extensions;
4+
5+
public static class AuthFailureReasonExtensions
6+
{
7+
public static string ToDefaultCode(this AuthFailureReason reason)
8+
=> reason switch
9+
{
10+
AuthFailureReason.InvalidCredentials => "invalid_credentials",
11+
AuthFailureReason.LockedOut => "locked",
12+
AuthFailureReason.RequiresMfa => "mfa_required",
13+
AuthFailureReason.SessionExpired => "session_expired",
14+
AuthFailureReason.SessionRevoked => "session_revoked",
15+
AuthFailureReason.TenantDisabled => "tenant_disabled",
16+
AuthFailureReason.Unauthorized => "unauthorized",
17+
AuthFailureReason.ReauthenticationRequired => "reauthentication_required",
18+
_ => "failed"
19+
};
20+
}

src/CodeBeam.UltimateAuth.Server/Flows/Login/LoginAuthority.cs

Lines changed: 6 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,5 @@
1-
using CodeBeam.UltimateAuth.Core.Options;
1+
using CodeBeam.UltimateAuth.Core.Domain;
2+
using CodeBeam.UltimateAuth.Core.Options;
23
using CodeBeam.UltimateAuth.Server.Options;
34
using Microsoft.Extensions.Options;
45

@@ -21,22 +22,22 @@ public LoginDecision Decide(LoginDecisionContext context)
2122
{
2223
if (!context.UserExists || context.UserKey is null)
2324
{
24-
return LoginDecision.Deny("Invalid credentials.");
25+
return LoginDecision.Deny(AuthFailureReason.InvalidCredentials);
2526
}
2627

2728
var state = context.SecurityState;
2829
if (state is not null)
2930
{
3031
if (state.IsLocked)
31-
return LoginDecision.Deny("user_is_locked");
32+
return LoginDecision.Deny(AuthFailureReason.LockedOut);
3233

3334
if (state.RequiresReauthentication)
34-
return LoginDecision.Challenge("reauth_required");
35+
return LoginDecision.Challenge(AuthFailureReason.ReauthenticationRequired);
3536
}
3637

3738
if (!context.CredentialsValid)
3839
{
39-
return LoginDecision.Deny("Invalid credentials.");
40+
return LoginDecision.Deny(AuthFailureReason.InvalidCredentials);
4041
}
4142

4243
return LoginDecision.Allow();
Lines changed: 9 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -1,25 +1,28 @@
1-
namespace CodeBeam.UltimateAuth.Server.Flows;
1+
using CodeBeam.UltimateAuth.Core.Domain;
2+
3+
namespace CodeBeam.UltimateAuth.Server.Flows;
24

35
/// <summary>
46
/// Represents the outcome of a login decision.
57
/// </summary>
68
public sealed class LoginDecision
79
{
810
public LoginDecisionKind Kind { get; }
9-
public string? Reason { get; }
11+
public AuthFailureReason? FailureReason { get; }
12+
1013

11-
private LoginDecision(LoginDecisionKind kind, string? reason = null)
14+
private LoginDecision(LoginDecisionKind kind, AuthFailureReason? reason = null)
1215
{
1316
Kind = kind;
14-
Reason = reason;
17+
FailureReason = reason;
1518
}
1619

1720
public static LoginDecision Allow()
1821
=> new(LoginDecisionKind.Allow);
1922

20-
public static LoginDecision Deny(string reason)
23+
public static LoginDecision Deny(AuthFailureReason reason)
2124
=> new(LoginDecisionKind.Deny, reason);
2225

23-
public static LoginDecision Challenge(string reason)
26+
public static LoginDecision Challenge(AuthFailureReason reason)
2427
=> new(LoginDecisionKind.Challenge, reason);
2528
}

src/CodeBeam.UltimateAuth.Server/Flows/Login/LoginOrchestrator.cs

Lines changed: 3 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -156,19 +156,18 @@ public async Task<LoginResult> LoginAsync(AuthFlowContext flow, LoginRequest req
156156
}
157157

158158
if (decision.Kind == LoginDecisionKind.Deny)
159-
return LoginResult.Failed();
159+
return LoginResult.Failed(decision.FailureReason);
160160

161161
if (decision.Kind == LoginDecisionKind.Challenge)
162162
{
163163
return LoginResult.Continue(new LoginContinuation
164164
{
165-
Type = LoginContinuationType.Mfa,
166-
Hint = decision.Reason
165+
Type = LoginContinuationType.Mfa
167166
});
168167
}
169168

170169
if (validatedUserId is null || userKey is not UserKey validUserKey)
171-
return LoginResult.Failed();
170+
return LoginResult.Failed(AuthFailureReason.InvalidCredentials);
172171

173172
var claims = await _claimsProvider.GetClaimsAsync(request.Tenant, validUserKey, ct);
174173

src/CodeBeam.UltimateAuth.Server/Infrastructure/Redirect/AuthRedirectResolver.cs

Lines changed: 9 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
using CodeBeam.UltimateAuth.Core.Domain;
22
using CodeBeam.UltimateAuth.Server.Auth;
3+
using CodeBeam.UltimateAuth.Server.Extensions;
34
using CodeBeam.UltimateAuth.Server.Options;
45
using Microsoft.AspNetCore.Http;
56

@@ -27,7 +28,7 @@ private RedirectDecision Resolve(AuthFlowContext flow, HttpContext ctx, string?
2728
if (!redirect.Enabled)
2829
return RedirectDecision.None();
2930

30-
if (redirect.AllowReturnUrlOverride && flow.ReturnUrlInfo is { } info)
31+
if (failureReason is null && redirect.AllowReturnUrlOverride && flow.ReturnUrlInfo is { } info)
3132
{
3233
if (info.IsAbsolute && (info.AbsoluteUri!.Scheme == Uri.UriSchemeHttp || info.AbsoluteUri!.Scheme == Uri.UriSchemeHttps))
3334
{
@@ -54,12 +55,17 @@ private RedirectDecision Resolve(AuthFlowContext flow, HttpContext ctx, string?
5455
var code = redirect.FailureCodes != null &&
5556
redirect.FailureCodes.TryGetValue(failureReason.Value, out var mapped)
5657
? mapped
57-
: "failed";
58+
: failureReason.Value.ToDefaultCode();
5859

5960
query = new Dictionary<string, string?>
6061
{
61-
[redirect.FailureQueryKey ?? "error"] = code
62+
[redirect.FailureQueryKey ?? "error"] = code,
6263
};
64+
65+
if (!string.IsNullOrWhiteSpace(flow.ReturnUrlInfo?.RelativePath))
66+
{
67+
query["returnUrl"] = flow.ReturnUrlInfo.RelativePath;
68+
}
6369
}
6470

6571
return RedirectDecision.To(UrlComposer.Combine(baseAddress, fallbackPath, query));

0 commit comments

Comments
 (0)