diff --git a/Abblix.Oidc.Server.Mvc/Controllers/DiscoveryController.cs b/Abblix.Oidc.Server.Mvc/Controllers/DiscoveryController.cs
index 0770c9fc..5c3d2664 100644
--- a/Abblix.Oidc.Server.Mvc/Controllers/DiscoveryController.cs
+++ b/Abblix.Oidc.Server.Mvc/Controllers/DiscoveryController.cs
@@ -39,6 +39,7 @@
using Abblix.Oidc.Server.Features.Issuer;
using Abblix.Oidc.Server.Features.Licensing;
using Abblix.Oidc.Server.Features.LogoutNotification;
+using Abblix.Oidc.Server.Features.UserInfo;
using Abblix.Oidc.Server.Model;
using Abblix.Utils;
using Microsoft.AspNetCore.Http;
diff --git a/Abblix.Oidc.Server/Endpoints/UserInfo/UserInfoRequestProcessor.cs b/Abblix.Oidc.Server/Endpoints/UserInfo/UserInfoRequestProcessor.cs
index 9bef1876..ac297cca 100644
--- a/Abblix.Oidc.Server/Endpoints/UserInfo/UserInfoRequestProcessor.cs
+++ b/Abblix.Oidc.Server/Endpoints/UserInfo/UserInfoRequestProcessor.cs
@@ -27,71 +27,60 @@
// For more information, please refer to the license agreement located at:
// https://github.com/Abblix/Oidc.Server/blob/master/README.md
-using Abblix.Jwt;
using Abblix.Oidc.Server.Common.Constants;
-using Abblix.Oidc.Server.Common.Interfaces;
using Abblix.Oidc.Server.Endpoints.UserInfo.Interfaces;
using Abblix.Oidc.Server.Features.Issuer;
using Abblix.Oidc.Server.Features.Licensing;
+using Abblix.Oidc.Server.Features.UserInfo;
using UserInfoResponse = Abblix.Oidc.Server.Endpoints.UserInfo.Interfaces.UserInfoResponse;
namespace Abblix.Oidc.Server.Endpoints.UserInfo;
///
-/// Processes user information requests, retrieving and formatting user information based on the provided request.
-/// This class plays a crucial role in handling requests to the UserInfo endpoint, ensuring that the returned
-/// user information adheres to the requested scopes and the OAuth 2.0 and OpenID Connect standards.
+/// Processes user information requests by retrieving and formatting user information based on the provided request.
+/// This class is integral in handling requests to the UserInfo endpoint, ensuring that the returned user information
+/// adheres to requested scopes and complies with OAuth 2.0 and OpenID Connect standards.
///
internal class UserInfoRequestProcessor : IUserInfoRequestProcessor
{
///
/// Initializes a new instance of the class.
///
- /// Provider for user information based on JWT claims. This component is responsible
- /// for fetching user-related data that can be returned to the client.
- /// Provider for determining which claims to include in the response based on the
- /// authorization context and requested scopes. This ensures that only the claims the client is authorized to receive
- /// are included.
- /// Converter for transforming subject identifiers (sub claims) based on client
- /// requirements, supporting privacy and client-specific identifier formats.
- /// Provider for the issuer URL, used in generating fully qualified claim names and ensuring
- /// consistency in the issuer claim across responses.
- public UserInfoRequestProcessor(
- IUserInfoProvider userInfoProvider,
- IScopeClaimsProvider scopeClaimsProvider,
- ISubjectTypeConverter subjectTypeConverter,
- IIssuerProvider issuerProvider)
+ /// Provider for the issuer URL, which is essential for generating fully qualified
+ /// claim names and ensuring consistency in the 'iss' claim across responses.
+ /// Provider for user claims based on JWT claims.
+ /// This component fetches user-related data that can be returned to the client, tailored to the client's
+ /// authorization context and scope.
+ public UserInfoRequestProcessor(IIssuerProvider issuerProvider, IUserClaimsProvider userClaimsProvider)
{
- _userInfoProvider = userInfoProvider;
- _scopeClaimsProvider = scopeClaimsProvider;
- _subjectTypeConverter = subjectTypeConverter;
_issuerProvider = issuerProvider;
+ _userClaimsProvider = userClaimsProvider;
}
- private readonly IUserInfoProvider _userInfoProvider;
- private readonly IScopeClaimsProvider _scopeClaimsProvider;
- private readonly ISubjectTypeConverter _subjectTypeConverter;
private readonly IIssuerProvider _issuerProvider;
+ private readonly IUserClaimsProvider _userClaimsProvider;
///
- /// Asynchronously processes a valid user information request and returns a response with the requested user information.
+ /// Asynchronously processes a valid user information request and returns a structured response containing
+ /// the requested user information.
///
- /// The valid user info request to process.
+ /// The valid user information request containing the authentication session,
+ /// authorization context and client information necessary to determine the scope and specifics of
+ /// the requested claims.
/// A representing the asynchronous operation,
- /// which upon completion will yield a .
+ /// which upon completion will yield a encapsulating either the user's claims
+ /// or an error response.
public async Task ProcessAsync(ValidUserInfoRequest request)
{
- var claimNames = _scopeClaimsProvider.GetRequestedClaims(
+ var userInfo = await _userClaimsProvider.GetUserClaimsAsync(
+ request.AuthSession,
request.AuthContext.Scope,
- request.AuthContext.RequestedClaims?.UserInfo);
+ request.AuthContext.RequestedClaims?.UserInfo,
+ request.ClientInfo);
- var userInfo = await _userInfoProvider.GetUserInfoAsync(request.AuthSession.Subject, claimNames);
if (userInfo == null)
- return new UserInfoErrorResponse(ErrorCodes.InvalidGrant, "The user is not found");
-
- var subject = _subjectTypeConverter.Convert(request.AuthSession.Subject, request.ClientInfo);
- userInfo.SetProperty(JwtClaimTypes.Subject, subject);
+ return new UserInfoErrorResponse(ErrorCodes.InvalidGrant, "The user claims aren't found");
var issuer = LicenseChecker.CheckIssuer(_issuerProvider.GetIssuer());
return new UserInfoFoundResponse(userInfo, request.ClientInfo, issuer);
diff --git a/Abblix.Oidc.Server/Features/ServiceCollectionExtensions.cs b/Abblix.Oidc.Server/Features/ServiceCollectionExtensions.cs
index d4cabd6e..eb7734b2 100644
--- a/Abblix.Oidc.Server/Features/ServiceCollectionExtensions.cs
+++ b/Abblix.Oidc.Server/Features/ServiceCollectionExtensions.cs
@@ -47,6 +47,7 @@
using Abblix.Oidc.Server.Features.Tokens.Formatters;
using Abblix.Oidc.Server.Features.Tokens.Revocation;
using Abblix.Oidc.Server.Features.Tokens.Validation;
+using Abblix.Oidc.Server.Features.UserInfo;
using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.DependencyInjection.Extensions;
using Microsoft.Extensions.Options;
@@ -103,8 +104,6 @@ public static IServiceCollection AddCommonServices(this IServiceCollection servi
services.TryAddSingleton();
services.TryAddSingleton(TimeProvider.System);
services.TryAddSingleton();
- services.TryAddSingleton();
- services.TryAddSingleton();
services.TryAddSingleton();
services.TryAddSingleton();
return services.AddJsonWebTokens();
@@ -353,14 +352,37 @@ public static IServiceCollection AddLicense(this IServiceCollection services, st
}
///
- /// Adds singleton services related to storage mechanisms to the specified .
+ /// Registers services for various storage functionalities related to the OAuth 2.0 and OpenID Connect flows within
+ /// the application. This method configures essential storage services that manage authorization codes and
+ /// authorization requests, ensuring their persistence and accessibility across the application.
///
- /// The to add services to.
- /// The so that additional calls can be chained.
+ /// The to which the storage services will be added.
+ /// This collection is crucial for configuring dependency injection in ASP.NET Core applications, allowing services
+ /// to be added, managed, and retrieved throughout the application lifecycle.
+ /// The modified after adding the storage services, permitting additional
+ /// configurations to be chained.
public static IServiceCollection AddStorages(this IServiceCollection services)
{
services.TryAddSingleton();
services.TryAddSingleton();
return services;
}
+
+ ///
+ /// Registers services related to user claims management into the provided .
+ /// This method sets up essential services required for processing and handling user claims based on authentication
+ /// sessions and authorization requests, facilitating the integration of user-specific data into tokens or responses.
+ ///
+ /// The to which the user claims provider services will be
+ /// added. This collection is a mechanism for adding and retrieving dependencies in .NET applications, often used
+ /// to configure dependency injection in ASP.NET Core applications.
+ /// The updated after adding the services, allowing for further
+ /// modifications and additions to be chained.
+ public static IServiceCollection AddUserInfo(this IServiceCollection services)
+ {
+ services.TryAddSingleton();
+ services.TryAddSingleton();
+ services.TryAddSingleton();
+ return services;
+ }
}
diff --git a/Abblix.Oidc.Server/Features/Tokens/IIdentityTokenService.cs b/Abblix.Oidc.Server/Features/Tokens/IIdentityTokenService.cs
index 82a5bb52..11a53d1b 100644
--- a/Abblix.Oidc.Server/Features/Tokens/IIdentityTokenService.cs
+++ b/Abblix.Oidc.Server/Features/Tokens/IIdentityTokenService.cs
@@ -67,7 +67,7 @@ public interface IIdentityTokenService
/// Identity tokens generated by this service adhere to the OpenID Connect standard, ensuring that they can be used
/// reliably in identity assertions across different clients and services that support OpenID Connect.
///
- Task CreateIdentityTokenAsync(
+ Task CreateIdentityTokenAsync(
AuthSession authSession,
AuthorizationContext authContext,
ClientInfo clientInfo,
diff --git a/Abblix.Oidc.Server/Features/Tokens/IdentityTokenService.cs b/Abblix.Oidc.Server/Features/Tokens/IdentityTokenService.cs
index a66240c1..533a57fe 100644
--- a/Abblix.Oidc.Server/Features/Tokens/IdentityTokenService.cs
+++ b/Abblix.Oidc.Server/Features/Tokens/IdentityTokenService.cs
@@ -32,46 +32,51 @@
using Abblix.Jwt;
using Abblix.Oidc.Server.Common;
using Abblix.Oidc.Server.Common.Constants;
-using Abblix.Oidc.Server.Common.Interfaces;
-using Abblix.Oidc.Server.Endpoints.UserInfo.Interfaces;
using Abblix.Oidc.Server.Features.ClientInformation;
using Abblix.Oidc.Server.Features.Issuer;
using Abblix.Oidc.Server.Features.Licensing;
using Abblix.Oidc.Server.Features.Tokens.Formatters;
using Abblix.Oidc.Server.Features.UserAuthentication;
+using Abblix.Oidc.Server.Features.UserInfo;
using Abblix.Utils;
namespace Abblix.Oidc.Server.Features.Tokens;
///
/// Facilitates the creation and management of identity tokens as part of the OpenID Connect authentication flow.
-/// This service assembles identity tokens that encapsulate authenticated user identity, aligning with OpenID Connect
-/// specifications.
+/// This service constructs identity tokens that encapsulate the authenticated user's identity, adhering to
+/// OpenID Connect specifications. It integrates additional security by incorporating claims for token integrity
+/// verification.
///
internal class IdentityTokenService : IIdentityTokenService
{
+ ///
+ /// Initializes a new instance of the class, setting up the necessary components
+ /// for identity token creation.
+ ///
+ /// Provides the issuer URL, used in the 'iss' claim of the identity token.
+ /// Provides the current UTC time, used to set the issued and expiration times of the identity
+ /// token.
+ /// Handles the formatting and signing of the JSON Web Token, ensuring it meets
+ /// the security requirements for transmission.
+ /// Retrieves user-specific claims to be embedded in the identity token,
+ /// based on the authentication session and client's requested scopes and claims.
public IdentityTokenService(
IIssuerProvider issuerProvider,
TimeProvider clock,
- IUserInfoProvider userInfoProvider,
- IScopeClaimsProvider scopeClaimsProvider,
- ISubjectTypeConverter subjectTypeConverter,
- IClientJwtFormatter jwtFormatter)
+ IClientJwtFormatter jwtFormatter,
+ IUserClaimsProvider userClaimsProvider)
{
_issuerProvider = issuerProvider;
_clock = clock;
- _userInfoProvider = userInfoProvider;
- _scopeClaimsProvider = scopeClaimsProvider;
- _subjectTypeConverter = subjectTypeConverter;
_jwtFormatter = jwtFormatter;
+ _userClaimsProvider = userClaimsProvider;
}
private readonly IIssuerProvider _issuerProvider;
private readonly TimeProvider _clock;
- private readonly IUserInfoProvider _userInfoProvider;
- private readonly IScopeClaimsProvider _scopeClaimsProvider;
- private readonly ISubjectTypeConverter _subjectTypeConverter;
private readonly IClientJwtFormatter _jwtFormatter;
+ private readonly IUserClaimsProvider _userClaimsProvider;
///
/// Generates an identity token encapsulating the user's authenticated session, optionally embedding claims based on
@@ -93,7 +98,7 @@ public IdentityTokenService(
/// user identification across services. It explicitly handles `c_hash` and `at_hash` creation, providing additional
/// security checks for token integrity.
///
- public async Task CreateIdentityTokenAsync(
+ public async Task CreateIdentityTokenAsync(
AuthSession authSession,
AuthorizationContext authContext,
ClientInfo clientInfo,
@@ -112,15 +117,14 @@ public async Task CreateIdentityTokenAsync(
scope = scope.Except(new[] { Scopes.Profile, Scopes.Email, Scopes.Address }).ToArray();
}
- var claimNames = _scopeClaimsProvider.GetRequestedClaims(
+ var userInfo = await _userClaimsProvider.GetUserClaimsAsync(
+ authSession,
scope,
- authContext.RequestedClaims?.IdToken);
+ authContext.RequestedClaims?.IdToken,
+ clientInfo);
- var userInfo = await _userInfoProvider.GetUserInfoAsync(authSession.Subject, claimNames);
if (userInfo == null)
- {
- throw new InvalidOperationException("The user claims were not found by subject value");
- }
+ return null;
var issuedAt = _clock.GetUtcNow();
@@ -137,7 +141,6 @@ public async Task CreateIdentityTokenAsync(
ExpiresAt = issuedAt + clientInfo.IdentityTokenExpiresIn,
Issuer = LicenseChecker.CheckIssuer(_issuerProvider.GetIssuer()),
- Subject = _subjectTypeConverter.Convert(authSession.Subject, clientInfo),
SessionId = authSession.SessionId,
AuthenticationTime = authSession.AuthenticationTime,
[JwtClaimTypes.AuthContextClassRef] = authSession.AuthContextClassRef,
diff --git a/Abblix.Oidc.Server/Features/Tokens/LogoutTokenService.cs b/Abblix.Oidc.Server/Features/Tokens/LogoutTokenService.cs
index 9abd23e9..902c942d 100644
--- a/Abblix.Oidc.Server/Features/Tokens/LogoutTokenService.cs
+++ b/Abblix.Oidc.Server/Features/Tokens/LogoutTokenService.cs
@@ -34,6 +34,7 @@
using Abblix.Oidc.Server.Features.ClientInformation;
using Abblix.Oidc.Server.Features.LogoutNotification;
using Abblix.Oidc.Server.Features.Tokens.Formatters;
+using Abblix.Oidc.Server.Features.UserInfo;
using Abblix.Utils;
using Microsoft.Extensions.Logging;
diff --git a/Abblix.Oidc.Server/Common/Interfaces/IScopeClaimsProvider.cs b/Abblix.Oidc.Server/Features/UserInfo/IScopeClaimsProvider.cs
similarity index 52%
rename from Abblix.Oidc.Server/Common/Interfaces/IScopeClaimsProvider.cs
rename to Abblix.Oidc.Server/Features/UserInfo/IScopeClaimsProvider.cs
index 0317ac54..25c92b93 100644
--- a/Abblix.Oidc.Server/Common/Interfaces/IScopeClaimsProvider.cs
+++ b/Abblix.Oidc.Server/Features/UserInfo/IScopeClaimsProvider.cs
@@ -27,30 +27,38 @@
// For more information, please refer to the license agreement located at:
// https://github.com/Abblix/Oidc.Server/blob/master/README.md
-using Abblix.Oidc.Server.Model;
-
-namespace Abblix.Oidc.Server.Common.Interfaces;
+namespace Abblix.Oidc.Server.Features.UserInfo;
///
-/// Represents a service responsible for mapping requested claims based on scopes and requested claim details.
+/// Defines a service responsible for determining the claims associated with specific OAuth 2.0 and OpenID Connect scopes.
+/// This interface facilitates the mapping of requested scopes to their corresponding claims, enabling effective claims
+/// management based on the authorization policies and client request parameters.
///
public interface IScopeClaimsProvider
{
///
- /// The requested claims based on scopes and requested claim details.
+ /// Retrieves the set of claim names associated with the requested scopes and any additional claim details.
+ /// This method allows for dynamic claim resolution based on the authorization request, supporting customization
+ /// of claims returned in tokens or user info responses.
///
- /// The requested scopes.
- /// The requested claim details.
- /// An IEnumerable of claim names.
- IEnumerable GetRequestedClaims(IEnumerable scopes, Dictionary? requestedClaims);
+ /// An enumerable of strings representing the requested scopes. Each scope can be associated
+ /// with one or multiple claims as defined by the implementation.
+ /// An optional collection of additional claims requested, which may not necessarily
+ /// be tied to specific scopes but are required by the client.
+ /// An IEnumerable of strings, each representing a claim name that should be included based on the
+ /// requested scopes and additional claims.
+ IEnumerable GetRequestedClaims(IEnumerable scopes, IEnumerable? requestedClaims);
///
- /// A collection of all the scopes supported by this provider.
+ /// Provides a collection of all the scopes that are recognized and supported by this provider.
+ /// This property can be used to validate scope requests or to generate metadata for discovery documents.
///
IEnumerable ScopesSupported { get; }
///
- /// A collection of all the claims that can be provided by this provider.
+ /// Provides a collection of all the claims that this provider can handle.
+ /// These claims represent the total set of data points that can be requested through various scopes
+ /// and are used for constructing tokens and user information responses.
///
IEnumerable ClaimsSupported { get; }
}
diff --git a/Abblix.Oidc.Server/Common/Interfaces/ISubjectTypeConverter.cs b/Abblix.Oidc.Server/Features/UserInfo/ISubjectTypeConverter.cs
similarity index 98%
rename from Abblix.Oidc.Server/Common/Interfaces/ISubjectTypeConverter.cs
rename to Abblix.Oidc.Server/Features/UserInfo/ISubjectTypeConverter.cs
index fc2cce4b..0b7be4de 100644
--- a/Abblix.Oidc.Server/Common/Interfaces/ISubjectTypeConverter.cs
+++ b/Abblix.Oidc.Server/Features/UserInfo/ISubjectTypeConverter.cs
@@ -29,7 +29,7 @@
using Abblix.Oidc.Server.Features.ClientInformation;
-namespace Abblix.Oidc.Server.Common.Interfaces;
+namespace Abblix.Oidc.Server.Features.UserInfo;
///
/// Defines the interface for a service that converts user subject identifiers according to the client's specified
diff --git a/Abblix.Oidc.Server/Features/UserInfo/IUserClaimsProvider.cs b/Abblix.Oidc.Server/Features/UserInfo/IUserClaimsProvider.cs
new file mode 100644
index 00000000..a1ce7946
--- /dev/null
+++ b/Abblix.Oidc.Server/Features/UserInfo/IUserClaimsProvider.cs
@@ -0,0 +1,67 @@
+// Abblix OpenID Connect Server Library
+// Copyright (c) 2024 by Abblix LLP
+//
+// This software is provided 'as-is', without any express or implied warranty. In no
+// event will the authors be held liable for any damages arising from the use of this
+// software.
+//
+// Permitted Use: This software is open for use and extension by non-profit,
+// educational and community projects under the condition that it remains unmodified
+// and used in its entirety through official Nuget packages. Any unauthorized
+// modification, forking of the whole repository, or altering individual files is
+// strictly prohibited to ensure development occurs solely within the official Abblix LLP
+// repository.
+//
+// Prohibited Actions: Redistribution, modification, incorporation of this software or
+// any part thereof into other products, and creation of derivative works are not
+// permitted without obtaining a commercial license from Abblix LLP.
+//
+// Commercial Use: A separate license is required for commercial use, including
+// functionalities extended beyond the original software. For information on obtaining
+// a commercial license, please contact Abblix LLP.
+//
+// Enforcement: Unauthorized redistribution, modification, or use of this software in
+// other projects or products is strictly prohibited without prior written permission
+// from the copyright holder. Violations may be subject to legal action.
+//
+// For more information, please refer to the license agreement located at:
+// https://github.com/Abblix/Oidc.Server/blob/master/README.md
+
+using System.Text.Json.Nodes;
+using Abblix.Oidc.Server.Features.ClientInformation;
+using Abblix.Oidc.Server.Features.UserAuthentication;
+using Abblix.Oidc.Server.Model;
+
+namespace Abblix.Oidc.Server.Features.UserInfo;
+
+///
+/// Defines an interface for retrieving user-specific claims based on authentication sessions and requested claims.
+/// This interface plays a crucial role in authentication flows, where it extracts and formats user data for inclusion
+/// in tokens or other authorization responses, ensuring compliance with specified scopes and claim requests.
+///
+public interface IUserClaimsProvider
+{
+ ///
+ /// Asynchronously retrieves structured user claims based on the provided authentication session, requested scopes,
+ /// additional claim details, and client information. This method is crucial for generating claims that are to be
+ /// embedded in identity tokens or provided through user info endpoints, allowing for a personalized and secure user
+ /// experience based on the authenticated session and application requirements.
+ ///
+ /// The authentication session which includes details about the user's authentication
+ /// state and may affect the resultant claims.
+ /// A collection of scopes indicating which categories of claims are requested.
+ /// Each scope can correlate to multiple claims, influencing the granularity and type of data returned.
+ /// Additional details about specific claims requested, often providing finer control
+ /// over the claims’ properties such as essentiality or value requirements, enhancing the flexibility and
+ /// adaptiveness of claim retrieval.
+ /// Information about the client application making the request, which may influence
+ /// the processing and filtering of claims based on client-specific settings or requirements.
+ /// A task that resolves to a encapsulating the user claims in a structured JSON
+ /// format suitable for further processing, or null if the necessary claims cannot be retrieved or are not
+ /// applicable based on the session details.
+ Task GetUserClaimsAsync(
+ AuthSession authSession,
+ ICollection scope,
+ ICollection>? requestedClaims,
+ ClientInfo clientInfo);
+}
diff --git a/Abblix.Oidc.Server/Endpoints/UserInfo/Interfaces/IUserInfoProvider.cs b/Abblix.Oidc.Server/Features/UserInfo/IUserInfoProvider.cs
similarity index 79%
rename from Abblix.Oidc.Server/Endpoints/UserInfo/Interfaces/IUserInfoProvider.cs
rename to Abblix.Oidc.Server/Features/UserInfo/IUserInfoProvider.cs
index 1029a8f4..4e763dd9 100644
--- a/Abblix.Oidc.Server/Endpoints/UserInfo/Interfaces/IUserInfoProvider.cs
+++ b/Abblix.Oidc.Server/Features/UserInfo/IUserInfoProvider.cs
@@ -29,12 +29,13 @@
using System.Text.Json.Nodes;
-
-namespace Abblix.Oidc.Server.Endpoints.UserInfo.Interfaces;
+namespace Abblix.Oidc.Server.Features.UserInfo;
///
-/// Provides functionality to retrieve user information as JWT claims, supporting both simple and structured claim values.
-/// This interface enables the dynamic extraction and packaging of user attributes into JWT claims, accommodating a variety
+/// Provides functionality to retrieve user information as JWT claims, supporting both simple and structured claim
+/// values.
+/// This interface enables the dynamic extraction and packaging of user attributes into JWT claims, accommodating a
+/// variety
/// of claim types including those that require complex, structured data beyond traditional scalar values.
///
public interface IUserInfoProvider
@@ -45,23 +46,28 @@ public interface IUserInfoProvider
/// specification by allowing for the selective disclosure of user information, catering to the need for complex
/// data structures within claims.
///
- /// The unique subject identifier (sub claim) of the user whose information is being requested.
- /// This identifier must uniquely identify the user across all applications and services.
- /// A collection of names representing the claims requested by a client application.
+ ///
+ /// The unique subject identifier (sub claim) of the user whose information is being requested.
+ /// This identifier must uniquely identify the user across all applications and services.
+ ///
+ ///
+ /// A collection of names representing the claims requested by a client application.
/// Implementations should check against this list to return only those claims that are requested and authorized
- /// for release, including both scalar values and structured data as necessary.
+ /// for release, including both scalar values and structured data as necessary.
+ ///
///
- /// A task that resolves to a , encapsulating the user's claims where each entry consists of
+ /// A task that resolves to a , encapsulating the user's claims where each entry consists of
/// a claim name and its value. The value can be a simple scalar value (e.g., a string or number) or a structured
/// object, allowing for complex data types to be represented. Returns null if no information is available for the
- /// given subject. The use of facilitates the representation of hierarchical data within claims,
+ /// given subject. The use of facilitates the representation of hierarchical data within
+ /// claims,
/// supporting richer and more detailed user profiles.
///
///
/// Implementers should ensure that the disclosure of user information complies with applicable privacy laws and
/// the principles of data minimization. Sensitive or personal information must only be shared with explicit user
/// consent and in a secure manner. In cases where the requested user or claims are not found, returning null or an
- /// empty helps maintain privacy and security.
+ /// empty helps maintain privacy and security.
///
Task GetUserInfoAsync(string subject, IEnumerable requestedClaims);
}
diff --git a/Abblix.Oidc.Server/Common/Implementation/ScopeClaimsProvider.cs b/Abblix.Oidc.Server/Features/UserInfo/ScopeClaimsProvider.cs
similarity index 71%
rename from Abblix.Oidc.Server/Common/Implementation/ScopeClaimsProvider.cs
rename to Abblix.Oidc.Server/Features/UserInfo/ScopeClaimsProvider.cs
index a52d3683..065597b7 100644
--- a/Abblix.Oidc.Server/Common/Implementation/ScopeClaimsProvider.cs
+++ b/Abblix.Oidc.Server/Features/UserInfo/ScopeClaimsProvider.cs
@@ -29,16 +29,19 @@
using Abblix.Jwt;
using Abblix.Oidc.Server.Common.Constants;
-using Abblix.Oidc.Server.Common.Interfaces;
-using Abblix.Oidc.Server.Model;
-namespace Abblix.Oidc.Server.Common.Implementation;
+namespace Abblix.Oidc.Server.Features.UserInfo;
///
-/// Provides claim names based on requested scopes and claims.
+/// Implements the interface to provide claim names based on requested scopes
+/// and claims. This class manages the association between scopes and the specific claims they include,
+/// facilitating the retrieval of appropriate claims for given scopes during the authorization process.
///
public class ScopeClaimsProvider : IScopeClaimsProvider
{
+ ///
+ /// A mapping from scopes to the respective arrays of claim types that each scope encompasses.
+ ///
private readonly Dictionary _scopeToClaimsMap = new[]
{
StandardScopes.OpenId,
@@ -50,25 +53,23 @@ public class ScopeClaimsProvider : IScopeClaimsProvider
}.ToDictionary(definition => definition.Scope, definition => definition.ClaimTypes, StringComparer.OrdinalIgnoreCase);
///
- /// Gets the requested claims based on scopes and requested claim details.
+ /// Retrieves the specific claims associated with the requested scopes and any additional requested claims.
///
- /// An array of requested scopes.
- /// A dictionary of requested claim details.
- /// Enumeration of claim names.
+ /// The collection of scopes for which claims need to be provided.
+ /// Additional specific claims requested outside of the scope requests.
+ /// A collection of claim names that are associated with the requested scopes and additional claims.
+ ///
public IEnumerable GetRequestedClaims(
IEnumerable scopes,
- Dictionary? requestedClaims)
+ IEnumerable? requestedClaims)
{
var claimNames = scopes
- .SelectMany(scope => _scopeToClaimsMap.TryGetValue(scope, out var claimsInScope) ? claimsInScope : Array.Empty())
+ .SelectMany(scope => _scopeToClaimsMap.TryGetValue(scope, out var claims) ? claims : Array.Empty())
.Prepend(JwtClaimTypes.Subject);
if (requestedClaims != null)
{
- claimNames = claimNames.Concat(
- from claim in requestedClaims
- where claim.Value.Essential == true
- select claim.Key);
+ claimNames = claimNames.Concat(requestedClaims);
}
return claimNames;
diff --git a/Abblix.Oidc.Server/Common/Implementation/SubjectTypeConverter.cs b/Abblix.Oidc.Server/Features/UserInfo/SubjectTypeConverter.cs
similarity index 97%
rename from Abblix.Oidc.Server/Common/Implementation/SubjectTypeConverter.cs
rename to Abblix.Oidc.Server/Features/UserInfo/SubjectTypeConverter.cs
index 954fd7f6..3aeaefde 100644
--- a/Abblix.Oidc.Server/Common/Implementation/SubjectTypeConverter.cs
+++ b/Abblix.Oidc.Server/Features/UserInfo/SubjectTypeConverter.cs
@@ -30,11 +30,10 @@
using System.Security.Cryptography;
using System.Text;
using Abblix.Oidc.Server.Common.Constants;
-using Abblix.Oidc.Server.Common.Interfaces;
using Abblix.Oidc.Server.Features.ClientInformation;
using Abblix.Utils;
-namespace Abblix.Oidc.Server.Common.Implementation;
+namespace Abblix.Oidc.Server.Features.UserInfo;
///
/// Implements conversion of subject identifiers for end-users based on the subject type requested by the client.
diff --git a/Abblix.Oidc.Server/Features/UserInfo/UserClaimsProvider.cs b/Abblix.Oidc.Server/Features/UserInfo/UserClaimsProvider.cs
new file mode 100644
index 00000000..c48052a2
--- /dev/null
+++ b/Abblix.Oidc.Server/Features/UserInfo/UserClaimsProvider.cs
@@ -0,0 +1,140 @@
+// Abblix OpenID Connect Server Library
+// Copyright (c) 2024 by Abblix LLP
+//
+// This software is provided 'as-is', without any express or implied warranty. In no
+// event will the authors be held liable for any damages arising from the use of this
+// software.
+//
+// Permitted Use: This software is open for use and extension by non-profit,
+// educational and community projects under the condition that it remains unmodified
+// and used in its entirety through official Nuget packages. Any unauthorized
+// modification, forking of the whole repository, or altering individual files is
+// strictly prohibited to ensure development occurs solely within the official Abblix LLP
+// repository.
+//
+// Prohibited Actions: Redistribution, modification, incorporation of this software or
+// any part thereof into other products, and creation of derivative works are not
+// permitted without obtaining a commercial license from Abblix LLP.
+//
+// Commercial Use: A separate license is required for commercial use, including
+// functionalities extended beyond the original software. For information on obtaining
+// a commercial license, please contact Abblix LLP.
+//
+// Enforcement: Unauthorized redistribution, modification, or use of this software in
+// other projects or products is strictly prohibited without prior written permission
+// from the copyright holder. Violations may be subject to legal action.
+//
+// For more information, please refer to the license agreement located at:
+// https://github.com/Abblix/Oidc.Server/blob/master/README.md
+
+using System.Text.Json.Nodes;
+using Abblix.Jwt;
+using Abblix.Oidc.Server.Common.Interfaces;
+using Abblix.Oidc.Server.Features.ClientInformation;
+using Abblix.Oidc.Server.Features.UserAuthentication;
+using Abblix.Oidc.Server.Model;
+using Microsoft.Extensions.Logging;
+
+namespace Abblix.Oidc.Server.Features.UserInfo;
+
+///
+/// Handles the retrieval of user claims for authentication sessions, ensuring compliance with requested scopes and
+/// specific claim details. This class integrates directly with user information providers and scope-to-claim mappings
+/// to fetch and validate the necessary user data. It supports converting user data into claims that adhere to
+/// OpenID Connect standards, tailored to the specific needs of the client making the request.
+///
+public class UserClaimsProvider : IUserClaimsProvider
+{
+ ///
+ /// Initializes a new instance of the class.
+ ///
+ /// The logger used for logging information and errors.
+ /// The provider used to retrieve detailed user information based on specific claims.
+ ///
+ /// The provider that maps requested scopes to the corresponding set of claims.
+ ///
+ /// The converter used to translate user identifiers into subject types as
+ /// required by different client configurations.
+ public UserClaimsProvider(
+ ILogger logger,
+ IUserInfoProvider userInfoProvider,
+ IScopeClaimsProvider scopeClaimsProvider,
+ ISubjectTypeConverter subjectTypeConverter)
+ {
+ _logger = logger;
+ _userInfoProvider = userInfoProvider;
+ _scopeClaimsProvider = scopeClaimsProvider;
+ _subjectTypeConverter = subjectTypeConverter;
+ }
+
+ private readonly ILogger _logger;
+ private readonly IScopeClaimsProvider _scopeClaimsProvider;
+ private readonly IUserInfoProvider _userInfoProvider;
+ private readonly ISubjectTypeConverter _subjectTypeConverter;
+
+ ///
+ /// Asynchronously retrieves structured user claims based on an authentication session and specific claim parameters.
+ /// This method ensures compliance with the OpenID Connect standards by validating essential claims and formatting
+ /// the user data into a structured JSON object.
+ ///
+ /// The authentication session providing the context for user claims retrieval.
+ /// A collection of scopes defining the categories of claims required.
+ /// A collection detailing specific claims requested by the client, including any
+ /// requirements for essential claims.
+ /// Information about the client application making the request, which may influence how
+ /// claims are processed and returned.
+ /// A task that when completed returns a representing the user claims,
+ /// or throws an exception if required claims are missing.
+ public async Task GetUserClaimsAsync(
+ AuthSession authSession,
+ ICollection scope,
+ ICollection>? requestedClaims,
+ ClientInfo clientInfo)
+ {
+ var claimNames = _scopeClaimsProvider.GetRequestedClaims(
+ scope,
+ from claim in requestedClaims select claim.Key)
+ ;
+
+ var userInfo = await _userInfoProvider.GetUserInfoAsync(
+ authSession.Subject,
+ claimNames.Distinct(StringComparer.Ordinal));
+ if (userInfo == null)
+ {
+ _logger.LogWarning("The user claims were not found by subject value");
+ return null;
+ }
+
+ var subject = _subjectTypeConverter.Convert(authSession.Subject, clientInfo);
+ userInfo.SetProperty(JwtClaimTypes.Subject, subject);
+
+ if (FindMissingClaims(userInfo, requestedClaims) is { Count: > 0 } missingClaims)
+ {
+ _logger.LogWarning("The following claims are requested, but not returned from {IUserInfoProvider}: {@MissingClaims}",
+ _userInfoProvider.GetType().FullName,
+ missingClaims);
+
+ return null;
+ }
+
+ return userInfo;
+ }
+
+ private static List? FindMissingClaims(
+ JsonObject userInfo,
+ ICollection>? requestedClaims)
+ {
+ if (requestedClaims == null)
+ return null;
+
+ List? missingClaims = null;
+ foreach (var (claim, details) in requestedClaims)
+ if (details.Essential == true && !userInfo.TryGetPropertyValue(claim, out _))
+ {
+ missingClaims ??= new List();
+ missingClaims.Add(claim);
+ }
+
+ return missingClaims;
+ }
+}
diff --git a/Abblix.Oidc.Server/ServiceCollectionExtensions.cs b/Abblix.Oidc.Server/ServiceCollectionExtensions.cs
index adc318fa..23f1ac31 100644
--- a/Abblix.Oidc.Server/ServiceCollectionExtensions.cs
+++ b/Abblix.Oidc.Server/ServiceCollectionExtensions.cs
@@ -117,7 +117,8 @@ public static IServiceCollection AddFeatures(this IServiceCollection services)
.AddSessionManagement()
.AddRandomGenerators()
.AddLogoutNotification()
- .AddStorages();
+ .AddStorages()
+ .AddUserInfo();
}
///