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");