Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Append missing iss param to authorization response #11

Merged
merged 4 commits into from
Jun 19, 2024
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
27 changes: 26 additions & 1 deletion Abblix.Jwt/JsonWebKeyFactory.cs
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,7 @@
// info@abblix.com

using System.Security.Cryptography;
using Abblix.Utils;
using Microsoft.IdentityModel.Tokens;

namespace Abblix.Jwt;
Expand Down Expand Up @@ -53,12 +54,14 @@ public static class JsonWebKeyFactory
rsa.KeySize = keySize;
var parameters = rsa.ExportParameters(true);

var parametersExponent = parameters.Exponent;
var key = new JsonWebKey
{
KeyType = "RSA",
KeyId = parameters.ToKeyId(),
Algorithm = algorithm,
Usage = usage,
RsaExponent = parameters.Exponent,
RsaExponent = parametersExponent,
RsaModulus = parameters.Modulus,
PrivateKey = parameters.D,
FirstPrimeFactor = parameters.P,
Expand All @@ -70,4 +73,26 @@ public static class JsonWebKeyFactory

return key;
}

private static string ToKeyId(this RSAParameters parameters)
{
var keyMaterial = (parameters.Modulus, parameters.Exponent) switch
{
({} modulus, {} exponent) => modulus.Concat(exponent),
({} modulus, null) => modulus,
(null, {} exponent) => exponent,
(null, null) => Array.Empty<byte>(),
};

// Compute the SHA-256 hash of the concatenated string
return SHA256.HashData(keyMaterial).ToHexString();
}

private static byte[] Concat(this byte[] modulus, byte[] exponent)
{
var buffer = new byte[modulus.Length + exponent.Length];
Array.Copy(modulus, buffer, modulus.Length);
Array.Copy(exponent, 0, buffer, modulus.Length, exponent.Length);
return buffer;
}
}
18 changes: 14 additions & 4 deletions Abblix.Oidc.Server.Mvc/Formatters/AuthorizationErrorFormatter.cs
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,7 @@

using Abblix.Oidc.Server.Common.Constants;
using Abblix.Oidc.Server.Endpoints.Authorization.Interfaces;
using Abblix.Oidc.Server.Features.Issuer;
using Abblix.Oidc.Server.Model;
using Abblix.Oidc.Server.Mvc.Binders;
using Abblix.Utils;
Expand All @@ -37,15 +38,23 @@ namespace Abblix.Oidc.Server.Mvc.Formatters;
public class AuthorizationErrorFormatter
{
/// <summary>
/// Initializes a new instance of <see cref="AuthorizationErrorFormatter"/> with the necessary parameter provider.
/// Initializes a new instance of <see cref="AuthorizationErrorFormatter"/> with necessary dependencies for
/// response parameter handling.
/// </summary>
/// <param name="parametersProvider">The provider for extracting and formatting response parameters.</param>
public AuthorizationErrorFormatter(IParametersProvider parametersProvider)
/// <param name="parametersProvider">The provider for extracting and formatting response parameters,
/// which includes details like state and error descriptions.</param>
/// <param name="issuerProvider">The provider for the issuer URL, ensuring the 'iss' claim is correctly
/// included in error responses if applicable.</param>
public AuthorizationErrorFormatter(
IParametersProvider parametersProvider,
IIssuerProvider issuerProvider)
{
_parametersProvider = parametersProvider;
_issuerProvider = issuerProvider;
}

private readonly IParametersProvider _parametersProvider;
protected readonly IIssuerProvider _issuerProvider;

/// <summary>
/// Asynchronously formats an authorization error response into an HTTP action result,
Expand All @@ -72,10 +81,11 @@ private ActionResult FormatResponse(AuthorizationRequest request, AuthorizationE

var response = new AuthorizationResponse
{
State = request.State,
Issuer = _issuerProvider.GetIssuer(),
Error = error.Error,
ErrorDescription = error.ErrorDescription,
ErrorUri = error.ErrorUri,
State = request.State,
};

return ToActionResult(response, error.ResponseMode, redirectUri);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,7 @@
using Abblix.Oidc.Server.Common.Constants;
using Abblix.Oidc.Server.Common.Exceptions;
using Abblix.Oidc.Server.Endpoints.Authorization.Interfaces;
using Abblix.Oidc.Server.Features.Issuer;
using Abblix.Oidc.Server.Features.SessionManagement;
using Abblix.Oidc.Server.Features.Storages;
using Abblix.Oidc.Server.Model;
Expand Down Expand Up @@ -59,14 +60,15 @@ internal class AuthorizationResponseFormatter : AuthorizationErrorFormatter, IAu
/// The service responsible for managing user sessions within the authorization process.</param>
/// <param name="httpContextAccessor">
/// Accessor to obtain the current HTTP context, facilitating access to request and response objects.</param>

public AuthorizationResponseFormatter(
IOptions<OidcOptions> options,
/// <param name="issuerProvider">
/// Provides issuer information crucial for generating consistent authorization responses.</param>
public AuthorizationResponseFormatter(IOptions<OidcOptions> options,
IAuthorizationRequestStorage authorizationRequestStorage,
IParametersProvider parametersProvider,
ISessionManagementService sessionManagementService,
IHttpContextAccessor httpContextAccessor)
: base(parametersProvider)
IHttpContextAccessor httpContextAccessor,
IIssuerProvider issuerProvider)
: base(parametersProvider, issuerProvider)
{
_options = options;
_authorizationRequestStorage = authorizationRequestStorage;
Expand Down Expand Up @@ -116,19 +118,16 @@ public async Task<ActionResult> FormatResponseAsync(
var modelResponse = new AuthorizationResponse
{
State = response.Model.State,
Issuer = _issuerProvider.GetIssuer(),
Scope = string.Join(' ', response.Model.Scope),

Code = success.Code,

TokenType = success.TokenType,
AccessToken = success.AccessToken?.EncodedJwt,

IdToken = success.IdToken?.EncodedJwt,

SessionState = success.SessionState,
};

var actionResult = ToActionResult(modelResponse, success.ResponseMode, response.Model.RedirectUri);
var actionResult = ToActionResult(modelResponse, success.ResponseMode, success.Model.RedirectUri);

if (_sessionManagementService.Enabled &&
success.SessionId.HasValue() &&
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,7 @@
using Abblix.Oidc.Server.Common.Exceptions;
using Abblix.Oidc.Server.Endpoints.Authorization.Interfaces;
using Abblix.Oidc.Server.Endpoints.PushedAuthorization.Interfaces;
using Abblix.Oidc.Server.Features.Issuer;
using Abblix.Oidc.Server.Model;
using Abblix.Oidc.Server.Mvc.Binders;
using Abblix.Oidc.Server.Mvc.Formatters.Interfaces;
Expand All @@ -39,11 +40,12 @@ public class PushedAuthorizationResponseFormatter : AuthorizationErrorFormatter,
{
/// <summary>
/// Initializes a new instance of the <see cref="PushedAuthorizationResponseFormatter"/> class
/// with the specified parameters provider.
/// with the specified parameters' provider.
/// </summary>
/// <param name="parametersProvider">Provides access to parameters used in formatting the response.</param>
public PushedAuthorizationResponseFormatter(IParametersProvider parametersProvider)
: base(parametersProvider)
/// <param name="issuerProvider">Provides access to the issuer information used in responses.</param>
public PushedAuthorizationResponseFormatter(IParametersProvider parametersProvider, IIssuerProvider issuerProvider)
: base(parametersProvider, issuerProvider)
{
}

Expand Down
1 change: 0 additions & 1 deletion Abblix.Oidc.Server.Mvc/Model/AuthorizationRequest.cs
Original file line number Diff line number Diff line change
Expand Up @@ -42,7 +42,6 @@ public record AuthorizationRequest
/// The specific scopes requested determine the access privileges granted.
/// </summary>
[BindProperty(SupportsGet = true, Name = Parameters.Scope)]
[AllowedValues(Scopes.OpenId, Scopes.Profile, Scopes.Email, Scopes.Phone, Scopes.Address, Scopes.OfflineAccess)]
[ModelBinder(typeof(SpaceSeparatedValuesBinder))]
public string[] Scope { get; init; } = Array.Empty<string>();

Expand Down
25 changes: 19 additions & 6 deletions Abblix.Oidc.Server.Mvc/Model/AuthorizationResponse.cs
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,8 @@ namespace Abblix.Oidc.Server.Mvc.Model;

/// <summary>
/// Represents an authorization response containing the result of an authorization request.
/// This includes potential error details, codes, tokens, and additional related data used
/// in both success and error states of OAuth 2.0 and OpenID Connect authorization processes.
/// </summary>
public record AuthorizationResponse
{
Expand All @@ -46,35 +48,39 @@ private static class Parameters

public const string Scope = "scope";
public const string SessionState = "session_state";

public const string Issuer = "iss";
}

/// <summary>
/// The error code if the authorization request has failed.
/// The error code if the authorization request has failed, identifying the specific error that occurred.
/// </summary>
[JsonPropertyName(Parameters.Error)]
public string? Error { init; get; }

/// <summary>
/// The human-readable description of the error if the authorization request has failed.
/// A human-readable explanation of the error, providing detailed insight into why the authorization request failed.
/// </summary>
[JsonPropertyName(Parameters.ErrorDescription)]
public string? ErrorDescription { init; get; }

/// <summary>
/// The URI for more information about the error if the authorization request has failed.
/// A URI to a web resource that provides more information about the error, helping clients understand or
/// mitigate the issue.
/// </summary>
[JsonPropertyName(Parameters.ErrorUri)]
public Uri? ErrorUri { init; get; }

/// <summary>
/// The state parameter that was included in the initial authorization request.
/// The state parameter originally provided by the client in the authorization request, returned unaltered in
/// the response to maintain state between the client and the authorization server.
/// </summary>
[JsonPropertyName(Parameters.State)]
public string? State { init; get; }

/// <summary>
/// The authorization code generated by the authorization server.
/// This code is used in the authorization code flow of OAuth 2.0 to obtain an access token.
/// The authorization code generated by the authorization server. This code must be exchanged for an access token
/// at the token endpoint as part of the authorization code flow.
/// </summary>
[JsonPropertyName(Parameters.Code)]
public string? Code { get; set; }
Expand Down Expand Up @@ -114,4 +120,11 @@ private static class Parameters
/// </summary>
[JsonPropertyName(Parameters.SessionState)]
public string? SessionState { get; set; }

/// <summary>
/// The issuer of the response, which is typically the URL of the authorization server from
/// which the response originated.
/// </summary>
[JsonPropertyName(Parameters.Issuer)]
public string? Issuer { get; set; }
}
Original file line number Diff line number Diff line change
Expand Up @@ -23,7 +23,6 @@
using Abblix.Jwt;
using Abblix.Oidc.Server.Common.Configuration;
using Abblix.Oidc.Server.Common.Interfaces;
using Abblix.Utils;
using Microsoft.Extensions.Options;


Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -34,7 +34,11 @@ namespace Abblix.Oidc.Server.Endpoints.Authorization.Interfaces;
/// <param name="SessionId">An optional session identifier that may be used for session management.</param>
/// <param name="AffectedClientIds"> Identifiers of the clients that are affected by or related to this authentication
/// process.</param>
public record SuccessfullyAuthenticated(AuthorizationRequest Model, string ResponseMode, string? SessionId, ICollection<string> AffectedClientIds)
public record SuccessfullyAuthenticated(
AuthorizationRequest Model,
string ResponseMode,
string? SessionId,
ICollection<string> AffectedClientIds)
: AuthorizationResponse(Model)
{
/// <summary>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -28,7 +28,6 @@
using Abblix.Oidc.Server.Features.ClientInformation;
using Abblix.Oidc.Server.Features.Storages;
using Abblix.Oidc.Server.Features.Tokens;
using Abblix.Oidc.Server.Features.Tokens.Revocation;
using Abblix.Oidc.Server.Features.Tokens.Validation;
using Abblix.Oidc.Server.Model;

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.Jwt;
using Abblix.Oidc.Server.Common;
using Abblix.Oidc.Server.Common.Constants;
using Abblix.Oidc.Server.Endpoints.Token.Interfaces;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -22,7 +22,6 @@

using System.Net.Http.Json;
using Abblix.Jwt;
using Abblix.Utils;
using Microsoft.Extensions.Logging;

namespace Abblix.Oidc.Server.Features.ClientInformation;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,6 @@
// info@abblix.com

using Abblix.Oidc.Server.Common.Configuration;
using Abblix.Utils;
using Microsoft.Extensions.Options;

namespace Abblix.Oidc.Server.Features.Licensing;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -20,8 +20,6 @@
// CONTACT: For license inquiries or permissions, contact Abblix LLP at
// info@abblix.com

using Abblix.Utils;

namespace Abblix.Oidc.Server.Features.Licensing;

/// <summary>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -23,7 +23,6 @@
using Abblix.Jwt;
using Abblix.Oidc.Server.Common;
using Abblix.Oidc.Server.Common.Interfaces;
using Abblix.Utils;

namespace Abblix.Oidc.Server.Features.Tokens.Formatters;

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -24,7 +24,6 @@
using Abblix.Oidc.Server.Common;
using Abblix.Oidc.Server.Common.Interfaces;
using Abblix.Oidc.Server.Features.ClientInformation;
using Abblix.Utils;

namespace Abblix.Oidc.Server.Features.Tokens.Formatters;

Expand Down
1 change: 0 additions & 1 deletion Abblix.Oidc.Server/Model/AuthorizationRequest.cs
Original file line number Diff line number Diff line change
Expand Up @@ -40,7 +40,6 @@ public record AuthorizationRequest
/// </summary>
[JsonPropertyName(Parameters.Scope)]
[JsonConverter(typeof(SpaceSeparatedValuesConverter))]
[AllowedValues(Scopes.OpenId, Scopes.Profile, Scopes.Email, Scopes.Phone, Scopes.Address, Scopes.OfflineAccess)]
public string[] Scope { get; init; } = Array.Empty<string>();

/// <summary>
Expand Down