diff --git a/Abblix.Oidc.Server/Common/Constants/BackchannelTokenDeliveryModes.cs b/Abblix.Oidc.Server/Common/Constants/BackchannelTokenDeliveryModes.cs
new file mode 100644
index 0000000..08781fb
--- /dev/null
+++ b/Abblix.Oidc.Server/Common/Constants/BackchannelTokenDeliveryModes.cs
@@ -0,0 +1,51 @@
+// Abblix OIDC Server Library
+// Copyright (c) Abblix LLP. All rights reserved.
+//
+// DISCLAIMER: This software is provided 'as-is', without any express or implied
+// warranty. Use at your own risk. Abblix LLP is not liable for any damages
+// arising from the use of this software.
+//
+// LICENSE RESTRICTIONS: This code may not be modified, copied, or redistributed
+// in any form outside of the official GitHub repository at:
+// https://github.com/Abblix/OIDC.Server. All development and modifications
+// must occur within the official repository and are managed solely by Abblix LLP.
+//
+// Unauthorized use, modification, or distribution of this software is strictly
+// prohibited and may be subject to legal action.
+//
+// For full licensing terms, please visit:
+//
+// https://oidc.abblix.com/license
+//
+// CONTACT: For license inquiries or permissions, contact Abblix LLP at
+// info@abblix.com
+
+namespace Abblix.Oidc.Server.Common.Constants;
+
+///
+/// Defines the available delivery modes for backchannel token delivery in Client-Initiated Backchannel Authentication
+/// (CIBA). These modes specify how the authentication server communicates the result of the backchannel authentication
+/// process to the client.
+///
+public class BackchannelTokenDeliveryModes
+{
+ ///
+ /// The "poll" mode where the client periodically polls the authorization server to check if the user has been
+ /// authenticated. This method is useful in cases where the client prefers to control the polling interval and
+ /// the process.
+ ///
+ public const string Poll = "poll";
+
+ ///
+ /// The "ping" mode where the authorization server notifies the client via a callback when the user has been
+ /// authenticated. The client still needs to make a subsequent request to retrieve the token.
+ ///
+ public const string Ping = "ping";
+
+ ///
+ /// The "push" mode where the authorization server directly pushes the token to the client once the user has been
+ /// authenticated. This method streamlines the process by delivering the token to the client without the need for
+ /// further requests.
+ ///
+ public const string Push = "push";
+}
diff --git a/Abblix.Oidc.Server/Common/Constants/GrantTypes.cs b/Abblix.Oidc.Server/Common/Constants/GrantTypes.cs
index f39cf18..2f67f57 100644
--- a/Abblix.Oidc.Server/Common/Constants/GrantTypes.cs
+++ b/Abblix.Oidc.Server/Common/Constants/GrantTypes.cs
@@ -40,13 +40,13 @@ public static class GrantTypes
public const string ClientCredentials = "client_credentials";
///
- /// Represents the Refresh Token grant type. Used to obtain a new access token using a refresh token.
- /// Helpful for maintaining user sessions without requiring reauthentication.
+ /// Represents the Refresh Token grant type. Used to get a new access token using a refresh token.
+ /// Helpful for maintaining user sessions without requiring re-authentication.
///
public const string RefreshToken = "refresh_token";
///
- /// Represents the Implicit grant type. Used in single-page applications to obtain access tokens directly
+ /// Represents the Implicit grant type. Used in single-page applications to get access tokens directly
/// from the authorization endpoint. Suitable for browser-based applications.
///
public const string Implicit = "implicit";
@@ -71,7 +71,7 @@ public static class GrantTypes
///
/// Represents the Device Authorization grant type. This grant type is used in scenarios where the client device
- /// lacks a browser or has limited input capabilities, allowing it to obtain user authorization from another device
+ /// lacks a browser or has limited input capabilities, allowing it to get user authorization from another device
/// with better input capabilities. It is particularly useful for devices in the IoT (Internet of Things) sector
/// and smart devices that require user interaction for authorization.
///
diff --git a/Abblix.Oidc.Server/Common/OperationResult.cs b/Abblix.Oidc.Server/Common/Result.cs
similarity index 88%
rename from Abblix.Oidc.Server/Common/OperationResult.cs
rename to Abblix.Oidc.Server/Common/Result.cs
index a75879d..c2a3a6f 100644
--- a/Abblix.Oidc.Server/Common/OperationResult.cs
+++ b/Abblix.Oidc.Server/Common/Result.cs
@@ -29,15 +29,15 @@ namespace Abblix.Oidc.Server.Common;
/// , or result in an error with an error code and description.
///
/// The type of the value returned in case of a successful result.
-public abstract record OperationResult
+public abstract record Result
{
- private OperationResult() { }
+ private Result() { }
///
- /// Represents a successful result containing a value of type .
+ /// Represents a successful result containing a value of specific type.
///
/// The value returned by the successful operation.
- public sealed record Success(T Value) : OperationResult
+ public sealed record Success(T Value) : Result
{
///
/// Returns a string that represents the current object, either the successful value or an error description.
@@ -54,7 +54,7 @@ public sealed record Success(T Value) : OperationResult
///
/// The code representing the type or cause of the error.
/// A human-readable description of the error.
- public sealed record Error(string ErrorCode, string ErrorDescription) : OperationResult
+ public sealed record Error(string ErrorCode, string ErrorDescription) : Result
{
///
/// Returns a string that represents the current object, either the successful value or an error description.
@@ -71,12 +71,12 @@ public override string ToString()
/// Implicitly converts a value of type into a successful result.
///
/// The value to be wrapped as a successful result.
- public static implicit operator OperationResult(T value) => new Success(value);
+ public static implicit operator Result(T value) => new Success(value);
///
/// Implicitly converts an into an result.
///
/// The error response to be wrapped as an error result.
- public static implicit operator OperationResult(ErrorResponse error)
+ public static implicit operator Result(ErrorResponse error)
=> new Error(error.Error, error.ErrorDescription);
}
diff --git a/Abblix.Oidc.Server/Common/StringExtensions.cs b/Abblix.Oidc.Server/Common/StringExtensions.cs
index 63ed9d2..12bfa97 100644
--- a/Abblix.Oidc.Server/Common/StringExtensions.cs
+++ b/Abblix.Oidc.Server/Common/StringExtensions.cs
@@ -32,7 +32,7 @@ internal static class StringExtensions
///
/// The array of strings to check.
/// The flag to search for.
- /// True if the flag is found; otherwise, false.
+ /// True, if the flag is found, otherwise, false.
public static bool HasFlag(this string[]? values, string flag)
=> values != null && values.Contains(flag, StringComparer.OrdinalIgnoreCase);
@@ -56,7 +56,8 @@ public static bool TryParse(this string source, string[] allowedValues, char sep
var result = new List(sourceValues.Length);
foreach (var sourceValue in sourceValues)
{
- var allowedValue = allowedValues.FirstOrDefault(value => string.Equals(value, sourceValue, StringComparison.OrdinalIgnoreCase));
+ var allowedValue = allowedValues.FirstOrDefault(
+ value => string.Equals(value, sourceValue, StringComparison.OrdinalIgnoreCase));
if (allowedValue == null)
{
values = default!;
diff --git a/Abblix.Oidc.Server/Endpoints/Authorization/RequestFetching/RequestObjectFetcher.cs b/Abblix.Oidc.Server/Endpoints/Authorization/RequestFetching/RequestObjectFetcher.cs
index 363ac3c..a6bc8ce 100644
--- a/Abblix.Oidc.Server/Endpoints/Authorization/RequestFetching/RequestObjectFetcher.cs
+++ b/Abblix.Oidc.Server/Endpoints/Authorization/RequestFetching/RequestObjectFetcher.cs
@@ -81,9 +81,9 @@ public async Task FetchAsync(AuthorizationRequest request)
var fetchResult = await FetchAsync(request, request.Request);
return fetchResult switch
{
- OperationResult.Success(var authorizationRequest) => authorizationRequest,
+ Result.Success(var authorizationRequest) => authorizationRequest,
- OperationResult.Error(var error, var description)
+ Result.Error(var error, var description)
=> ErrorFactory.ValidationError(error, description),
_ => throw new UnexpectedTypeException(nameof(fetchResult), fetchResult.GetType()),
diff --git a/Abblix.Oidc.Server/Endpoints/BackChannelAuthentication/BackChannelAuthenticationHandler.cs b/Abblix.Oidc.Server/Endpoints/BackChannelAuthentication/BackChannelAuthenticationHandler.cs
index 87075de..bc0f686 100644
--- a/Abblix.Oidc.Server/Endpoints/BackChannelAuthentication/BackChannelAuthenticationHandler.cs
+++ b/Abblix.Oidc.Server/Endpoints/BackChannelAuthentication/BackChannelAuthenticationHandler.cs
@@ -74,11 +74,11 @@ public async Task HandleAsync(
var fetchResult = await _fetcher.FetchAsync(request);
switch (fetchResult)
{
- case OperationResult.Success(var requestObject):
+ case Result.Success(var requestObject):
request = requestObject;
break;
- case OperationResult.Error(var error, var description):
+ case Result.Error(var error, var description):
return new BackChannelAuthenticationError(error, description);
default:
diff --git a/Abblix.Oidc.Server/Endpoints/BackChannelAuthentication/RequestFetching/CompositeRequestFetcher.cs b/Abblix.Oidc.Server/Endpoints/BackChannelAuthentication/RequestFetching/CompositeRequestFetcher.cs
index 9c6722e..488b263 100644
--- a/Abblix.Oidc.Server/Endpoints/BackChannelAuthentication/RequestFetching/CompositeRequestFetcher.cs
+++ b/Abblix.Oidc.Server/Endpoints/BackChannelAuthentication/RequestFetching/CompositeRequestFetcher.cs
@@ -55,18 +55,18 @@ public CompositeRequestFetcher(IBackChannelAuthenticationRequestFetcher[] fetche
/// The backchannel authentication request to be processed.
/// A that represents the outcome of the fetching process.
/// It could be a success, fault, or an unexpected type error if the result is not handled correctly.
- public async Task> FetchAsync(BackChannelAuthenticationRequest request)
+ public async Task> FetchAsync(BackChannelAuthenticationRequest request)
{
foreach (var fetcher in _fetchers)
{
var result = await fetcher.FetchAsync(request);
switch (result)
{
- case OperationResult.Success(var success):
+ case Result.Success(var success):
request = success;
continue;
- case OperationResult.Error error:
+ case Result.Error error:
return error;
default:
diff --git a/Abblix.Oidc.Server/Endpoints/BackChannelAuthentication/RequestFetching/IBackChannelAuthenticationRequestFetcher.cs b/Abblix.Oidc.Server/Endpoints/BackChannelAuthentication/RequestFetching/IBackChannelAuthenticationRequestFetcher.cs
index 660fbe7..79ea4e8 100644
--- a/Abblix.Oidc.Server/Endpoints/BackChannelAuthentication/RequestFetching/IBackChannelAuthenticationRequestFetcher.cs
+++ b/Abblix.Oidc.Server/Endpoints/BackChannelAuthentication/RequestFetching/IBackChannelAuthenticationRequestFetcher.cs
@@ -39,5 +39,5 @@ public interface IBackChannelAuthenticationRequestFetcher
/// The backchannel authentication request to be fetched and validated.
/// A task that represents the asynchronous operation. The task result contains a
/// indicating whether the fetch was successful or if it resulted in an error.
- Task> FetchAsync(BackChannelAuthenticationRequest request);
+ Task> FetchAsync(BackChannelAuthenticationRequest request);
}
diff --git a/Abblix.Oidc.Server/Endpoints/BackChannelAuthentication/RequestFetching/RequestObjectFetcher.cs b/Abblix.Oidc.Server/Endpoints/BackChannelAuthentication/RequestFetching/RequestObjectFetcher.cs
index 128f105..ff3b01d 100644
--- a/Abblix.Oidc.Server/Endpoints/BackChannelAuthentication/RequestFetching/RequestObjectFetcher.cs
+++ b/Abblix.Oidc.Server/Endpoints/BackChannelAuthentication/RequestFetching/RequestObjectFetcher.cs
@@ -61,7 +61,7 @@ public RequestObjectFetcher(
/// encapsulates the full request details.
///
///
- /// A task representing the asynchronous operation. The task result contains an
+ /// A task representing the asynchronous operation. The task result contains an
/// with either a successfully processed or an error result
/// indicating validation issues.
///
@@ -70,8 +70,8 @@ public RequestObjectFetcher(
/// the backchannel authentication request.
/// If the JWT is valid, it binds the JWT payload to the backchannel authentication request model.
/// If the JWT is invalid, the method logs a warning and returns an error result encapsulated in
- /// an .
+ /// an .
///
- public Task> FetchAsync(BackChannelAuthenticationRequest request)
+ public Task> FetchAsync(BackChannelAuthenticationRequest request)
=> FetchAsync(request, request.Request);
}
diff --git a/Abblix.Oidc.Server/Endpoints/BackChannelAuthentication/Validation/UserIdentityValidator.cs b/Abblix.Oidc.Server/Endpoints/BackChannelAuthentication/Validation/UserIdentityValidator.cs
index 88a4790..d8a4aa1 100644
--- a/Abblix.Oidc.Server/Endpoints/BackChannelAuthentication/Validation/UserIdentityValidator.cs
+++ b/Abblix.Oidc.Server/Endpoints/BackChannelAuthentication/Validation/UserIdentityValidator.cs
@@ -123,11 +123,11 @@ public UserIdentityValidator(
var idTokenResult = await ValidateIdTokenHint(context, request.IdTokenHint);
switch (idTokenResult)
{
- case OperationResult.Success(var idToken):
+ case Result.Success(var idToken):
context.IdToken = idToken;
break;
- case OperationResult.Error(var error, var description):
+ case Result.Error(var error, var description):
return new BackChannelAuthenticationValidationError(error, description);
}
}
@@ -141,10 +141,10 @@ public UserIdentityValidator(
/// The validation context containing the client information.
/// The ID token hint string to be validated.
///
- /// An representing the validation result,
+ /// An representing the validation result,
/// which can either be a successful token or an error.
///
- private async Task> ValidateIdTokenHint(
+ private async Task> ValidateIdTokenHint(
BackChannelAuthenticationValidationContext context,
string idTokenHint)
{
@@ -173,7 +173,7 @@ private async Task> ValidateIdTokenHint(
}
// Helper method to generate an error response for invalid requests
- OperationResult.Error InvalidRequest(string description)
+ Result.Error InvalidRequest(string description)
=> new (ErrorCodes.InvalidRequest, description);
}
}
diff --git a/Abblix.Oidc.Server/Endpoints/Revocation/RevocationRequestValidator.cs b/Abblix.Oidc.Server/Endpoints/Revocation/RevocationRequestValidator.cs
index 6fa1d0c..047ba8c 100644
--- a/Abblix.Oidc.Server/Endpoints/Revocation/RevocationRequestValidator.cs
+++ b/Abblix.Oidc.Server/Endpoints/Revocation/RevocationRequestValidator.cs
@@ -27,7 +27,7 @@
using Abblix.Oidc.Server.Features.ClientAuthentication;
using Abblix.Oidc.Server.Features.Tokens.Validation;
using Abblix.Oidc.Server.Model;
-
+using Microsoft.Extensions.Logging;
namespace Abblix.Oidc.Server.Endpoints.Revocation;
@@ -44,18 +44,26 @@ public class RevocationRequestValidator : IRevocationRequestValidator
/// Initializes a new instance of the class.
/// The constructor sets up the validator with necessary components for client authentication and JWT validation.
///
+ /// Provides logging capabilities to record validation outcomes and errors.
///
- /// The client request authenticator to be used in the validation process.
+ /// The client request authenticator to be used in the validation process. Ensures that the client sending the
+ /// revocation request is authenticated and authorized to revoke tokens.
+ ///
///
- /// The JWT validator to be used for validating the token included in the revocation request.
+ /// The JWT validator to be used for validating the token included in the revocation request. Ensures that
+ /// the token is valid and that it belongs to the client requesting revocation.
+ ///
public RevocationRequestValidator(
+ ILogger logger,
IClientAuthenticator clientAuthenticator,
IAuthServiceJwtValidator jwtValidator)
{
+ _logger = logger;
_clientAuthenticator = clientAuthenticator;
_jwtValidator = jwtValidator;
}
+ private readonly ILogger _logger;
private readonly IClientAuthenticator _clientAuthenticator;
private readonly IAuthServiceJwtValidator _jwtValidator;
@@ -65,17 +73,24 @@ public RevocationRequestValidator(
/// that the token belongs to the authenticated client and is valid as per JWT standards.
///
///
- /// The revocation request to be validated. Contains the token to be revoked and client information.
+ /// The revocation request to be validated. Contains the token to be revoked and client information.
+ ///
/// Additional client request information for contextual validation.
///
/// A representing the asynchronous operation, which upon completion will yield a
- /// .
- /// The result indicates whether the request is valid or contains any errors.
+ /// . The result indicates whether the request is valid or
+ /// contains any errors.
///
+ ///
+ /// This method follows the OAuth 2.0 revocation flow, ensuring that the token being revoked belongs to
+ /// the authenticated client, protecting against cross-client token revocation. In case of validation failure,
+ /// it logs a warning with the specific cause.
+ ///
public async Task ValidateAsync(
RevocationRequest revocationRequest,
ClientRequest clientRequest)
{
+ // Authenticate the client making the revocation request.
var clientInfo = await _clientAuthenticator.TryAuthenticateClientAsync(clientRequest);
if (clientInfo == null)
{
@@ -87,16 +102,21 @@ public async Task ValidateAsync(
var result = await _jwtValidator.ValidateAsync(revocationRequest.Token);
switch (result)
{
+ // If token validation fails, log the error and return an invalid token result.
+ case JwtValidationError error:
+ _logger.LogWarning("The token validation failed: {@Error}", error);
+ return ValidRevocationRequest.InvalidToken(revocationRequest);
+
+ // If the token was issued to a different client, log a warning and return an invalid token result.
case ValidJsonWebToken { Token.Payload.ClientId: var clientId } when clientId != clientInfo.ClientId:
- //TODO maybe log the message: The token was issued to another client?
+ _logger.LogWarning("The token was issued to another client {ClientId}", clientId);
return ValidRevocationRequest.InvalidToken(revocationRequest);
+ // If the token is valid and belongs to the authenticated client, return a valid revocation request.
case ValidJsonWebToken { Token: var token }:
return new ValidRevocationRequest(revocationRequest, token);
- case JwtValidationError: //TODO log error
- return ValidRevocationRequest.InvalidToken(revocationRequest);
-
+ // Unexpected result type, throw an exception to indicate a misconfiguration or unexpected validation outcome.
default:
throw new UnexpectedTypeException(nameof(result), result.GetType());
}
diff --git a/Abblix.Oidc.Server/Features/ClientAuthentication/PrivateKeyJwtAuthenticator.cs b/Abblix.Oidc.Server/Features/ClientAuthentication/PrivateKeyJwtAuthenticator.cs
index 3c52a55..4d6b551 100644
--- a/Abblix.Oidc.Server/Features/ClientAuthentication/PrivateKeyJwtAuthenticator.cs
+++ b/Abblix.Oidc.Server/Features/ClientAuthentication/PrivateKeyJwtAuthenticator.cs
@@ -74,83 +74,86 @@ public IEnumerable ClientAuthenticationMethodsSupported
}
///
- /// Attempts to authenticate a client using the Private Key JWT method by validating the JWT provided in the client request.
+ /// Attempts to authenticate a client using the Private Key JWT method by validating the JWT provided in
+ /// the client request.
///
/// The client request containing the JWT to authenticate.
/// The authenticated , or null if authentication fails.
public async Task TryAuthenticateClientAsync(ClientRequest request)
{
- if (request.ClientAssertionType == ClientAssertionTypes.JwtBearer)
+ if (request.ClientAssertionType != ClientAssertionTypes.JwtBearer)
{
- if (!request.ClientAssertion.HasValue())
- {
+ _logger.LogWarning(
+ $"{ClientAssertionType} is not '{ClientAssertionTypes.JwtBearer}'");
+ return null;
+ }
+
+ if (!request.ClientAssertion.HasValue())
+ {
+ _logger.LogWarning(
+ $"{ClientAssertionType} is '{ClientAssertionTypes.JwtBearer}', but {ClientAssertion} is empty");
+ return null;
+ }
+
+ JwtValidationResult? result;
+ ClientInfo? clientInfo;
+ using (var scope = _serviceProvider.CreateScope())
+ {
+ var tokenValidator = scope.ServiceProvider.GetRequiredService();
+ (result, clientInfo) = await tokenValidator.ValidateAsync(request.ClientAssertion);
+ }
+
+ JsonWebToken token;
+ switch (result, clientInfo)
+ {
+ case (ValidJsonWebToken validToken, { TokenEndpointAuthMethod: ClientAuthenticationMethods.PrivateKeyJwt }):
+ token = validToken.Token;
+ break;
+
+ case (ValidJsonWebToken, clientInfo: not null):
_logger.LogWarning(
- $"{ClientAssertionType} is '{ClientAssertionTypes.JwtBearer}', but {ClientAssertion} is empty");
- return null;
- }
-
- JwtValidationResult? result;
- ClientInfo? clientInfo;
- using (var scope = _serviceProvider.CreateScope())
- {
- var tokenValidator = scope.ServiceProvider.GetRequiredService();
- (result, clientInfo) = await tokenValidator.ValidateAsync(request.ClientAssertion);
- }
-
- JsonWebToken token;
- switch (result, clientInfo)
- {
- case (ValidJsonWebToken validToken, { TokenEndpointAuthMethod: ClientAuthenticationMethods.PrivateKeyJwt }):
- token = validToken.Token;
- break;
-
- case (ValidJsonWebToken, clientInfo: not null):
- _logger.LogWarning(
- "The authentication method is not allowed for the client {@ClientId}",
- clientInfo.ClientId);
- return null;
-
- case (ValidJsonWebToken, clientInfo: null):
- _logger.LogWarning("Something went wrong, token cannot be validated without client specified");
- return null;
-
- case (JwtValidationError error, _):
- _logger.LogWarning("Invalid PrivateKeyJwt: {@Error}", error);
- return null;
-
- default:
- throw new UnexpectedTypeException(nameof(result), result.GetType());
- }
-
- string? subject;
- try
- {
- subject = token.Payload.Subject;
- }
- catch (InvalidOperationException ex)
- {
- _logger.LogWarning("Invalid PrivateKeyJwt: {Message}", ex.Message);
+ "The authentication method is not allowed for the client {@ClientId}",
+ clientInfo.ClientId);
return null;
- }
- var issuer = token.Payload.Issuer;
- if (issuer == null || subject == null || issuer != subject)
- {
- _logger.LogWarning(
- "Invalid PrivateKeyJwt: iss is \'{Issuer}\', but sub is {Subject}",
- issuer, subject);
+ case (ValidJsonWebToken, clientInfo: null):
+ _logger.LogWarning("Something went wrong, token cannot be validated without client specified");
+ return null;
+ case (JwtValidationError error, _):
+ _logger.LogWarning("Invalid PrivateKeyJwt: {@Error}", error);
return null;
- }
- if (token is { Payload: { JwtId: { } jwtId, ExpiresAt: { } expiresAt } })
- {
- await _tokenRegistry.SetStatusAsync(jwtId, JsonWebTokenStatus.Used, expiresAt);
- }
+ default:
+ throw new UnexpectedTypeException(nameof(result), result.GetType());
+ }
+
+ string? subject;
+ try
+ {
+ subject = token.Payload.Subject;
+ }
+ catch (InvalidOperationException ex)
+ {
+ _logger.LogWarning("Invalid PrivateKeyJwt: {Message}", ex.Message);
+ return null;
+ }
+
+ var issuer = token.Payload.Issuer;
+ if (issuer == null || subject == null || issuer != subject)
+ {
+ _logger.LogWarning(
+ "Invalid PrivateKeyJwt: iss is \'{Issuer}\', but sub is {Subject}",
+ issuer, subject);
+
+ return null;
+ }
- return clientInfo;
+ if (token is { Payload: { JwtId: { } jwtId, ExpiresAt: { } expiresAt } })
+ {
+ await _tokenRegistry.SetStatusAsync(jwtId, JsonWebTokenStatus.Used, expiresAt);
}
- return null;
+ return clientInfo;
}
}
diff --git a/Abblix.Oidc.Server/Features/RequestObject/RequestObjectFetcherBase.cs b/Abblix.Oidc.Server/Features/RequestObject/RequestObjectFetcherBase.cs
index d9ddfcb..a3fd374 100644
--- a/Abblix.Oidc.Server/Features/RequestObject/RequestObjectFetcherBase.cs
+++ b/Abblix.Oidc.Server/Features/RequestObject/RequestObjectFetcherBase.cs
@@ -66,14 +66,14 @@ protected RequestObjectFetcherBase(
/// The initial request model to bind the JWT payload to.
/// The JWT contained within the request, if any.
///
- /// A task representing the asynchronous operation. The task result contains an
+ /// A task representing the asynchronous operation. The task result contains an
/// which either represents a successfully processed request or an error indicating issues with the JWT validation.
///
///
/// This method is used to decode and validate the JWT contained in the request. If the JWT is valid, the payload
/// is bound to the request model. If the JWT is invalid, an error is returned and logged.
///
- protected async Task> FetchAsync(T request, string? requestObject)
+ protected async Task> FetchAsync(T request, string? requestObject)
where T : class
{
if (!requestObject.HasValue())
@@ -106,7 +106,7 @@ protected async Task> FetchAsync(T request, string? reques
throw new UnexpectedTypeException(nameof(result), result.GetType());
}
- static OperationResult.Error InvalidRequestObject(string description)
+ static Result.Error InvalidRequestObject(string description)
=> new(ErrorCodes.InvalidRequestObject, description);
}
}
diff --git a/Abblix.Oidc.Server/Model/ClientRegistrationRequest.cs b/Abblix.Oidc.Server/Model/ClientRegistrationRequest.cs
index 0d848cd..91832f2 100644
--- a/Abblix.Oidc.Server/Model/ClientRegistrationRequest.cs
+++ b/Abblix.Oidc.Server/Model/ClientRegistrationRequest.cs
@@ -38,7 +38,7 @@ namespace Abblix.Oidc.Server.Model;
public record ClientRegistrationRequest
{
///
- /// Array of redirection URIs for the OP to redirect the End-User after obtaining authorization.
+ /// Array of redirection URIs for the OP to redirect the End-User after getting authorization.
///
[JsonPropertyName(Parameters.RedirectUris)]
[Required]
@@ -312,8 +312,9 @@ public record ClientRegistrationRequest
/// true if the front-channel logout requires a session identifier; otherwise, false.
///
///
- /// This property corresponds to the 'frontchannel_logout_session_required' parameter in the OpenID Connect specification.
- /// When set to true, it indicates that the client requires a session identifier to be sent with front-channel logout requests.
+ /// This property corresponds to the 'frontchannel_logout_session_required' parameter in the OpenID Connect
+ /// specification. When set to true, it indicates that the client requires a session identifier
+ /// to be sent with front-channel logout requests.
/// This is typically used to facilitate logout across multiple sessions or devices.
///
[JsonPropertyName(Parameters.FrontChannelLogoutSessionRequired)]
diff --git a/Abblix.Oidc.Server/Model/ConfigurationResponse.cs b/Abblix.Oidc.Server/Model/ConfigurationResponse.cs
index b96339f..8c6f28d 100644
--- a/Abblix.Oidc.Server/Model/ConfigurationResponse.cs
+++ b/Abblix.Oidc.Server/Model/ConfigurationResponse.cs
@@ -33,7 +33,9 @@ namespace Abblix.Oidc.Server.Model;
public record ConfigurationResponse
{
///
- /// Nested class containing string constants for JSON property names.
+ /// Nested class containing string constants for JSON property names used in the configuration response.
+ /// These names map directly to the fields returned by the OpenID Connect discovery document, ensuring
+ /// proper serialization and deserialization of configuration data.
///
public static class Parameters
{
@@ -68,6 +70,10 @@ public static class Parameters
public const string PushedAuthorizationRequestEndpoint = "pushed_authorization_request_endpoint";
public const string RequirePushedAuthorizationRequests = "require_pushed_authorization_requests";
public const string RequireSignedRequestObject = "require_signed_request_object";
+ public const string BackchannelTokenDeliveryModesSupported = "backchannel_token_delivery_modes_supported";
+ public const string BackchannelAuthenticationEndpoint = "backchannel_authentication_endpoint";
+ public const string BackchannelAuthenticationRequestSigningAlgValuesSupported = "backchannel_authentication_request_signing_alg_values_supported";
+ public const string BackchannelUserCodeParameterSupported = "backchannel_user_code_parameter_supported";
}
///
@@ -133,7 +139,8 @@ public static class Parameters
public Uri? RegistrationEndpoint { init; get; }
///
- /// The URL for the Pushed Authorization Request endpoint, which allows clients to pre-register authorization requests.
+ /// The URL for the Pushed Authorization Request endpoint, which allows clients to pre-register authorization
+ /// requests.
///
[JsonPropertyName(Parameters.PushedAuthorizationRequestEndpoint)]
public Uri? PushedAuthorizationRequestEndpoint { get; set; }
@@ -277,5 +284,30 @@ public static class Parameters
/// integrity of request objects received by the provider.
///
[JsonPropertyName(Parameters.RequireSignedRequestObject)]
- public bool RequireSignedRequestObject { init; get; } //TODO use it!
+ public bool? RequireSignedRequestObject { init; get; } //TODO use it!
+
+ ///
+ /// Lists the supported backchannel token delivery modes for client-initiated backchannel authentication.
+ ///
+ [JsonPropertyName(Parameters.BackchannelTokenDeliveryModesSupported)]
+ public IEnumerable? BackchannelTokenDeliveryModesSupported { get; init; }
+
+ ///
+ /// The backchannel authentication endpoint for initiating CIBA (Client-Initiated Backchannel Authentication)
+ /// requests.
+ ///
+ [JsonPropertyName(Parameters.BackchannelAuthenticationEndpoint)]
+ public Uri? BackchannelAuthenticationEndpoint { get; init; }
+
+ ///
+ /// Lists the supported signing algorithms for backchannel authentication requests.
+ ///
+ [JsonPropertyName(Parameters.BackchannelAuthenticationRequestSigningAlgValuesSupported)]
+ public IEnumerable? BackchannelAuthenticationRequestSigningAlgValuesSupported { get; init; }
+
+ ///
+ /// Indicates whether the OpenID Provider supports the backchannel user code parameter for CIBA.
+ ///
+ [JsonPropertyName(Parameters.BackchannelUserCodeParameterSupported)]
+ public bool? BackchannelUserCodeParameterSupported { get; init; }
}
diff --git a/Abblix.Oidc.Server/Model/ErrorResponse.cs b/Abblix.Oidc.Server/Model/ErrorResponse.cs
index 089da54..8a42f56 100644
--- a/Abblix.Oidc.Server/Model/ErrorResponse.cs
+++ b/Abblix.Oidc.Server/Model/ErrorResponse.cs
@@ -27,7 +27,7 @@ namespace Abblix.Oidc.Server.Model;
///
/// Represents a standardized error response, commonly used in web APIs and OAuth2/OpenID Connect protocols.
///
-public record ErrorResponse(string Error, string ErrorDescription)
+public readonly record struct ErrorResponse(string Error, string ErrorDescription)
{
///
/// The error code representing the specific type of error encountered.