/// <summary> /// Returns claims for an identity token /// </summary> /// <param name="subject">The subject</param> /// <param name="client">The client</param> /// <param name="scopes">The requested scopes</param> /// <param name="includeAllIdentityClaims">Specifies if all claims should be included in the token, or if the userinfo endpoint can be used to retrieve them</param> /// <param name="request">The raw request</param> /// <returns> /// Claims for the identity token /// </returns> public virtual async Task <IEnumerable <Claim> > GetIdentityTokenClaimsAsync(ClaimsPrincipal subject, Client client, IEnumerable <Scope> scopes, bool includeAllIdentityClaims, ValidatedRequest request) { Logger.Info("Getting claims for identity token for subject: " + subject.GetSubjectId()); var outputClaims = new List <Claim>(GetStandardSubjectClaims(subject)); outputClaims.AddRange(GetOptionalClaims(subject)); var additionalClaims = new List <string>(); // if a include all claims rule exists, call the user service without a claims filter if (scopes.IncludesAllClaimsForUserRule(ScopeType.Identity)) { Logger.Info("All claims rule found - emitting all claims for user."); var context = new ProfileDataRequestContext( subject, client, Constants.ProfileDataCallers.ClaimsProviderIdentityToken); await _users.GetProfileDataAsync(context); var claims = FilterProtocolClaims(context.IssuedClaims); if (claims != null) { outputClaims.AddRange(claims); } return(outputClaims); } // fetch all identity claims that need to go into the id token foreach (var scope in scopes) { if (scope.Type == ScopeType.Identity) { foreach (var scopeClaim in scope.Claims) { if (includeAllIdentityClaims || scopeClaim.AlwaysIncludeInIdToken) { additionalClaims.Add(scopeClaim.Name); } } } } if (additionalClaims.Count > 0) { var context = new ProfileDataRequestContext( subject, client, Constants.ProfileDataCallers.ClaimsProviderIdentityToken, additionalClaims); await _users.GetProfileDataAsync(context); var claims = FilterProtocolClaims(context.IssuedClaims); if (claims != null) { outputClaims.AddRange(claims); } } return(outputClaims); }
protected override ActionResult ShowConsent(ValidatedRequest validatedRequest) { Tracing.Information("OIDC Consent screen"); return View("Consent", new OidcViewModel(validatedRequest)); }
/// <summary> /// Returns claims for an identity token /// </summary> /// <param name="subject">The subject</param> /// <param name="resources">The requested resources</param> /// <param name="includeAllIdentityClaims">Specifies if all claims should be included in the token, or if the userinfo endpoint can be used to retrieve them</param> /// <param name="request">The raw request</param> /// <returns> /// Claims for the identity token /// </returns> public virtual async Task <IEnumerable <Claim> > GetIdentityTokenClaimsAsync(ClaimsPrincipal subject, Resources resources, bool includeAllIdentityClaims, ValidatedRequest request) { Logger.LogDebug("Getting claims for identity token for subject: {subject} and client: {clientId}", subject.GetSubjectId(), request.Client.ClientId); var outputClaims = new List <Claim>(GetStandardSubjectClaims(subject)); outputClaims.AddRange(GetOptionalClaims(subject)); // fetch all identity claims that need to go into the id token if (includeAllIdentityClaims || request.Client.AlwaysIncludeUserClaimsInIdToken) { var additionalClaimTypes = new List <string>(); foreach (var identityResource in resources.IdentityResources) { foreach (var userClaim in identityResource.UserClaims) { additionalClaimTypes.Add(userClaim); } } // filter so we don't ask for claim types that we will eventually filter out additionalClaimTypes = FilterRequestedClaimTypes(additionalClaimTypes).ToList(); var context = new ProfileDataRequestContext( subject, request.Client, IdentityServerConstants.ProfileDataCallers.ClaimsProviderIdentityToken, additionalClaimTypes); context.RequestedResources = resources; await Profile.GetProfileDataAsync(context); var claims = FilterProtocolClaims(context.IssuedClaims); if (claims != null) { outputClaims.AddRange(claims); } } else { Logger.LogDebug("In addition to an id_token, an access_token was requested. No claims other than sub are included in the id_token. To obtain more user claims, either use the user info endpoint or set AlwaysIncludeUserClaimsInIdToken on the client configuration."); } return(outputClaims); }
/// <summary> /// Returns claims for an identity token. /// </summary> /// <param name="subject">The subject.</param> /// <param name="client">The client.</param> /// <param name="scopes">The requested scopes.</param> /// <param name="request">The raw request.</param> /// <returns> /// Claims for the access token /// </returns> public virtual async Task <IEnumerable <Claim> > GetAccessTokenClaimsAsync(ClaimsPrincipal subject, Client client, IEnumerable <Scope> scopes, ValidatedRequest request) { // add client_id var outputClaims = new List <Claim> { new Claim(Constants.ClaimTypes.ClientId, client.ClientId), }; // check for client claims if (client.Claims != null && client.Claims.Any()) { if (subject == null || client.AlwaysSendClientClaims) { foreach (var claim in client.Claims) { var claimType = claim.Type; if (client.PrefixClientClaims) { claimType = "client_" + claimType; } outputClaims.Add(new Claim(claimType, claim.Value, claim.ValueType)); } } } // add scopes foreach (var scope in scopes) { outputClaims.Add(new Claim(Constants.ClaimTypes.Scope, scope.Name)); } // a user is involved if (subject != null) { outputClaims.AddRange(GetStandardSubjectClaims(subject)); outputClaims.AddRange(GetOptionalClaims(subject)); // if a include all claims rule exists, call the user service without a claims filter if (scopes.IncludesAllClaimsForUserRule(ScopeType.Resource) || scopes.IncludesAllClaimsForUserRule(ScopeType.Identity)) { var context = new ProfileDataRequestContext( subject, client, Constants.ProfileDataCallers.ClaimsProviderAccessToken); await _users.GetProfileDataAsync(context); var claims = FilterProtocolClaims(context.IssuedClaims); if (claims != null) { outputClaims.AddRange(claims); } return(outputClaims); } // fetch all resource claims that need to go into the access token var additionalClaims = new List <string>(); foreach (var scope in scopes) { if (scope.Type == ScopeType.Resource) { if (scope.Claims != null) { foreach (var scopeClaim in scope.Claims) { additionalClaims.Add(scopeClaim.Name); } } } } if (additionalClaims.Count > 0) { var context = new ProfileDataRequestContext( subject, client, Constants.ProfileDataCallers.ClaimsProviderAccessToken, additionalClaims.Distinct()); await _users.GetProfileDataAsync(context); var claims = FilterProtocolClaims(context.IssuedClaims); if (claims != null) { outputClaims.AddRange(claims); } } } return(outputClaims); }
public ValidatedRequest Validate(Application application, AuthorizeRequest request) { // If the request fails due to a missing, invalid, or mismatching // redirection URI, or if the client identifier is missing or invalid, // the authorization server SHOULD inform the resource owner of the // error and MUST NOT automatically redirect the user-agent to the // invalid redirection URI. var validatedRequest = new ValidatedRequest(); // validate request model binding if (request == null) { throw new AuthorizeRequestResourceOwnerException("Invalid request parameters."); } validatedRequest.Application = application; Tracing.InformationFormat("OAuth2 application: {0} ({1})", validatedRequest.Application.Name, validatedRequest.Application.Namespace); validatedRequest.ShowRememberConsent = application.AllowRememberConsentDecision; // make sure redirect uri is present if (string.IsNullOrWhiteSpace(request.redirect_uri)) { throw new AuthorizeRequestResourceOwnerException("Missing redirect URI"); } // validate client if (string.IsNullOrWhiteSpace(request.client_id)) { throw new AuthorizeRequestResourceOwnerException("Missing client identifier"); } var client = _clientManager.Get(request.client_id); if (client == null) { throw new AuthorizeRequestResourceOwnerException("Invalid client: " + request.client_id); } validatedRequest.Client = client; Tracing.InformationFormat("Client: {0} ({1})", validatedRequest.Client.Name, validatedRequest.Client.ClientId); // make sure redirect_uri is a valid uri, and in case of http is over ssl Uri redirectUri; if (Uri.TryCreate(request.redirect_uri, UriKind.Absolute, out redirectUri)) { if (redirectUri.Scheme == Uri.UriSchemeHttp) { throw new AuthorizeRequestClientException( "Redirect URI not over SSL : " + request.redirect_uri, new Uri(request.redirect_uri), OAuthConstants.Errors.InvalidRequest, string.Empty, validatedRequest.State); } // make sure redirect uri is registered with client var validUri = validatedRequest.Client.RedirectUris.Get(request.redirect_uri); if (validUri == null) { throw new AuthorizeRequestResourceOwnerException("Invalid redirect URI: " + request.redirect_uri); } validatedRequest.RedirectUri = validUri; Tracing.InformationFormat("Redirect URI: {0} ({1})", validatedRequest.RedirectUri.Uri, validatedRequest.RedirectUri.Description); } else { var message = "Invalid redirect URI: " + request.redirect_uri; Tracing.Error(message); throw new AuthorizeRequestResourceOwnerException("Invalid redirect URI: " + request.redirect_uri); } // check state if (!string.IsNullOrWhiteSpace(request.state)) { validatedRequest.State = request.state; Tracing.Information("State: " + validatedRequest.State); } else { Tracing.Information("No state supplied."); } // validate response type if (String.IsNullOrWhiteSpace(request.response_type)) { throw new AuthorizeRequestClientException( "response_type is null or empty", new Uri(validatedRequest.RedirectUri.Uri), OAuthConstants.Errors.InvalidRequest, string.Empty, validatedRequest.State); } // check response type (only code and token are supported) if (!request.response_type.Equals(OAuthConstants.ResponseTypes.Token, StringComparison.Ordinal) && !request.response_type.Equals(OAuthConstants.ResponseTypes.Code, StringComparison.Ordinal)) { throw new AuthorizeRequestClientException( "response_type is not token or code: " + request.response_type, new Uri(validatedRequest.RedirectUri.Uri), OAuthConstants.Errors.UnsupportedResponseType, string.Empty, validatedRequest.State); } validatedRequest.ResponseType = request.response_type; Tracing.Information("Response type: " + validatedRequest.ResponseType); if (request.response_type == OAuthConstants.ResponseTypes.Code) { ValidateCodeResponseType(validatedRequest, request); } else if (request.response_type == OAuthConstants.ResponseTypes.Token) { ValidateTokenResponseType(validatedRequest, request); } else { throw new AuthorizeRequestClientException( "Invalid response_type: " + request.response_type, new Uri(validatedRequest.RedirectUri.Uri), OAuthConstants.Errors.UnsupportedResponseType, request.response_type, validatedRequest.State); } ValidateScopes(request, validatedRequest); // TODO: fix based upon past "remember me" settings validatedRequest.ShowConsent = client.RequireConsent || application.RequireConsent; Tracing.Information("Authorize request validation successful."); return(validatedRequest); }
/// <summary> /// Returns claims for an identity token. /// </summary> /// <param name="subject">The subject.</param> /// <param name="resources">The requested resources</param> /// <param name="request">The raw request.</param> /// <returns> /// Claims for the access token /// </returns> public virtual async Task <IEnumerable <Claim> > GetAccessTokenClaimsAsync(ClaimsPrincipal subject, Resources resources, ValidatedRequest request) { Logger.LogDebug("Getting claims for access token for client: {clientId}", request.Client.ClientId); // add client_id var outputClaims = new List <Claim> { new Claim(JwtClaimTypes.ClientId, request.Client.ClientId) }; // check for client claims if (request.ClientClaims != null && request.ClientClaims.Any()) { if (subject == null || request.Client.AlwaysSendClientClaims) { foreach (var claim in request.ClientClaims) { var claimType = claim.Type; if (request.Client.ClientClaimsPrefix.IsPresent()) { claimType = request.Client.ClientClaimsPrefix + claimType; } outputClaims.Add(new Claim(claimType, claim.Value, claim.ValueType)); } } } // add scopes foreach (var scope in resources.IdentityResources) { outputClaims.Add(new Claim(JwtClaimTypes.Scope, scope.Name)); } foreach (var scope in resources.ApiResources.SelectMany(x => x.Scopes)) { outputClaims.Add(new Claim(JwtClaimTypes.Scope, scope.Name)); } // a user is involved if (subject != null) { if (resources.OfflineAccess) { outputClaims.Add(new Claim(JwtClaimTypes.Scope, IdentityServerConstants.StandardScopes.OfflineAccess)); } Logger.LogDebug("Getting claims for access token for subject: {subject}", subject.GetSubjectId()); outputClaims.AddRange(GetStandardSubjectClaims(subject)); outputClaims.AddRange(GetOptionalClaims(subject)); // fetch all resource claims that need to go into the access token var additionalClaimTypes = new List <string>(); foreach (var api in resources.ApiResources) { // add claims configured on api resource if (api.UserClaims != null) { foreach (var claim in api.UserClaims) { additionalClaimTypes.Add(claim); } } // add claims configured on scope foreach (var scope in api.Scopes) { if (scope.UserClaims != null) { foreach (var claim in scope.UserClaims) { additionalClaimTypes.Add(claim); } } } } // filter so we don't ask for claim types that we will eventually filter out additionalClaimTypes = FilterRequestedClaimTypes(additionalClaimTypes).ToList(); var context = new ProfileDataRequestContext( subject, request.Client, IdentityServerConstants.ProfileDataCallers.ClaimsProviderAccessToken, additionalClaimTypes.Distinct()); context.RequestedResources = resources; await Profile.GetProfileDataAsync(context); var claims = FilterProtocolClaims(context.IssuedClaims); if (claims != null) { outputClaims.AddRange(claims); } } return(outputClaims); }
public override async Task <IEnumerable <Claim> > GetAccessTokenClaimsAsync(ClaimsPrincipal subject, ResourceValidationResult resourceResult, ValidatedRequest request) { if (_scopedOverrideRawScopeValues.IsOverride) { var scopes = new HashSet <ParsedScopeValue>(); foreach (var s in _scopedOverrideRawScopeValues.Scopes) { scopes.Add((new ParsedScopeValue(s))); } if (resourceResult.Resources.OfflineAccess) { scopes.Add((new ParsedScopeValue(IdentityServerConstants.StandardScopes.OfflineAccess))); } resourceResult.ParsedScopes = scopes; } var claims = await base.GetAccessTokenClaimsAsync(subject, resourceResult, request); var clientExtra = request.Client as ClientExtra; if (!clientExtra.IncludeClientId) { claims = from claim in claims where claim.Type != JwtClaimTypes.ClientId select claim; } /* * if (!clientExtra.IncludeAmr) * { * var queryAmr = from claim in claims * where claim.Type == JwtClaimTypes.AuthenticationMethod * select claim; * if (queryAmr.Count() == 1) * { * // only meant to remove the single one that IDS4 added. * claims = from claim in claims * where claim.Type != JwtClaimTypes.AuthenticationMethod * select claim; * } * }*/ var subjectClaim = (from claim in claims where claim.Type == JwtClaimTypes.Subject select claim).FirstOrDefault(); if (subjectClaim != null && subjectClaim.Value == Constants.ReservedSubject) { claims = from claim in claims where claim.Type != JwtClaimTypes.Subject select claim; } return(claims); }
/// <summary> /// Returns claims for an identity token /// </summary> /// <param name="subject">The subject</param> /// <param name="client">The client</param> /// <param name="identityResources">The requested scopes</param> /// <param name="includeAllIdentityClaims">Specifies if all claims should be included in the token, or if the userinfo endpoint can be used to retrieve them</param> /// <param name="request">The raw request</param> /// <returns> /// Claims for the identity token /// </returns> public virtual async Task <IEnumerable <Claim> > GetIdentityTokenClaimsAsync(ClaimsPrincipal subject, Client client, Resources resources, bool includeAllIdentityClaims, ValidatedRequest request) { _logger.LogDebug("Getting claims for identity token for subject: {subject} and client: {clientId}", subject.GetSubjectId(), client.ClientId); var outputClaims = new List <Claim>(GetStandardSubjectClaims(subject)); outputClaims.AddRange(GetOptionalClaims(subject)); // fetch all identity claims that need to go into the id token if (includeAllIdentityClaims || client.AlwaysIncludeUserClaimsInIdToken) { var additionalClaims = new List <string>(); foreach (var identityResource in resources.IdentityResources) { foreach (var userClaim in identityResource.UserClaims) { additionalClaims.Add(userClaim); } } if (additionalClaims.Count > 0) { var context = new ProfileDataRequestContext( subject, client, IdentityServerConstants.ProfileDataCallers.ClaimsProviderIdentityToken, additionalClaims); await _profile.GetProfileDataAsync(context); var claims = FilterProtocolClaims(context.IssuedClaims); if (claims != null) { outputClaims.AddRange(claims); } } } return(outputClaims); }
public override async Task <IEnumerable <Claim> > GetAccessTokenClaimsAsync(ClaimsPrincipal subject, Client client, Resources resources, ValidatedRequest request) { var arbitraryClaimsCheck = request.Raw.ContainsAny(RequiredArbitraryClaimsArgument); var arbitraryScopesCheck = request.Raw.ContainsAny(RequiredArbitraryScopes); if (!arbitraryClaimsCheck && !arbitraryScopesCheck) { var missing = string.Join(",", RequiredArbitraryClaimsArgument.ToArray()); missing += ","; missing += string.Join(",", RequiredArbitraryScopes.ToArray()); var ex = new Exception(string.Format("RequiredArgument failed need the following [{0}]", missing)); _logger.LogError(LoggingEvents.REQUIRED_ITEMS_MISSING, ex); throw ex; } var result = base.GetAccessTokenClaimsAsync(subject, client, resources, request); var rr = request.Raw.AllKeys.ToDictionary(k => k, k => request.Raw[k]); List <Claim> finalClaims = new List <Claim>(result.Result); if (arbitraryScopesCheck) { var newScopes = rr["arbitrary-scopes"].Split(new char[] { ' ', '\t' }, StringSplitOptions.RemoveEmptyEntries); foreach (var scope in newScopes) { finalClaims.Add(new Claim("scope", scope)); } } Dictionary <string, string> values; if (arbitraryClaimsCheck) { values = JsonConvert.DeserializeObject <Dictionary <string, string> >(rr["arbitrary-claims"]); // paranoia check. In no way can we allow creation which tries to spoof someone elses client_id. var query = from value in values where string.Compare(value.Key, "client_id", true) != 0 select value; var trimmedClaims = query.ToList(); finalClaims.AddRange(trimmedClaims.Select(value => new Claim(value.Key, value.Value))); } if (subject != null) { finalClaims.AddRange(subject.Claims.Where(p2 => finalClaims.All(p1 => p1.Type != p2.Type))); } // if we find any, than add them to the original and send that back. IEnumerable <Claim> claimresults = finalClaims; return(claimresults); }
public virtual TokenResponse CreateTokenResponseFromRefreshToken(StoredGrant handle, IStoredGrantManager handleManager) { var resourceOwner = Principal.Create( "OAuth2", handle.ResourceOwner.ToClaims().ToArray()); if (DateTime.UtcNow > handle.Expiration) { throw new InvalidOperationException("Refresh token has expired."); } var validatedRequest = new ValidatedRequest { Client = handle.Client, Application = handle.Application, Scopes = handle.Scopes, }; var response = CreateTokenResponse(validatedRequest, resourceOwner); if (handle.CreateRefreshToken) { StoredGrant refreshTokenHandle; if (validatedRequest.Application.AllowSlidingRefreshTokenExpiration) { var rememberTimeSpan = handle.Expiration.Subtract(handle.Created); var newRefreshTokenExpiration = DateTime.UtcNow.Add(rememberTimeSpan); refreshTokenHandle = StoredGrant.CreateRefreshTokenHandle( resourceOwner.GetSubject(), handle.Client, handle.Application, resourceOwner.Claims, handle.Scopes, newRefreshTokenExpiration, createRefreshToken: validatedRequest.Client.AllowRefreshToken && validatedRequest.Application.AllowRefreshToken); } else { refreshTokenHandle = StoredGrant.CreateRefreshTokenHandle( resourceOwner.GetSubject(), handle.Client, handle.Application, resourceOwner.Claims, handle.Scopes, handle.Expiration, createRefreshToken: validatedRequest.Client.AllowRefreshToken && validatedRequest.Application.AllowRefreshToken); } response.RefreshToken = refreshTokenHandle.GrantId; handleManager.Add(refreshTokenHandle); handleManager.Delete(handle.GrantId); } else { response.RefreshToken = handle.GrantId; } return(response); }
/// <summary> /// Returns claims for an identity token. /// </summary> /// <param name="subject">The subject.</param> /// <param name="client">The client.</param> /// <param name="scopes">The requested scopes.</param> /// <param name="request">The raw request.</param> /// <returns> /// Claims for the access token /// </returns> public virtual async Task <IEnumerable <Claim> > GetAccessTokenClaimsAsync(ClaimsPrincipal subject, Client client, IEnumerable <Scope> scopes, ValidatedRequest request) { // add client_id var outputClaims = new List <Claim> { new Claim(Constants.ClaimTypes.ClientId, client.ClientId), }; // check for client claims return(await ConstructOutputClaims(subject, client, scopes, outputClaims)); }
public override async Task <IEnumerable <Claim> > GetAccessTokenClaimsAsync(ClaimsPrincipal subject, Client client, IEnumerable <Scope> scopes, ValidatedRequest request) { var claims = await base.GetAccessTokenClaimsAsync(subject, client, scopes, request); var newClaims = claims.ToList(); if (subject != null) { newClaims.Add(subject.FindFirst("user511id")); newClaims.Add(subject.FindFirst("userrole")); newClaims.Add(subject.FindFirst("greetingname")); newClaims.Add(subject.FindFirst("useremail")); newClaims.Add(subject.FindFirst("encrypted511Id")); newClaims.Add(subject.FindFirst("userroleid")); newClaims.Add(subject.FindFirst("usercountry")); newClaims.Add(subject.FindFirst("isvaliduser")); newClaims.Add(subject.FindFirst("name")); } return(newClaims); }
/// <summary> /// Returns claims for an access token. /// </summary> /// <param name="subject">The subject.</param> /// <param name="resourceResult">The validated resource result</param> /// <param name="request">The raw request.</param> /// <returns> /// Claims for the access token /// </returns> public virtual async Task <IEnumerable <Claim> > GetAccessTokenClaimsAsync(ClaimsPrincipal subject, ResourceValidationResult resourceResult, ValidatedRequest request) { Logger.LogDebug("Getting claims for access token for client: {clientId}", request.Client.ClientId); var outputClaims = new List <Claim> { new Claim(JwtClaimTypes.ClientId, request.ClientId) }; // log if client ID is overwritten if (!string.Equals(request.ClientId, request.Client.ClientId)) { Logger.LogDebug("Client {clientId} is impersonating {impersonatedClientId}", request.Client.ClientId, request.ClientId); } // check for client claims if (request.ClientClaims != null && request.ClientClaims.Any()) { if (subject == null || request.Client.AlwaysSendClientClaims) { foreach (var claim in request.ClientClaims) { var claimType = claim.Type; if (request.Client.ClientClaimsPrefix.IsPresent()) { claimType = request.Client.ClientClaimsPrefix + claimType; } outputClaims.Add(new Claim(claimType, claim.Value, claim.ValueType)); } } } // add scopes (filter offline_access) // we use the ScopeValues collection rather than the Resources.Scopes because we support dynamic scope values // from the request, so this issues those in the token. foreach (var scope in resourceResult.RawScopeValues.Where(x => x != IdentityServerConstants.StandardScopes.OfflineAccess)) { outputClaims.Add(new Claim(JwtClaimTypes.Scope, scope)); } // a user is involved if (subject != null) { if (resourceResult.Resources.OfflineAccess) { outputClaims.Add(new Claim(JwtClaimTypes.Scope, IdentityServerConstants.StandardScopes.OfflineAccess)); } Logger.LogDebug("Getting claims for access token for subject: {subject}", subject.GetSubjectId()); outputClaims.AddRange(GetStandardSubjectClaims(subject)); outputClaims.AddRange(GetOptionalClaims(subject)); // fetch all resource claims that need to go into the access token var additionalClaimTypes = new List <string>(); foreach (var api in resourceResult.Resources.ApiResources) { // add claims configured on api resource if (api.UserClaims != null) { foreach (var claim in api.UserClaims) { additionalClaimTypes.Add(claim); } } } foreach (var scope in resourceResult.Resources.ApiScopes) { // add claims configured on scopes if (scope.UserClaims != null) { foreach (var claim in scope.UserClaims) { additionalClaimTypes.Add(claim); } } } // filter so we don't ask for claim types that we will eventually filter out additionalClaimTypes = FilterRequestedClaimTypes(additionalClaimTypes).ToList(); var context = new ProfileDataRequestContext( subject, request.Client, IdentityServerConstants.ProfileDataCallers.ClaimsProviderAccessToken, additionalClaimTypes.Distinct()) { RequestedResources = resourceResult, ValidatedRequest = request }; await Profile.GetProfileDataAsync(context); var claims = FilterProtocolClaims(context.IssuedClaims); if (claims != null) { outputClaims.AddRange(claims); } } return(outputClaims); }