diff --git a/Abblix.Oidc.Server/Endpoints/UserInfo/UserInfoRequestValidator.cs b/Abblix.Oidc.Server/Endpoints/UserInfo/UserInfoRequestValidator.cs index 37065ca7..f081c798 100644 --- a/Abblix.Oidc.Server/Endpoints/UserInfo/UserInfoRequestValidator.cs +++ b/Abblix.Oidc.Server/Endpoints/UserInfo/UserInfoRequestValidator.cs @@ -71,7 +71,9 @@ public UserInfoRequestValidator( /// Additional client request information for contextual validation. /// A representing the asynchronous operation, /// which upon completion will yield a . - public async Task ValidateAsync(UserInfoRequest userInfoRequest, ClientRequest clientRequest) + public async Task ValidateAsync( + UserInfoRequest userInfoRequest, + ClientRequest clientRequest) { string jwtAccessToken; var authorizationHeader = clientRequest.AuthorizationHeader; @@ -79,7 +81,9 @@ public async Task ValidateAsync(UserInfoRequest { if (authorizationHeader.Scheme != TokenTypes.Bearer) { - return new UserInfoRequestError(ErrorCodes.InvalidGrant, $"The scheme name '{authorizationHeader.Scheme}' is not supported"); + return new UserInfoRequestError( + ErrorCodes.InvalidGrant, + $"The scheme name '{authorizationHeader.Scheme}' is not supported"); } if (userInfoRequest.AccessToken != null) @@ -118,10 +122,12 @@ public async Task ValidateAsync(UserInfoRequest switch (result) { - case ValidJsonWebToken { Token: { Header.Type: var tokenType } token }: - if (tokenType != JwtTypes.AccessToken) - return new UserInfoRequestError(ErrorCodes.InvalidGrant, $"Invalid token type: {tokenType}"); + case ValidJsonWebToken { Token.Header.Type: var tokenType } when tokenType != JwtTypes.AccessToken: + return new UserInfoRequestError( + ErrorCodes.InvalidGrant, + $"Invalid token type: {tokenType}"); + case ValidJsonWebToken { Token: var token }: (authSession, authContext) = await _accessTokenService.AuthenticateByAccessTokenAsync(token); break; @@ -134,7 +140,11 @@ public async Task ValidateAsync(UserInfoRequest var clientInfo = await _clientInfoProvider.TryFindClientAsync(authContext.ClientId).WithLicenseCheck(); if (clientInfo == null) - return new UserInfoRequestError(ErrorCodes.InvalidGrant, $"The client '{authContext.ClientId}' is not found"); + { + return new UserInfoRequestError( + ErrorCodes.InvalidGrant, + $"The client '{authContext.ClientId}' is not found"); + } return new ValidUserInfoRequest(userInfoRequest, authSession, authContext, clientInfo); } diff --git a/Abblix.Oidc.Server/Features/ScopeManagement/IScopeManager.cs b/Abblix.Oidc.Server/Features/ScopeManagement/IScopeManager.cs index 2417ac04..5edd5730 100644 --- a/Abblix.Oidc.Server/Features/ScopeManagement/IScopeManager.cs +++ b/Abblix.Oidc.Server/Features/ScopeManagement/IScopeManager.cs @@ -28,7 +28,7 @@ namespace Abblix.Oidc.Server.Features.ScopeManagement; /// /// Defines a contract for managing and retrieving scope definitions. /// -public interface IScopeManager +public interface IScopeManager: IEnumerable { /// /// Attempts to retrieve the definition of a specified scope. diff --git a/Abblix.Oidc.Server/Features/ScopeManagement/ScopeManager.cs b/Abblix.Oidc.Server/Features/ScopeManagement/ScopeManager.cs index c2000ae0..d965a216 100644 --- a/Abblix.Oidc.Server/Features/ScopeManagement/ScopeManager.cs +++ b/Abblix.Oidc.Server/Features/ScopeManagement/ScopeManager.cs @@ -20,6 +20,7 @@ // CONTACT: For license inquiries or permissions, contact Abblix LLP at // info@abblix.com +using System.Collections; using System.Diagnostics.CodeAnalysis; using Abblix.Oidc.Server.Common.Configuration; using Abblix.Oidc.Server.Common.Constants; @@ -67,4 +68,8 @@ public ScopeManager(IOptions options) /// True if the scope exists and the definition is retrieved, false otherwise. public bool TryGet(string scope, [MaybeNullWhen(false)] out ScopeDefinition definition) => _scopes.TryGetValue(scope, out definition); + + public IEnumerator GetEnumerator() => _scopes.Values.GetEnumerator(); + + IEnumerator IEnumerable.GetEnumerator() => GetEnumerator(); } diff --git a/Abblix.Oidc.Server/Features/UserInfo/ScopeClaimsProvider.cs b/Abblix.Oidc.Server/Features/UserInfo/ScopeClaimsProvider.cs index 36eb784c..c63f3351 100644 --- a/Abblix.Oidc.Server/Features/UserInfo/ScopeClaimsProvider.cs +++ b/Abblix.Oidc.Server/Features/UserInfo/ScopeClaimsProvider.cs @@ -21,7 +21,7 @@ // info@abblix.com using Abblix.Jwt; -using Abblix.Oidc.Server.Common.Constants; +using Abblix.Oidc.Server.Features.ScopeManagement; namespace Abblix.Oidc.Server.Features.UserInfo; @@ -32,18 +32,12 @@ namespace Abblix.Oidc.Server.Features.UserInfo; /// public class ScopeClaimsProvider : IScopeClaimsProvider { - /// - /// A mapping from scopes to the respective arrays of claim types that each scope encompasses. - /// - private readonly Dictionary _scopeToClaimsMap = new[] + public ScopeClaimsProvider(IScopeManager scopeManager) { - StandardScopes.OpenId, - StandardScopes.Profile, - StandardScopes.Email, - StandardScopes.Address, - StandardScopes.Phone, - StandardScopes.OfflineAccess, - }.ToDictionary(definition => definition.Scope, definition => definition.ClaimTypes, StringComparer.OrdinalIgnoreCase); + _scopeManager = scopeManager; + } + + private readonly IScopeManager _scopeManager; /// /// Retrieves the specific claims associated with the requested scopes and any additional requested claims. @@ -57,7 +51,9 @@ public IEnumerable GetRequestedClaims( IEnumerable? requestedClaims) { var claimNames = scopes - .SelectMany(scope => _scopeToClaimsMap.TryGetValue(scope, out var claims) ? claims : Array.Empty()) + .SelectMany(scope => _scopeManager.TryGet(scope, out var scopeDefinition) + ? scopeDefinition.ClaimTypes + : Array.Empty()) .Prepend(JwtClaimTypes.Subject); if (requestedClaims != null) @@ -71,10 +67,12 @@ public IEnumerable GetRequestedClaims( /// /// A collection of all the scopes supported by this provider. /// - public IEnumerable ScopesSupported => _scopeToClaimsMap.Keys; + public IEnumerable ScopesSupported + => _scopeManager.Select(def => def.Scope); /// /// A collection of all the claims that can be provided by this provider. /// - public IEnumerable ClaimsSupported => _scopeToClaimsMap.Values.SelectMany(claims => claims); + public IEnumerable ClaimsSupported + => _scopeManager.SelectMany(def => def.ClaimTypes).Distinct(StringComparer.Ordinal); } diff --git a/Abblix.Oidc.Server/Features/UserInfo/UserClaimsProvider.cs b/Abblix.Oidc.Server/Features/UserInfo/UserClaimsProvider.cs index d618b769..56a9e460 100644 --- a/Abblix.Oidc.Server/Features/UserInfo/UserClaimsProvider.cs +++ b/Abblix.Oidc.Server/Features/UserInfo/UserClaimsProvider.cs @@ -84,11 +84,11 @@ public UserClaimsProvider( ClientInfo clientInfo) { var claimNames = _scopeClaimsProvider.GetRequestedClaims( - scope, requestedClaims?.Select(claim => claim.Key)); + scope, requestedClaims?.Select(claim => claim.Key)) + .Distinct(StringComparer.Ordinal); var userInfo = await _userInfoProvider.GetUserInfoAsync( - authSession.Subject, - claimNames.Distinct(StringComparer.Ordinal)); + authSession.Subject, claimNames); if (userInfo == null) { _logger.LogWarning("The user claims were not found by subject value");