From a5f1b17cd96c8053cc3e2ca0a6d66d533a76b349 Mon Sep 17 00:00:00 2001 From: Richard Garcia Date: Sun, 29 Mar 2026 12:17:16 -0300 Subject: [PATCH 1/2] feature(#11): includes the static property `Invalid issuer` to represent an invalid token issuer error, with a specific code and description. --- .../Errors/AuthenticationErrors.cs | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/Applications/Backend/Source/HttpsRichardy.Federation.Domain/Errors/AuthenticationErrors.cs b/Applications/Backend/Source/HttpsRichardy.Federation.Domain/Errors/AuthenticationErrors.cs index 87bad49..4ec44e6 100644 --- a/Applications/Backend/Source/HttpsRichardy.Federation.Domain/Errors/AuthenticationErrors.cs +++ b/Applications/Backend/Source/HttpsRichardy.Federation.Domain/Errors/AuthenticationErrors.cs @@ -17,6 +17,11 @@ public static class AuthenticationErrors Description: "The token signature is invalid." ); + public static readonly Error InvalidIssuer = new( + Code: "#ERROR-1A9C3", + Description: "The token issuer is invalid." + ); + public static readonly Error InvalidRefreshToken = new( Code: "#ERROR-2C0D9", Description: "The provided refresh token is invalid, expired, or has already been used." From 21162a2e73f1df102dca81097d0d63217d4837dc Mon Sep 17 00:00:00 2001 From: Richard Garcia Date: Sun, 29 Mar 2026 12:18:28 -0300 Subject: [PATCH 2/2] feature(#11): this commit introduces validation of the token type and issuer in the OnTokenValidated event. Specific errors are logged in HttpContext.Items and returned in OnChallenge, enabling more detailed authentication responses. --- .../Constants/Authentication.cs | 33 +++++++++++++++++-- 1 file changed, 31 insertions(+), 2 deletions(-) diff --git a/Applications/Backend/Source/HttpsRichardy.Federation.WebApi/Constants/Authentication.cs b/Applications/Backend/Source/HttpsRichardy.Federation.WebApi/Constants/Authentication.cs index d0fc937..5952cb7 100644 --- a/Applications/Backend/Source/HttpsRichardy.Federation.WebApi/Constants/Authentication.cs +++ b/Applications/Backend/Source/HttpsRichardy.Federation.WebApi/Constants/Authentication.cs @@ -26,6 +26,31 @@ public static class Authentication return context.Response.WriteAsync(JsonSerializer.Serialize(AuthenticationErrors.InvalidTokenFormat, _serializer)); }, + OnTokenValidated = context => + { + var request = context.HttpContext.Request; + if (context.SecurityToken is not Microsoft.IdentityModel.JsonWebTokens.JsonWebToken token) + { + context.HttpContext.Items["authentication.error"] = AuthenticationErrors.InvalidTokenFormat; + context.Fail("The token format is invalid or the token is malformed."); + + return Task.CompletedTask; + } + + var expectedIssuer = $"{request.Scheme}://{request.Host}".TrimEnd('/'); + var actualIssuer = token.Issuer?.TrimEnd('/'); + + if (!string.Equals(actualIssuer, expectedIssuer, StringComparison.OrdinalIgnoreCase)) + { + context.HttpContext.Items["authentication.error"] = AuthenticationErrors.InvalidIssuer; + context.Fail("The token issuer is invalid."); + + return Task.CompletedTask; + } + + return Task.CompletedTask; + }, + OnChallenge = context => { context.HandleResponse(); @@ -36,7 +61,11 @@ public static class Authentication context.Response.StatusCode = StatusCodes.Status401Unauthorized; context.Response.ContentType = MediaTypeNames.Application.Json; - return context.Response.WriteAsync(JsonSerializer.Serialize(AuthenticationErrors.Unauthenticated, _serializer)); + var error = context.HttpContext.Items["authentication.error"] as Error + ?? AuthenticationErrors.Unauthenticated; + + return context.Response.WriteAsync(JsonSerializer.Serialize(error, _serializer)); } }; -} \ No newline at end of file +} +