diff --git a/Abblix.Jwt/JsonWebKey.cs b/Abblix.Jwt/JsonWebKey.cs index 15ae2ebd..1b63afc6 100644 --- a/Abblix.Jwt/JsonWebKey.cs +++ b/Abblix.Jwt/JsonWebKey.cs @@ -34,41 +34,48 @@ namespace Abblix.Jwt; //TODO consider to split this class into specialized subclasses (RSA/EllipticCurve) /// -/// Represents a JSON Web Key (JWK), a data structure that represents a cryptographic key in JSON format. -/// The class supports various key types including RSA and Elliptic Curve, and is capable of representing both public and private keys. +/// Represents a JSON Web Key (JWK), a versatile structure for representing cryptographic keys using JSON. +/// It supports various cryptographic algorithms, including RSA and Elliptic Curve, and can represent both +/// public and private keys. JWKs are crucial for digital signatures, encryption, and ensuring secure +/// communication in web-based protocols. /// -public class JsonWebKey +public record JsonWebKey { /// - /// Key type (kty). Identifies the cryptographic algorithm family used with the key, such as "RSA" or "EC" for Elliptic Curve. + /// Identifies the cryptographic algorithm family used with the key, such as RSA or EC (Elliptic Curve), + /// specifying the key's type and its intended cryptographic use. /// [JsonPropertyName("kty")] [JsonPropertyOrder(1)] public string? KeyType { get; set; } /// - /// Intended use of the key (use). Indicates how the key is meant to be used, such as "sig" for signing or "enc" for encryption. + /// Indicates the intended use of the key, for example, "sig" (signature) for signing operations or + /// "enc" (encryption) for encryption operations, guiding clients on how to use the key appropriately. /// [JsonPropertyName("use")] [JsonPropertyOrder(2)] public string? Usage { get; set; } /// - /// Algorithm (alg). Specifies the algorithm that the key is intended to be used with. + /// Specifies the algorithm intended for use with the key, aligning with JWT and JWA specifications + /// to ensure interoperability and secure key management. /// [JsonPropertyName("alg")] [JsonPropertyOrder(3)] public string? Algorithm { get; set; } /// - /// Key ID (kid). A hint indicating which specific key owned by the signer should be used. + /// A unique identifier for the key, facilitating key selection and management in multi-key environments, + /// enabling clients and servers to reference and utilize the correct key for cryptographic operations. /// [JsonPropertyName("kid")] [JsonPropertyOrder(4)] public string? KeyId { get; set; } /// - /// X.509 Certificate Chain (x5c). Contains a chain of one or more PKIX certificates. + /// Contains a chain of one or more PKIX certificates (RFC 5280), offering a method to associate X.509 + /// certificates with the key for validation and trust chain establishment in secure communications. /// [JsonPropertyName("x5c")] [JsonPropertyOrder(5)] @@ -76,7 +83,8 @@ public class JsonWebKey public byte[][]? Certificates { get; set; } /// - /// X.509 Certificate SHA-1 Thumbprint (x5t). A base64url-encoded SHA-1 thumbprint (a.k.a. digest) of the DER encoding of an X.509 certificate. + /// A base64url-encoded SHA-1 thumbprint of the DER encoding of an X.509 certificate, providing a compact + /// means to associate a certificate with the JWK for verification purposes without transmitting the full certificate. /// [JsonPropertyName("x5t")] [JsonPropertyOrder(5)] @@ -169,4 +177,30 @@ public class JsonWebKey [JsonPropertyOrder(16)] [JsonConverter(typeof(Base64UrlTextEncoderConverter))] public byte[]? FirstCrtCoefficient { get; set; } + + /// + /// Prepares a sanitized version of the JWK that excludes private key information unless explicitly included, + /// suitable for public sharing while preserving the integrity of sensitive data. + /// + /// Whether to include private key data in the sanitized output. + /// + /// A new instance of with or without private key data based on the input parameter. + /// + public JsonWebKey Sanitize(bool includePrivateKeys) + { + return includePrivateKeys switch + { + true when PrivateKey is { Length: > 0} => this, + true => throw new InvalidOperationException($"There is no private key for kid={KeyId}"), + false => this with + { + PrivateKey = null, + FirstCrtCoefficient = null, + FirstPrimeFactor = null, + FirstFactorCrtExponent = null, + SecondPrimeFactor = null, + SecondFactorCrtExponent = null, + } + }; + } } diff --git a/Abblix.Oidc.Server/Common/Configuration/OidcOptions.cs b/Abblix.Oidc.Server/Common/Configuration/OidcOptions.cs index 316a63d8..8130dfba 100644 --- a/Abblix.Oidc.Server/Common/Configuration/OidcOptions.cs +++ b/Abblix.Oidc.Server/Common/Configuration/OidcOptions.cs @@ -27,9 +27,7 @@ // For more information, please refer to the license agreement located at: // https://github.com/Abblix/Oidc.Server/blob/master/README.md -using System.Security.Cryptography.X509Certificates; using Abblix.Jwt; -using Abblix.Oidc.Server.Common.Constants; using Abblix.Oidc.Server.Features.ClientInformation; using Abblix.Oidc.Server.Model; @@ -41,215 +39,122 @@ namespace Abblix.Oidc.Server.Common.Configuration; public record OidcOptions { /// - /// The OIDC discovery options. + /// Configuration options for OIDC discovery. These options control how the OIDC server advertises its capabilities + /// and endpoints to clients through the OIDC Discovery mechanism. Proper configuration ensures that clients can + /// dynamically discover information about the OIDC server, such as URLs for authorization, token, userinfo, and + /// JWKS endpoints, supported scopes, response types, and more. /// public DiscoveryOptions Discovery { get; set; } = new(); /// - /// The issuer identifier to be used in tokens. It is useful for scenarios where the issuer - /// needs to be consistent and predefined, such as environments with multiple hosts. + /// Represents the unique identifier of the OIDC server. + /// It is recommended to use a URL that is controlled by the entity operating the OIDC server, and it should be + /// consistent across different environments to maintain trust with client applications. /// public string? Issuer { get; set; } /// - /// The client configurations supported by the OIDC server. + /// A collection of client configurations supported by this OIDC server. Each object + /// defines the settings and capabilities of a registered client, including client ID, client secrets, + /// redirect URIs, and other OAuth2/OIDC parameters. Proper client configuration is essential for securing client + /// applications and enabling them to interact with the OIDC server according to the OAuth2 and OIDC specifications. /// public IEnumerable Clients { get; set; } = Array.Empty(); /// - /// The URI for account selection during authentication. + /// The URL to a user interface or service that allows users to select an account during the authentication process. + /// This is useful in scenarios where users have multiple accounts and need to choose which one to use for signing in. /// public Uri? AccountSelectionUri { get; set; } /// - /// The URI for obtaining user consent during authentication. + /// The URL to a user interface or service for obtaining user consent during the authentication process. Consent is + /// often required when the client application requests access to user data or when sharing information between + /// different parties. This URI should point to a page or API that can manage consent workflows and communicate + /// the user's decisions back to the OIDC server. /// public Uri? ConsentUri { get; set; } /// - /// The URI for handling interactions during the authentication process. + /// The URL to a user interface or service for handling additional interactions required during the authentication + /// process. This can include multi-factor authentication, user consent, or any custom interaction required by the + /// authentication flow. The OIDC server can redirect users to this URI when additional interaction is needed. /// public Uri? InteractionUri { get; set; } /// - /// The URI for initiating a login process. + /// The URL to initiate the login process. This URI is typically used in scenarios where the OIDC server needs to + /// direct users to a specific login interface or when integrating with external identity providers. Configuring + /// this URI allows the OIDC server to delegate the initial user authentication step to another service or UI. /// public Uri? LoginUri { get; set; } /// - /// The parameter name for authorization request identifier. + /// The name of the parameter used by the OIDC server to pass the authorization request identifier. This parameter + /// name is used in URLs and requests to reference specific authorization requests, especially in advanced features + /// like Pushed Authorization Requests (PAR). Customizing this parameter name can help align with specific client + /// requirements or naming conventions. /// public string RequestUriParameterName { get; set; } = AuthorizationRequest.Parameters.RequestUri; /// - /// Specifies which OIDC endpoints are enabled. + /// Specifies which OIDC endpoints are enabled on the server. This property allows for fine-grained control over + /// the available functionality, enabling or disabling specific endpoints based on the server's role, security + /// considerations, or operational requirements. By default, all endpoints are enabled. /// public OidcEndpoints EnabledEndpoints { get; set; } = OidcEndpoints.All; /// - /// The certificates used for signing tokens. + /// The collection of JSON Web Keys (JWK) used for signing tokens issued by the OIDC server. + /// Signing tokens is a critical security measure that ensures the integrity and authenticity of the tokens. + /// These keys are used to digitally sign ID tokens, access tokens, and other JWTs issued by the server, + /// allowing clients to verify that the tokens have not been tampered with and were indeed issued by this server. + /// It is recommended to rotate these keys periodically to maintain the security of the token signing process. /// - public IReadOnlyCollection SigningCertificates { get; set; } = Array.AsReadOnly(Array.Empty()); + public IReadOnlyCollection SigningKeys { get; set; } = Array.Empty(); /// - /// The options related to the check session cookie. + /// Options related to the check session mechanism in OIDC. This configuration controls how the OIDC server manages + /// session state information, allowing clients to monitor the login session's status. Properly configuring these + /// options ensures that clients can react to session changes (e.g., logout) in a timely and secure manner. /// public CheckSessionCookieOptions CheckSessionCookie { get; set; } = new(); /// - /// The duration of a login session's expiration. + /// The duration after which a login session expires. This setting determines how long a user's authentication + /// session remains valid before requiring re-authentication. Configuring this duration is essential for balancing + /// security concerns with usability, particularly in environments with varying security requirements. /// public TimeSpan LoginSessionExpiresIn { get; set; } = TimeSpan.FromMinutes(10); /// - /// Indicates support for claims parameters in requests. - /// - public bool ClaimsParameterSupported { get; set; } = true; - - /// - /// A list of scopes supported by the service. - /// - public IList ScopesSupported { get; set; } = new List - { - Scopes.OpenId, - Scopes.Profile, - Scopes.Email, - Scopes.Phone, - Scopes.Address, - Scopes.OfflineAccess, - }; - - /// - /// The claims supported by the service. - /// - public IList ClaimsSupported { get; set; } = new List - { - JwtClaimTypes.Subject, - JwtClaimTypes.Email, - JwtClaimTypes.EmailVerified, - JwtClaimTypes.PhoneNumber, - JwtClaimTypes.PhoneNumberVerified, - }; - - /// - /// The response types supported by the service. - /// - public IList ResponseTypesSupported { get; set; } = new List - { - ResponseTypes.Code, - ResponseTypes.Token, - ResponseTypes.IdToken, - string.Join(" ", ResponseTypes.IdToken, ResponseTypes.Token), - string.Join(" ", ResponseTypes.Code, ResponseTypes.IdToken), - string.Join(" ", ResponseTypes.Code, ResponseTypes.Token), - string.Join(" ", ResponseTypes.Code, ResponseTypes.IdToken, ResponseTypes.Token), - }; - - /// - /// The response modes supported by the service. - /// - public IList ResponseModesSupported { get; set; } = new List - { - ResponseModes.FormPost, - ResponseModes.Query, - ResponseModes.Fragment, - }; - - /// - /// A list of algorithms supported for signing ID tokens. - /// - public IList IdTokenSigningAlgorithmValuesSupported { get; set; } = new List - { - SigningAlgorithms.None, - SigningAlgorithms.RS256, - }; - - /// - /// A list of subject types supported by the OIDC service. - /// - public IList SubjectTypesSupported { get; set; } = new List - { - SubjectTypes.Public, - SubjectTypes.Pairwise, - }; - - /// - /// A list of supported methods for code challenge in PKCE (Proof Key for Code Exchange). - /// - public IList CodeChallengeMethodsSupported { get; set; } = new List - { - CodeChallengeMethods.Plain, - CodeChallengeMethods.S256, - }; - - /// - /// Indicates whether the request parameter is supported by the OIDC service. - /// - public bool RequestParameterSupported { get; set; } = true; - - /// - /// A list of prompt values supported by the OIDC service. - /// - public IList PromptValuesSupported { get; set; } = new List - { - Prompts.None, - Prompts.Login, - Prompts.Consent, - Prompts.SelectAccount, - Prompts.Create, - }; - - /// - /// Options for configuring a new client in the OIDC service. + /// Configuration options for registering new clients dynamically in the OIDC server. These options define default + /// values and constraints for new client registrations, facilitating dynamic and secure client onboarding processes. /// public NewClientOptions NewClientOptions { get; init; } = new(); /// - /// A list of algorithms supported for signing UserInfo responses. - /// - public IList UserInfoSigningAlgValuesSupported { get; set; } = new List - { - SigningAlgorithms.None, - SigningAlgorithms.RS256, - }; - - /// - /// A list of algorithms supported for signing request objects. - /// - public IList RequestObjectSigningAlgValuesSupported { get; set; } = new List - { - SigningAlgorithms.None, - SigningAlgorithms.RS256, - }; - - /// - /// The encryption certificates for the OpenID Connect service tokens. + /// The collection of JSON Web Keys (JWK) used for encrypting tokens or sensitive information sent to the clients. + /// Encryption is essential for protecting sensitive data within tokens, especially when tokens are passed through + /// less secure channels or when storing tokens at the client side. These keys are utilized to encrypt ID tokens and, + /// optionally, access tokens when the OIDC server sends them to clients. Clients use the corresponding public keys + /// to decrypt the tokens and access the contained claims. /// - public IList EncryptionCertificates { get; set; } = new List(); + public IReadOnlyCollection EncryptionKeys { get; set; } = Array.Empty(); /// - /// The duration for which a pushed authorization request (PAR) is considered valid. + /// The duration for which a Pushed Authorization Request (PAR) is valid. PAR is a security enhancement that allows + /// clients to pre-register authorization requests directly with the authorization server. This duration specifies + /// the maximum time a pre-registered request is considered valid, balancing the need for security with usability + /// in completing the authorization process. /// - /// - /// This property defines the lifespan of a pushed authorization request. Pushed authorization requests are - /// a security feature in OIDC that allows clients to send authorization requests directly to the authorization - /// server via a backchannel connection, rather than through the user's browser. This duration specifies - /// how long the server should consider the request valid after it has been received. It is important to balance - /// security and usability when configuring this value, ensuring that requests are valid long enough for users - /// to complete the authentication process without leaving too large a window for potential misuse. - /// public TimeSpan PushedAuthorizationRequestExpiresIn { get; set; } = TimeSpan.FromMinutes(1); /// - /// The JWT used for licensing and configuration validation of the OIDC service. + /// A JWT used for licensing and configuration validation of the OIDC service. This token contains claims that the + /// OIDC service uses to validate its configuration, features, and licensing status, ensuring the service operates + /// within its licensed capabilities. Proper validation of this token is crucial for the service's legal and functional + /// compliance. /// - /// - /// This property holds a JSON Web Token (JWT) that the OIDC service uses to validate its configuration and - /// licensing status. The token typically contains claims that the service decodes to determine the features - /// and capabilities that are enabled, based on the licensing agreement. Proper validation of this token - /// is crucial for ensuring that the service operates within the terms of its licensing and has access - /// to the correct set of features. The format and content of this token are determined by the service provider - /// and may include information such as the license expiry date, the licensed feature set and other relevant data. - /// public string? LicenseJwt { get; set; } } diff --git a/Abblix.Oidc.Server/Common/Implementation/ServiceKeysProvider.cs b/Abblix.Oidc.Server/Common/Implementation/OidcOptionsKeysProvider.cs similarity index 86% rename from Abblix.Oidc.Server/Common/Implementation/ServiceKeysProvider.cs rename to Abblix.Oidc.Server/Common/Implementation/OidcOptionsKeysProvider.cs index ddaab61d..cea07923 100644 --- a/Abblix.Oidc.Server/Common/Implementation/ServiceKeysProvider.cs +++ b/Abblix.Oidc.Server/Common/Implementation/OidcOptionsKeysProvider.cs @@ -37,17 +37,16 @@ namespace Abblix.Oidc.Server.Common.Implementation; /// -/// Provides access to JSON Web Keys (JWK) used for encryption and signing JWT tokens, -/// based on the preconfigured X509 certificates defined in OIDC options. +/// Provides access to JSON Web Keys (JWK) used for encryption and signing JWT tokens. /// /// /// This implementation provides keys for encryption and signing purposes by mapping X509 certificates to JWK format. /// It is recommended to implement a dynamic resolution mechanism in production environments /// to enable seamless certificate replacement without the need for service reloading. /// -internal class ServiceKeysProvider : IAuthServiceKeysProvider +internal class OidcOptionsKeysProvider : IAuthServiceKeysProvider { - public ServiceKeysProvider(IOptions options) + public OidcOptionsKeysProvider(IOptions options) { _options = options; } @@ -62,9 +61,8 @@ public ServiceKeysProvider(IOptions options) public IAsyncEnumerable GetEncryptionKeys(bool includePrivateKeys) { var jsonWebKeys = - from cert in _options.Value.EncryptionCertificates - orderby cert.NotAfter descending - select cert.ToJsonWebKey(includePrivateKeys); + from jwk in _options.Value.EncryptionKeys + select jwk.Sanitize(includePrivateKeys); return jsonWebKeys.AsAsync(); } @@ -77,9 +75,8 @@ orderby cert.NotAfter descending public IAsyncEnumerable GetSigningKeys(bool includePrivateKeys) { var jsonWebKeys = - from cert in _options.Value.SigningCertificates - orderby cert.NotAfter descending - select cert.ToJsonWebKey(includePrivateKeys); + from jwk in _options.Value.SigningKeys + select jwk.Sanitize(includePrivateKeys); return jsonWebKeys.AsAsync(); } diff --git a/Abblix.Oidc.Server/Features/ServiceCollectionExtensions.cs b/Abblix.Oidc.Server/Features/ServiceCollectionExtensions.cs index 4573ec9e..f43ac8f2 100644 --- a/Abblix.Oidc.Server/Features/ServiceCollectionExtensions.cs +++ b/Abblix.Oidc.Server/Features/ServiceCollectionExtensions.cs @@ -270,7 +270,7 @@ public static IServiceCollection AddIdentityToken(this IServiceCollection servic public static IServiceCollection AddAuthServiceJwt(this IServiceCollection services) { return services - .AddSingleton() + .AddSingleton() .AddSingleton() .AddSingleton(); diff --git a/Abblix.Oidc.Server/Features/Tokens/Formatters/AuthServiceJwtFormatter.cs b/Abblix.Oidc.Server/Features/Tokens/Formatters/AuthServiceJwtFormatter.cs index af2e35b0..606e076c 100644 --- a/Abblix.Oidc.Server/Features/Tokens/Formatters/AuthServiceJwtFormatter.cs +++ b/Abblix.Oidc.Server/Features/Tokens/Formatters/AuthServiceJwtFormatter.cs @@ -64,7 +64,7 @@ public AuthServiceJwtFormatter( /// public async Task FormatAsync(JsonWebToken token) { - // Select the appropriate signing key based on the JWT's specified algorithm + // Select the appropriate signing key based on the JWT specified algorithm var signingCredentials = await _serviceKeysProvider.GetSigningKeys(true) .FirstByAlgorithmAsync(token.Header.Algorithm);