Skip to content

Commit

Permalink
Make ScopeClaimsProvider depends on IScopeManager
Browse files Browse the repository at this point in the history
  • Loading branch information
kirill-abblix committed Jun 28, 2024
1 parent 03cc721 commit d5039bc
Show file tree
Hide file tree
Showing 5 changed files with 38 additions and 25 deletions.
22 changes: 16 additions & 6 deletions Abblix.Oidc.Server/Endpoints/UserInfo/UserInfoRequestValidator.cs
Original file line number Diff line number Diff line change
Expand Up @@ -71,15 +71,19 @@ public UserInfoRequestValidator(
/// <param name="clientRequest">Additional client request information for contextual validation.</param>
/// <returns>A <see cref="Task"/> representing the asynchronous operation,
/// which upon completion will yield a <see cref="UserInfoRequestValidationResult"/>.</returns>
public async Task<UserInfoRequestValidationResult> ValidateAsync(UserInfoRequest userInfoRequest, ClientRequest clientRequest)
public async Task<UserInfoRequestValidationResult> ValidateAsync(
UserInfoRequest userInfoRequest,
ClientRequest clientRequest)
{
string jwtAccessToken;
var authorizationHeader = clientRequest.AuthorizationHeader;
if (authorizationHeader != null)
{
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)
Expand Down Expand Up @@ -118,10 +122,12 @@ public async Task<UserInfoRequestValidationResult> 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;

Expand All @@ -134,7 +140,11 @@ public async Task<UserInfoRequestValidationResult> 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);
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -28,7 +28,7 @@ namespace Abblix.Oidc.Server.Features.ScopeManagement;
/// <summary>
/// Defines a contract for managing and retrieving scope definitions.
/// </summary>
public interface IScopeManager
public interface IScopeManager: IEnumerable<ScopeDefinition>
{
/// <summary>
/// Attempts to retrieve the definition of a specified scope.
Expand Down
5 changes: 5 additions & 0 deletions Abblix.Oidc.Server/Features/ScopeManagement/ScopeManager.cs
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand Down Expand Up @@ -67,4 +68,8 @@ public ScopeManager(IOptions<OidcOptions> options)
/// <returns>True if the scope exists and the definition is retrieved, false otherwise.</returns>
public bool TryGet(string scope, [MaybeNullWhen(false)] out ScopeDefinition definition)
=> _scopes.TryGetValue(scope, out definition);

public IEnumerator<ScopeDefinition> GetEnumerator() => _scopes.Values.GetEnumerator();

IEnumerator IEnumerable.GetEnumerator() => GetEnumerator();
}
28 changes: 13 additions & 15 deletions Abblix.Oidc.Server/Features/UserInfo/ScopeClaimsProvider.cs
Original file line number Diff line number Diff line change
Expand Up @@ -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;

Expand All @@ -32,18 +32,12 @@ namespace Abblix.Oidc.Server.Features.UserInfo;
/// </summary>
public class ScopeClaimsProvider : IScopeClaimsProvider
{
/// <summary>
/// A mapping from scopes to the respective arrays of claim types that each scope encompasses.
/// </summary>
private readonly Dictionary<string, string[]> _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;

/// <summary>
/// Retrieves the specific claims associated with the requested scopes and any additional requested claims.
Expand All @@ -57,7 +51,9 @@ public IEnumerable<string> GetRequestedClaims(
IEnumerable<string>? requestedClaims)
{
var claimNames = scopes
.SelectMany(scope => _scopeToClaimsMap.TryGetValue(scope, out var claims) ? claims : Array.Empty<string>())
.SelectMany(scope => _scopeManager.TryGet(scope, out var scopeDefinition)
? scopeDefinition.ClaimTypes
: Array.Empty<string>())
.Prepend(JwtClaimTypes.Subject);

if (requestedClaims != null)
Expand All @@ -71,10 +67,12 @@ public IEnumerable<string> GetRequestedClaims(
/// <summary>
/// A collection of all the scopes supported by this provider.
/// </summary>
public IEnumerable<string> ScopesSupported => _scopeToClaimsMap.Keys;
public IEnumerable<string> ScopesSupported
=> _scopeManager.Select(def => def.Scope);

/// <summary>
/// A collection of all the claims that can be provided by this provider.
/// </summary>
public IEnumerable<string> ClaimsSupported => _scopeToClaimsMap.Values.SelectMany(claims => claims);
public IEnumerable<string> ClaimsSupported
=> _scopeManager.SelectMany(def => def.ClaimTypes).Distinct(StringComparer.Ordinal);
}
6 changes: 3 additions & 3 deletions Abblix.Oidc.Server/Features/UserInfo/UserClaimsProvider.cs
Original file line number Diff line number Diff line change
Expand Up @@ -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");
Expand Down

0 comments on commit d5039bc

Please sign in to comment.