Skip to content

Commit

Permalink
Fixed comments and issues found by SonarQube
Browse files Browse the repository at this point in the history
  • Loading branch information
kirill-abblix committed Sep 26, 2024
1 parent b29241e commit 88e4256
Show file tree
Hide file tree
Showing 21 changed files with 193 additions and 128 deletions.
1 change: 0 additions & 1 deletion Abblix.Jwt.UnitTests/JwtEncryptionTests.cs
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,6 @@
// info@abblix.com

using System.Text.Json.Nodes;
using Abblix.Utils;
using Microsoft.IdentityModel.Tokens;
using Xunit;

Expand Down
2 changes: 1 addition & 1 deletion Abblix.Jwt/JsonWebTokenCreator.cs
Original file line number Diff line number Diff line change
Expand Up @@ -65,7 +65,7 @@ public Task<string> IssueAsync(
{
TokenType = jwt.Header.Type,
Issuer = jwt.Payload.Issuer,
Audience = jwt.Payload.Audiences?.SingleOrDefault(), //TODO replace JwtSecurityTokenHandler with own code to overcome this limitation
Audience = jwt.Payload.Audiences.SingleOrDefault(), //TODO replace JwtSecurityTokenHandler with own code to overcome this limitation

IssuedAt = CheckDateOverflow(jwt.Payload.IssuedAt, nameof(jwt.Payload.IssuedAt)),
NotBefore = CheckDateOverflow(jwt.Payload.NotBefore, nameof(jwt.Payload.NotBefore)),
Expand Down
43 changes: 32 additions & 11 deletions Abblix.Oidc.Server.Mvc/Controllers/AuthenticationController.cs
Original file line number Diff line number Diff line change
Expand Up @@ -22,7 +22,6 @@

using System.Net.Mime;
using Abblix.Oidc.Server.Common.Constants;
using Abblix.Oidc.Server.Common.Exceptions;
using Abblix.Oidc.Server.Endpoints.Authorization.Interfaces;
using Abblix.Oidc.Server.Endpoints.BackChannelAuthentication.Interfaces;
using Abblix.Oidc.Server.Endpoints.CheckSession.Interfaces;
Expand Down Expand Up @@ -57,18 +56,21 @@ namespace Abblix.Oidc.Server.Mvc.Controllers;
public sealed class AuthenticationController : ControllerBase
{
/// <summary>
/// Handles the pushed authorization endpoint. This endpoint is used for receiving and processing pushed authorization requests
/// from clients, validating the request, and generating a response that either contains a URI for the stored authorization request
/// or an error message.
/// Handles the pushed authorization endpoint. This endpoint is used for receiving and processing pushed
/// authorization requests from clients, validating the request, and generating a response that either contains
/// a URI for the stored authorization request or an error message.
/// </summary>
/// <param name="handler">The handler responsible for processing pushed authorization requests.</param>
/// <param name="formatter">The service for formatting the authorization response.</param>
/// <param name="authorizationRequest">The authorization request received from the client.</param>
/// <param name="clientRequest">Additional client request information for contextual validation.</param>
/// <returns>An action result containing the formatted authorization response, which can be a success or an error response.</returns>
/// <returns>
/// An action result containing the formatted authorization response, which can be a success or an error response.
/// </returns>
/// <remarks>
/// This method first validates the incoming authorization request. If the request is valid, it is processed and stored,
/// and a response containing the request URI is returned. If the request is invalid, an error response is generated.
/// This method first validates the incoming authorization request.
/// If the request is valid, it is processed and stored, and a response containing the request URI is returned.
/// If the request is invalid, an error response is generated.
/// </remarks>
[HttpPost(Path.PushAuthorizationRequest)]
[Consumes(MediaTypes.FormUrlEncoded)]
Expand All @@ -86,7 +88,8 @@ public async Task<ActionResult<AuthorizationResponse>> PushAuthorizeAsync(
}

/// <summary>
/// Handles requests to the authorization endpoint, performing user authentication and getting consent for requested scopes.
/// Handles requests to the authorization endpoint, performing user authentication and getting consent for
/// requested scopes.
/// </summary>
/// <param name="handler">The handler responsible for processing authorization requests.</param>
/// <param name="formatter">The formatter used to generate a response for the authorization request.</param>
Expand Down Expand Up @@ -201,13 +204,33 @@ public async Task<ActionResult> CheckSessionAsync(
}

/// <summary>
/// Handles the backchannel authentication endpoint, initiating an out-of-band authentication process.
/// Handles the backchannel authentication endpoint, initiating the authentication flow that occurs outside
/// the traditional user-agent interaction as specified by CIBA.
/// </summary>
/// <remarks>
/// The method implements the CIBA (Client-Initiated Backchannel Authentication) protocol,
/// enabling the authentication of a user through an out-of-band mechanism.
/// Clients initiate the authentication request, and the user's authentication happens through a separate channel
/// (e.g., mobile device).
///
/// For more details, refer to the CIBA documentation:
/// <see href="https://openid.net/specs/openid-client-initiated-backchannel-authentication-core-1_0.html#rfc.section.7">
/// CIBA - Client Initiated Backchannel Authentication Documentation
/// </see>
/// </remarks>
/// <param name="handler">
/// Service that processes the authentication request, validating and initiating the backchannel flow.</param>
/// <param name="formatter">
/// Service that formats the response to the client, based on the result of the backchannel authentication request.
/// </param>
/// <param name="authenticationRequest">
/// The backchannel authentication request containing user-related authentication parameters.</param>
/// <param name="clientRequest">
/// The client request providing the client-related information needed for the request.</param>
/// <returns>
/// An <see cref="ActionResult"/> representing the HTTP response to the backchannel authentication request.
/// The response may indicate successful initiation of the process or an error if the request fails validation.
/// </returns>
[HttpPost(Path.BackchannelAuthentication)]
[Consumes(MediaTypes.FormUrlEncoded)]
public async Task<ActionResult> BackChannelAuthenticationAsync(
Expand All @@ -218,9 +241,7 @@ public async Task<ActionResult> BackChannelAuthenticationAsync(
{
var mappedAuthenticationRequest = authenticationRequest.Map();
var mappedClientRequest = clientRequest.Map();

var response = await handler.HandleAsync(mappedAuthenticationRequest, mappedClientRequest);

return await formatter.FormatResponseAsync(mappedAuthenticationRequest, response);
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -20,37 +20,61 @@
// CONTACT: For license inquiries or permissions, contact Abblix LLP at
// info@abblix.com

using Abblix.Oidc.Server.Common.Constants;
using Abblix.Oidc.Server.Common.Exceptions;
using Abblix.Oidc.Server.Endpoints.BackChannelAuthentication.Interfaces;
using Abblix.Oidc.Server.Model;
using Abblix.Oidc.Server.Mvc.Formatters.Interfaces;
using Microsoft.AspNetCore.Http;
using Microsoft.AspNetCore.Mvc;

namespace Abblix.Oidc.Server.Mvc.Formatters;

/// <summary>
/// Formats responses for back-channel authentication.
/// Handles the formatting of responses for back-channel authentication requests.
/// This class ensures that the appropriate HTTP responses are generated based on the type of back-channel
/// authentication response, encapsulating success or error scenarios as defined by OAuth 2.0 standards.
/// </summary>
public class BackChannelAuthenticationResponseFormatter : IBackChannelAuthenticationResponseFormatter
{
/// <summary>
/// Formats a response for back-channel authentication asynchronously.
/// This method transforms the back-channel authentication response into a suitable HTTP response,
/// ensuring that different outcomes (success or various types of errors) are appropriately represented
/// as HTTP status codes and payloads.
/// </summary>
/// <param name="request">The back-channel authentication request.</param>
/// <param name="response">The back-channel authentication response to be formatted.</param>
/// <param name="request">The original back-channel authentication request that triggered the response.</param>
/// <param name="response">The back-channel authentication response that needs to be formatted into an HTTP result.
/// </param>
/// <returns>A <see cref="Task"/> representing the asynchronous operation,
/// with the formatted response as an <see cref="ActionResult"/>.</returns>
/// <remarks>
/// This method formats successful authentication requests as `200 OK`, client-related errors as `401 Unauthorized`
/// or `403 Forbidden`, and general errors as `400 Bad Request`.
/// It provides consistent handling for different response types,
/// ensuring the API behaves predictably according to the OAuth 2.0 back-channel authentication specification.
/// </remarks>
public Task<ActionResult> FormatResponseAsync(
BackChannelAuthenticationRequest request,
BackChannelAuthenticationResponse response)
{
return Task.FromResult<ActionResult>(response switch
{
// If the authentication was successful, return a 200 OK response with the success details.
BackChannelAuthenticationSuccess success => new OkObjectResult(success),

// If the error indicates an invalid client, return a 401 Unauthorized response.
BackChannelAuthenticationError { Error: ErrorCodes.InvalidClient, ErrorDescription: var description }
=> new UnauthorizedObjectResult(new ErrorResponse(ErrorCodes.InvalidClient, description)),

// If access was denied, return a 403 Forbidden response.
BackChannelAuthenticationError { Error: ErrorCodes.AccessDenied, ErrorDescription: var description }
=> new ObjectResult(new ErrorResponse(ErrorCodes.InvalidClient, description))
{ StatusCode = StatusCodes.Status403Forbidden },

// For any other type of error, return a 400 Bad Request response.
BackChannelAuthenticationError { Error: var error, ErrorDescription: var description }
=> new BadRequestObjectResult(new ErrorResponse(error, description)),

// If the response type is unexpected, throw an exception for further debugging.
_ => throw new UnexpectedTypeException(nameof(response), response.GetType()),
});
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -31,22 +31,21 @@ namespace Abblix.Oidc.Server.Mvc.Formatters;
/// <summary>
/// Provides a response formatter for Check Session frames.
/// </summary>
internal class CheckSessionResponseFormatter : ICheckSessionResponseFormatter
public class CheckSessionResponseFormatter : ICheckSessionResponseFormatter
{
/// <summary>
/// Formats a response for a Check Session frame asynchronously.
/// </summary>
/// <param name="response">The Check Session response containing HTML content.</param>
/// <returns>A <see cref="Task"/> representing the asynchronous operation, with the formatted response as an <see cref="ActionResult"/>.</returns>
/// <returns>A <see cref="Task"/> representing the asynchronous operation,
/// with the formatted response as an <see cref="ActionResult"/>.</returns>
public Task<ActionResult> FormatResponseAsync(CheckSessionResponse response)
{
var content = response.HtmlContent;

var result = new ContentResult
{
StatusCode = StatusCodes.Status200OK,
ContentType = MediaTypeNames.Text.Html,
Content = content,
Content = response.HtmlContent,
};

return Task.FromResult<ActionResult>(result);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,6 @@
// CONTACT: For license inquiries or permissions, contact Abblix LLP at
// info@abblix.com

using Abblix.Oidc.Server.Endpoints.BackChannelAuthentication.Interfaces;
using Abblix.Oidc.Server.Model;
using Microsoft.AspNetCore.Mvc;

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -48,14 +48,14 @@ public class IntrospectionResponseFormatter : IIntrospectionResponseFormatter
/// </remarks>
public Task<ActionResult> FormatResponseAsync(IntrospectionRequest request, IntrospectionResponse response)
{
return Task.FromResult<ActionResult>(response switch
return Task.FromResult(response switch
{
IntrospectionSuccessResponse success => Format(success),

IntrospectionErrorResponse error =>
new UnauthorizedObjectResult(new ErrorResponse(error.Error, error.ErrorDescription)),

_ => throw new ArgumentOutOfRangeException(nameof(response))
_ => throw new ArgumentOutOfRangeException(nameof(response)),
});
}

Expand Down
2 changes: 1 addition & 1 deletion Abblix.Oidc.Server.Tests/OidcClient.cs
Original file line number Diff line number Diff line change
Expand Up @@ -56,7 +56,7 @@ private async Task DiscoverEndpoints()
{
var discoveryResponse = JsonDocument.Parse(await _client.GetStringAsync(".well-known/openid-configuration"));

Uri Discover(string name) => new(discoveryResponse.RootElement.GetProperty(name).GetString());
Uri Discover(string name) => Uri.TryCreate(discoveryResponse.RootElement.GetProperty(name).GetString(), UriKind.RelativeOrAbsolute, out var uri) ? uri : default;

_authorizationEndpoint = Discover("authorization_endpoint");
_tokenEndpoint = Discover("token_endpoint");
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -33,6 +33,6 @@ public class ClientRegistrationRequestTest
"{\"client_name\":\"dynamic_client_1 RqxLk9BdhK8qC3z\",\"grant_types\":[\"implicit\"],\"jwks\":{\"keys\":[{\"kty\":\"RSA\",\"e\":\"AQAB\",\"use\":\"sig\",\"alg\":\"RS256\",\"n\":\"gUOdYo2PpUUnZzozIPJ-7mK2Z5jYBxjj_5iB2TDnElt8yUc-mcCeOQrsaswPgKx2KMSJ50kwrFHHEuNyiDhgNMgtmJ98RuhggXaPF1fmmHss_Wc1OSqyGYLWbEzYGsRck5yTVP4xsPYAeP5xkkLze_FXJvwITNu2aGxXEYwokkrcWgL3AsXtYKClIwmacHhVNEMn-ALe3sMTifx4F8TqmNAlD4FPga094txHJNo2Ho6z4kn5L4uq_WXklDjaIDOqQZtdn0emXig3RHQcOtepFcXt7pcK9E2M3kxKFOMPpY8c4kaDfQ41jv23vbm9oDTh5s3TB0ZwcKJXj4-06gwTWw\"}]},\"token_endpoint_auth_method\":\"client_secret_basic\",\"response_types\":[\"id_token token\"],\"redirect_uris\":[\"https://www.certification.openid.net/test/a/Abblix/callback\"],\"contacts\":[\"certification@oidf.org\"]}")]
public void DeserializeClientRegistrationRequestTest(string json)
{
var request = JsonSerializer.Deserialize<ClientRegistrationRequest>(json);
JsonSerializer.Deserialize<ClientRegistrationRequest>(json);
}
}
43 changes: 24 additions & 19 deletions Abblix.Oidc.Server/Common/OperationResult.cs
Original file line number Diff line number Diff line change
Expand Up @@ -37,41 +37,46 @@ private OperationResult() { }
/// Represents a successful result containing a value of type <typeparamref name="T"/>.
/// </summary>
/// <param name="Value">The value returned by the successful operation.</param>
public sealed record Success(T Value) : OperationResult<T>;
public sealed record Success(T Value) : OperationResult<T>
{
/// <summary>
/// Returns a string that represents the current object, either the successful value or an error description.
/// </summary>
/// <returns>
/// A string representation of the result, displaying the value in case of success,
/// or an error message in case of failure.
/// </returns>
public override string ToString() => Value?.ToString() ?? string.Empty;
}

/// <summary>
/// Represents an error result containing an error code and a descriptive message.
/// </summary>
/// <param name="ErrorCode">The code representing the type or cause of the error.</param>
/// <param name="ErrorDescription">A human-readable description of the error.</param>
public sealed record Error(string ErrorCode, string ErrorDescription) : OperationResult<T>;
public sealed record Error(string ErrorCode, string ErrorDescription) : OperationResult<T>
{
/// <summary>
/// Returns a string that represents the current object, either the successful value or an error description.
/// </summary>
/// <returns>
/// A string representation of the result, displaying the value in case of success,
/// or an error message in case of failure.
/// </returns>
public override string ToString()
=> $"{nameof(ErrorCode)}: {ErrorCode}, {nameof(ErrorDescription)}: {ErrorDescription}";
}

/// <summary>
/// Implicitly converts a value of type <typeparamref name="T"/> into a successful <see cref="Success"/> result.
/// </summary>
/// <param name="value">The value to be wrapped as a successful result.</param>
public static implicit operator OperationResult<T>(T value)
=> new Success(value);
public static implicit operator OperationResult<T>(T value) => new Success(value);

/// <summary>
/// Implicitly converts an <see cref="ErrorResponse"/> into an <see cref="Error"/> result.
/// </summary>
/// <param name="error">The error response to be wrapped as an error result.</param>
public static implicit operator OperationResult<T>(ErrorResponse error)
=> new Error(error.Error, error.ErrorDescription);

/// <summary>
/// Returns a string that represents the current object, either the successful value or an error description.
/// </summary>
/// <returns>
/// A string representation of the result, displaying the value in case of success,
/// or an error message in case of failure.
/// </returns>
public override string? ToString()
=> this switch
{
Success(var value) => value?.ToString(),
Error(var error, var description) => $"Error: {error}, Description: {description}",
_ => $"Unexpected type: {GetType().FullName}",
};
}
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,6 @@
// CONTACT: For license inquiries or permissions, contact Abblix LLP at
// info@abblix.com

using System.Runtime.InteropServices.ComTypes;
using Abblix.Oidc.Server.Common;
using Abblix.Oidc.Server.Common.Constants;
using Abblix.Oidc.Server.Endpoints.Authorization.Interfaces;
Expand Down
Loading

0 comments on commit 88e4256

Please sign in to comment.