public Task <ResourceValidationResult> ValidateRequestedResourcesAsync(ResourceValidationRequest request) { var resource = new IdentityServer4.Models.Resources(); resource.OfflineAccess = true; resource.IdentityResources.Add(new IdentityServer4.Models.IdentityResource { Name = "openid", DisplayName = "openid" }); resource.IdentityResources.Add(new IdentityServer4.Models.IdentityResource { Name = "profile", DisplayName = "profile" }); resource.ApiResources.Add(new IdentityServer4.Models.ApiResource { Name = "PagetechsLMS.WebAndApiAPI", DisplayName = "PagetechsLMS.WebAndApiAPI" }); resource.ApiScopes.Add(new IdentityServer4.Models.ApiScope { Name = "PagetechsLMS.WebAndApiAPI", DisplayName = "PagetechsLMS.WebAndApiAPI" }); var result = new ResourceValidationResult(resource); return(Task.FromResult(result)); }
public virtual async Task <ResourceValidationResult> ValidateRequestedResourcesAsync(ResourceValidationRequest request) { if (request == null) { throw new ArgumentNullException("request"); } ParsedScopesResult parsedScopesResult = _scopeParser.ParseScopeValues(request.Scopes); ResourceValidationResult result = new ResourceValidationResult(); if (!parsedScopesResult.Succeeded) { foreach (ParsedScopeValidationError error in parsedScopesResult.Errors) { _logger.LogError("Invalid parsed scope {scope}, message: {error}", error.RawValue, error.Error); result.InvalidScopes.Add(error.RawValue); } return(result); } string[] scopeNames = parsedScopesResult.ParsedScopes.Select((ParsedScopeValue x) => x.ParsedName).Distinct().ToArray(); Resources resourcesFromStore = await _store.FindEnabledResourcesByScopeAsync(scopeNames); foreach (ParsedScopeValue parsedScope in parsedScopesResult.ParsedScopes) { await ValidateScopeAsync(request.Client, resourcesFromStore, parsedScope, result); } if (result.InvalidScopes.Count > 0) { result.Resources.IdentityResources.Clear(); result.Resources.ApiResources.Clear(); result.Resources.ApiScopes.Clear(); result.ParsedScopes.Clear(); } return(result); }
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 the collection of scope values that are required. /// </summary> /// <param name="resourceValidationResult"></param> /// <returns></returns> public static IEnumerable <string> GetRequiredScopeValues(this ResourceValidationResult resourceValidationResult) { var names = resourceValidationResult.Resources.IdentityResources.Where(x => x.Required).Select(x => x.Name).ToList(); names.AddRange(resourceValidationResult.Resources.ApiScopes.Where(x => x.Required).Select(x => x.Name)); var values = resourceValidationResult.ParsedScopes.Where(x => names.Contains(x.ParsedName)).Select(x => x.RawValue); return(values); }
private async Task SetValidationResponse( HttpActionContext actionContext, CancellationToken cancellationToken, ValidationErrorDetails validationErrorDetails) { var result = new ResourceValidationResult(actionContext.Request, validationErrorDetails); var response = await result.ExecuteAsync(cancellationToken); actionContext.Response = response; }
public async Task GetAccessTokenClaimsAsync_should_contain_parameterized_scope_values() { _resources.ApiScopes.Add(new ApiScope("api")); var resourceResult = new ResourceValidationResult() { Resources = _resources, ParsedScopes = { new ParsedScopeValue("api:123", "api", "123") } }; var claims = await _subject.GetAccessTokenClaimsAsync(_user, resourceResult, _validatedRequest); var scopes = claims.Where(x => x.Type == JwtClaimTypes.Scope).Select(x => x.Value); scopes.Count().Should().Be(1); scopes.ToArray().Should().BeEquivalentTo(new string[] { "api:123" }); }
protected override Task <IEnumerable <string> > GetRequestedClaimTypesAsync(ResourceValidationResult resourceValidationResult) { IEnumerable <string> result = null; if (resourceValidationResult == null) { result = Enumerable.Empty <string>(); } else { var identityResources = resourceValidationResult.Resources.IdentityResources; result = identityResources.SelectMany(x => x.UserClaims).Distinct(); } return(Task.FromResult(result)); }
public async Task GetAccessTokenClaimsAsync_should_only_consider_parsed_scope_values_and_not_ApiScope() { // arguably, if this situation arises, then the ResourceValidationResult was not populated properly // with ParsedScopes matching ApiScopes _resources.ApiScopes.Add(new ApiScope("api1")); var resourceResult = new ResourceValidationResult() { Resources = _resources, ParsedScopes = { new ParsedScopeValue("api2") } }; var claims = await _subject.GetAccessTokenClaimsAsync(_user, resourceResult, _validatedRequest); var scopes = claims.Where(x => x.Type == JwtClaimTypes.Scope).Select(x => x.Value); scopes.Count().Should().Be(1); scopes.ToArray().Should().BeEquivalentTo(new string[] { "api2" }); }
/// <summary> /// Gets the identity resources from the scopes. /// </summary> /// <param name="scopes"></param> /// <returns></returns> protected internal virtual async Task <ResourceValidationResult> GetRequestedResourcesAsync(IEnumerable <string> scopes) { if (scopes == null || !scopes.Any()) { return(null); } var scopeString = string.Join(" ", scopes); Logger.LogDebug("Scopes in access token: {scopes}", scopeString); // if we ever parameterize identity scopes, then we would need to invoke the resource validator's parse API here var identityResources = await Resources.FindEnabledIdentityResourcesByScopeAsync(scopes); var resources = new Resources(identityResources, Enumerable.Empty <ApiResource>(), Enumerable.Empty <ApiScope>()); var result = new ResourceValidationResult(resources); return(result); }
public DefaultIdentityServerInteractionServiceTests() { _mockMockHttpContextAccessor = new MockHttpContextAccessor(_options, _mockUserSession, _mockEndSessionStore); _subject = new DefaultIdentityServerInteractionService(new StubClock(), _mockMockHttpContextAccessor, _mockLogoutMessageStore, _mockErrorMessageStore, _mockConsentStore, _mockPersistedGrantService, _mockUserSession, _mockReturnUrlParser, TestLogger.Create <DefaultIdentityServerInteractionService>() ); _resourceValidationResult = new ResourceValidationResult(); _resourceValidationResult.Resources.IdentityResources.Add(new IdentityResources.OpenId()); _resourceValidationResult.ParsedScopes.Add(new ParsedScopeValue("openid")); }
public static IEnumerable <ScopeModel> GetApiScopeModelsFromRequest(ResourceValidationResult validatedResources, string[] scopesConsented, bool check) { var apiScopes = new List <ScopeModel>(); foreach (var parsedScope in validatedResources.ParsedScopes) { var apiScope = validatedResources.Resources.FindApiScope(parsedScope.ParsedName); if (apiScope != null) { var scopeVm = ScopeModel.CreateScopeViewModel(parsedScope, apiScope, scopesConsented.Contains(parsedScope.RawValue) || check); apiScopes.Add(scopeVm); } } if (ConsentOptions.EnableOfflineAccess && validatedResources.Resources.OfflineAccess) { apiScopes.Add(ScopeModel.GetOfflineAccessScope( scopesConsented.Contains(IdentityServerConstants.StandardScopes.OfflineAccess) || check)); } return(apiScopes); }
public void FilterByResourceIndicator_should_filter_properly() { var resources = new Resources( new IdentityResource[] { new IdentityResources.OpenId(), new IdentityResources.Profile(), }, new ApiResource[] { new ApiResource("resource1") { Scopes = { "scope1", "scope2" } }, new ApiResource("resource2") { Scopes = { "scope1" } }, new ApiResource("isolated1") { Scopes = { "scope3", "scope2" }, RequireResourceIndicator = true }, new ApiResource("isolated2") { Scopes = { "scope3" }, RequireResourceIndicator = true }, }, new ApiScope[] { new ApiScope("scope1"), new ApiScope("scope2"), new ApiScope("scope3"), }); var subject = new ResourceValidationResult(resources); { var result = subject.FilterByResourceIndicator(null); result.Resources.ApiResources.Select(x => x.Name).Should().BeEquivalentTo(new[] { "resource1", "resource2" }); result.Resources.ApiScopes.Select(x => x.Name).Should().BeEquivalentTo(new[] { "scope1", "scope2", "scope3" }); result.Resources.OfflineAccess.Should().BeFalse(); } { resources.OfflineAccess = true; var result = subject.FilterByResourceIndicator(null); result.Resources.ApiResources.Select(x => x.Name).Should().BeEquivalentTo(new[] { "resource1", "resource2" }); result.Resources.ApiScopes.Select(x => x.Name).Should().BeEquivalentTo(new[] { "scope1", "scope2", "scope3" }); result.Resources.OfflineAccess.Should().BeTrue(); } { var result = subject.FilterByResourceIndicator("resource1"); result.Resources.ApiResources.Select(x => x.Name).Should().BeEquivalentTo(new[] { "resource1" }); result.Resources.ApiScopes.Select(x => x.Name).Should().BeEquivalentTo(new[] { "scope1", "scope2" }); } { var result = subject.FilterByResourceIndicator("resource2"); result.Resources.ApiResources.Select(x => x.Name).Should().BeEquivalentTo(new[] { "resource2" }); result.Resources.ApiScopes.Select(x => x.Name).Should().BeEquivalentTo(new[] { "scope1" }); } { var result = subject.FilterByResourceIndicator("isolated1"); result.Resources.ApiResources.Select(x => x.Name).Should().BeEquivalentTo(new[] { "isolated1" }); result.Resources.ApiScopes.Select(x => x.Name).Should().BeEquivalentTo(new[] { "scope2", "scope3" }); } { var result = subject.FilterByResourceIndicator("isolated2"); result.Resources.ApiResources.Select(x => x.Name).Should().BeEquivalentTo(new[] { "isolated2" }); result.Resources.ApiScopes.Select(x => x.Name).Should().BeEquivalentTo(new[] { "scope3" }); } }
/// <summary> /// Returns claims for an access token. /// </summary> /// <param name="subject">The subject.</param> /// <param name="resourceResult"></param> /// <param name="request">The raw request.</param> /// <returns> /// Claims for the access token /// </returns> public override async Task <IEnumerable <Claim> > GetAccessTokenClaimsAsync( ClaimsPrincipal subject, ResourceValidationResult resourceResult, ValidatedRequest request) { var outputClaims = (await base.GetAccessTokenClaimsAsync(subject, resourceResult, request)).ToList(); Logger.LogDebug( $"GetAccessTokenClaimsAsync. Subject:{subject != null} Incoming claims: {string.Join(',', outputClaims.Select(x => $"{x.Type}:{x.Value}"))}"); // a user is involved if (subject != null) { Logger.LogDebug( $"GetAccessTokenClaimsAsync. Subject claims: {string.Join(',', subject.Claims.Select(x => $"{x.Type}:{x.Value}"))}"); 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 res in resourceResult.Resources.IdentityResources) { // add claims configured on scope if (res.UserClaims != null) { foreach (var claim in res.UserClaims) { additionalClaimTypes.Add(claim); } } } // add sid if present and requested if (request.SessionId.IsPresent() && additionalClaimTypes.Contains(JwtClaimTypes.SessionId)) { outputClaims.Add(new Claim(JwtClaimTypes.SessionId, request.SessionId)); } var tenantId = (subject.Claims.FirstOrDefault(_ => _.Type == Constants.TenantIdClaimType) ?? outputClaims.Find(_ => _.Type == Constants.TenantIdClaimType))?.Value; if (tenantId != null) { //var tenantInfo = await _tenantService.GetTenantInfoAsync(tenantId); //var availableModules = tenantInfo?.Subscription?.Modules?.ToArray() ?? new string[] { }; //_logger.LogDebug($"availableModules for tenant: {string.Join(',', availableModules)}"); //if (request.Client is NativeAppClient && availableModules.Length > 0) //{ // availableModules = await _securityService.FilterAvailableModulesAsync(tenantInfo, // _users.FindBySubjectId(subject.GetSubjectId(), tenantId)); //} //foreach (var module in availableModules) //{ // outputClaims.Add(new Claim(Constants.SupportedModuleClaimType, module)); //} } } //if (request.Secret.Type == "NoSecret") if ((request as ValidatedTokenRequest)?.GrantType != "client_credentials") { //filter out server-side-only scopes here } if (outputClaims.All(c => c.Type != JwtClaimTypes.IdentityProvider)) { outputClaims.Add(new Claim(JwtClaimTypes.IdentityProvider, Constants.IdpName)); } //_logger.LogInformation($"GetAccessTokenClaimsAsync called. {request.}"); Logger.LogDebug( $"GetAccessTokenClaimsAsync. Subject:{subject != null} Outcoming claims: {string.Join(',', outputClaims.Select(x => $"{x.Type}:{x.Value}"))}"); return(outputClaims); }
public override async Task <IEnumerable <Claim> > GetIdentityTokenClaimsAsync(ClaimsPrincipal subject, ResourceValidationResult resourceValidationResult, bool includeAllIdentityClaims, ValidatedRequest request) { var rcf = _context.HttpContext.Features.Get <IRequestCultureFeature>(); var outputClaims = (await base.GetIdentityTokenClaimsAsync(subject, resourceValidationResult, includeAllIdentityClaims, request)).ToList(); //if (rcf.Provider != null) //{ //// outputClaims.RemoveAll(_ => _.Type == JwtClaimTypes.Locale); //// outputClaims.Add(new Claim(JwtClaimTypes.Locale, //// LocaleTransformer.Ensure5SymbolCulture(rcf.RequestCulture.Culture).TextInfo.CultureName)); // _logger.LogInformation( // $"Adding Locale={LocaleTransformer.Ensure5SymbolCulture(rcf.RequestCulture.Culture).TextInfo.CultureName} from IRequestCultureFeature to IdentityToken.OutputClaims collection {subject?.Identity.Name}"); //} //else if (subject?.HasClaim(_ => _.Type == JwtClaimTypes.Locale) == true) //{ // outputClaims.RemoveAll(_ => _.Type == JwtClaimTypes.Locale); // outputClaims.Add(subject.FindFirst(_ => _.Type == JwtClaimTypes.Locale)); // _logger.LogInformation( // $"Adding Locale={subject.FindFirst(_ => _.Type == JwtClaimTypes.Locale)} from Identity toIdentityToken.OutputClaims collection {subject.Identity.Name}"); //} //else if (request.ClientClaims.Any(_ => _.Type == JwtClaimTypes.Locale)) //{ // outputClaims.RemoveAll(_ => _.Type == JwtClaimTypes.Locale); // outputClaims.Add(request.ClientClaims.First(_ => _.Type == JwtClaimTypes.Locale)); // _logger.LogInformation( // $"Adding Locale={request.ClientClaims.First(_ => _.Type == JwtClaimTypes.Locale)} from ClientClaims to IdentityToken.OutputClaims collection {subject?.Identity.Name}"); //} //else //{ // _logger.LogInformation($"No IRequestCultureFeature.Provider nor Request.ClientClaims of type Locale found. {subject?.Identity.Name}"); //} //localization.Value.SupportedCultures outputClaims.Add(new Claim(Constants.TenantIdClaimType, "devstable")); outputClaims.Add(new Claim("userid", "123456")); return(outputClaims); }
public static IEnumerable <ScopeModel> GetIdentityResourcesFromRequest(ResourceValidationResult validatedResources, string[] scopesConsented, bool check) { return(validatedResources.Resources.IdentityResources.Select(x => ScopeModel.CreateScopeViewModel(x, scopesConsented.Contains(x.Name) || check)).ToArray()); }
protected virtual async Task ValidateScopeAsync(Client client, Resources resourcesFromStore, ParsedScopeValue requestedScope, ResourceValidationResult result) { if (requestedScope.ParsedName == "offline_access") { if (await IsClientAllowedOfflineAccessAsync(client)) { result.Resources.OfflineAccess = true; result.ParsedScopes.Add(new ParsedScopeValue("offline_access")); } else { result.InvalidScopes.Add("offline_access"); } return; } IdentityResource identity = resourcesFromStore.FindIdentityResourcesByScope(requestedScope.ParsedName); if (identity != null) { if (await IsClientAllowedIdentityResourceAsync(client, identity)) { result.ParsedScopes.Add(requestedScope); result.Resources.IdentityResources.Add(identity); } else { result.InvalidScopes.Add(requestedScope.RawValue); } return; } ApiScope apiScope = resourcesFromStore.FindApiScope(requestedScope.ParsedName); if (apiScope != null) { if (await IsClientAllowedApiScopeAsync(client, apiScope)) { result.ParsedScopes.Add(requestedScope); result.Resources.ApiScopes.Add(apiScope); foreach (ApiResource item in resourcesFromStore.FindApiResourcesByScope(apiScope.Name)) { result.Resources.ApiResources.Add(item); } } else { result.InvalidScopes.Add(requestedScope.RawValue); } } else { _logger.LogError("Scope {scope} not found in store.", requestedScope.ParsedName); result.InvalidScopes.Add(requestedScope.RawValue); } }
public Task <IEnumerable <Claim> > GetIdentityTokenClaimsAsync(ClaimsPrincipal subject, ResourceValidationResult resources, bool includeAllIdentityClaims, ValidatedRequest request) { return(Task.FromResult(IdentityTokenClaims.AsEnumerable())); }
/// <inheritdoc/> public virtual async Task <ResourceValidationResult> ValidateRequestedResourcesAsync(ResourceValidationRequest request) { using var activity = Tracing.ValidationActivitySource.StartActivity("DefaultResourceValidator.ValidateRequestedResources"); activity?.SetTag(Tracing.Properties.Scope, request.Scopes.ToSpaceSeparatedString()); activity?.SetTag(Tracing.Properties.Resource, request.ResourceIndicators.ToSpaceSeparatedString()); if (request == null) { throw new ArgumentNullException(nameof(request)); } var result = new ResourceValidationResult(); var parsedScopesResult = _scopeParser.ParseScopeValues(request.Scopes); if (!parsedScopesResult.Succeeded) { foreach (var invalidScope in parsedScopesResult.Errors) { _logger.LogError("Invalid parsed scope {scope}, message: {error}", invalidScope.RawValue, invalidScope.Error); result.InvalidScopes.Add(invalidScope.RawValue); } return(result); } var scopeNames = parsedScopesResult.ParsedScopes.Select(x => x.ParsedName).Distinct().ToArray(); // todo: this API might want to pass resource indicators to better filter var scopeResourcesFromStore = await _store.FindEnabledResourcesByScopeAsync(scopeNames); if (request.ResourceIndicators?.Any() == true) { // remove isolated API resources not included in the requested resource indicators scopeResourcesFromStore.ApiResources = scopeResourcesFromStore.ApiResources .Where(x => // only allow non-isolated resources if the request could produce multiple access // tokens. this will happen if the request is for a RT, so check for offline_access (request.IncludeNonIsolatedApiResources && !x.RequireResourceIndicator) || request.ResourceIndicators.Contains(x.Name)) .ToHashSet(); if (!request.IncludeNonIsolatedApiResources) { // filter API scopes that don't match the resources requested var allResourceScopes = scopeResourcesFromStore.ApiResources.SelectMany(x => x.Scopes).ToArray(); scopeResourcesFromStore.ApiScopes = scopeResourcesFromStore.ApiScopes.Where(x => allResourceScopes.Contains(x.Name)) .ToHashSet(); } // find requested resource indicators not matched by scope var matchedApiResourceNames = scopeResourcesFromStore.ApiResources.Select(x => x.Name).ToArray(); var invalidRequestedResourceIndicators = request.ResourceIndicators.Except(matchedApiResourceNames); if (invalidRequestedResourceIndicators.Any()) { foreach (var invalid in invalidRequestedResourceIndicators) { _logger.LogError("Invalid resource identifier {resource}. It is either not found, not enabled, or does not support any of the requested scopes.", invalid); result.InvalidResourceIndicators.Add(invalid); } return(result); } } else { // no resource indicators, so filter all API resources marked as isolated scopeResourcesFromStore.ApiResources = scopeResourcesFromStore.ApiResources.Where(x => !x.RequireResourceIndicator).ToHashSet(); } foreach (var scope in parsedScopesResult.ParsedScopes) { await ValidateScopeAsync(request.Client, scopeResourcesFromStore, scope, result); } if (result.InvalidScopes.Count > 0 || result.InvalidResourceIndicators.Count > 0) { result.Resources.IdentityResources.Clear(); result.Resources.ApiResources.Clear(); result.Resources.ApiScopes.Clear(); result.ParsedScopes.Clear(); } return(result); }
/// <summary> /// Validates that the requested scopes is contained in the store, and the client is allowed to request it. /// </summary> /// <param name="client"></param> /// <param name="resourcesFromStore"></param> /// <param name="requestedScope"></param> /// <param name="result"></param> /// <returns></returns> protected virtual async Task ValidateScopeAsync( Client client, Resources resourcesFromStore, ParsedScopeValue requestedScope, ResourceValidationResult result) { if (requestedScope.ParsedName == IdentityServerConstants.StandardScopes.OfflineAccess) { if (await IsClientAllowedOfflineAccessAsync(client)) { result.Resources.OfflineAccess = true; result.ParsedScopes.Add(new ParsedScopeValue(IdentityServerConstants.StandardScopes.OfflineAccess)); } else { result.InvalidScopes.Add(IdentityServerConstants.StandardScopes.OfflineAccess); } } else { var identity = resourcesFromStore.FindIdentityResourcesByScope(requestedScope.ParsedName); if (identity != null) { if (await IsClientAllowedIdentityResourceAsync(client, identity)) { result.ParsedScopes.Add(requestedScope); result.Resources.IdentityResources.Add(identity); } else { result.InvalidScopes.Add(requestedScope.RawValue); } } else { var apiScope = resourcesFromStore.FindApiScope(requestedScope.ParsedName); if (apiScope != null) { if (await IsClientAllowedApiScopeAsync(client, apiScope)) { result.ParsedScopes.Add(requestedScope); result.Resources.ApiScopes.Add(apiScope); var apis = resourcesFromStore.FindApiResourcesByScope(apiScope.Name); foreach (var api in apis) { result.Resources.ApiResources.Add(api); } } else { result.InvalidScopes.Add(requestedScope.RawValue); } } else { _logger.LogError("Scope {scope} not found in store or not supported by requested resource indicators.", requestedScope.ParsedName); result.InvalidScopes.Add(requestedScope.RawValue); } } } }
public override async Task <IEnumerable <Claim> > GetAccessTokenClaimsAsync(ClaimsPrincipal subject, ResourceValidationResult resourceResult, ValidatedRequest request) { var s = await base.GetAccessTokenClaimsAsync(subject, resourceResult, request); return(s); }
/// <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, ResourceValidationResult 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.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) { RequestedResources = request.ValidatedResources, ValidatedRequest = request }; 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); }
public async Task <IEndpointResult> ProcessAsync(HttpContext httpContext) { Logger.LogInformation($"[{nameof(InitRegistrationEndpoint)}] Started processing trusted device registration initiation endpoint."); var isPostRequest = HttpMethods.IsPost(httpContext.Request.Method); var isApplicationFormContentType = httpContext.Request.HasApplicationFormContentType(); // Validate HTTP request type and method. if (!isPostRequest || !isApplicationFormContentType) { return(Error(OidcConstants.TokenErrors.InvalidRequest, "Request must be of type 'POST' and have a Content-Type equal to 'application/x-www-form-urlencoded'.")); } // Ensure that a valid 'Authorization' header exists. var tokenUsageResult = await Token.Validate(httpContext); if (!tokenUsageResult.TokenFound) { return(Error(OidcConstants.ProtectedResourceErrors.InvalidToken, "No access token is present in the request.")); } // Validate request data and access token. var parameters = (await httpContext.Request.ReadFormAsync()).AsNameValueCollection(); var requestValidationResult = await Request.Validate(parameters, tokenUsageResult.Token); if (requestValidationResult.IsError) { return(Error(requestValidationResult.Error, requestValidationResult.ErrorDescription)); } // Ensure device is not already registered or belongs to any other user. var existingDevice = await UserDeviceStore.GetByDeviceId(requestValidationResult.DeviceId); var isNewDeviceOrOwnedByUser = existingDevice == null || existingDevice.UserId.Equals(requestValidationResult.UserId, StringComparison.OrdinalIgnoreCase); if (!isNewDeviceOrOwnedByUser) { return(Error(OidcConstants.ProtectedResourceErrors.InvalidToken, "Device does not belong to the this user.")); } // Ensure that the principal has declared a phone number which is also confirmed. // We will get these 2 claims by retrieving the identity resources from the store (using the requested scopes existing in the access token) and then calling the profile service. // This will help us make sure that the 'phone' scope was requested and finally allowed in the token endpoint. var identityResources = await ResourceStore.FindEnabledIdentityResourcesByScopeAsync(requestValidationResult.RequestedScopes); var resources = new Resources(identityResources, Enumerable.Empty <ApiResource>(), Enumerable.Empty <ApiScope>()); var validatedResources = new ResourceValidationResult(resources); if (!validatedResources.Succeeded) { return(Error(OidcConstants.ProtectedResourceErrors.InvalidToken, "Identity resources could be validated.")); } var requestedClaimTypes = resources.IdentityResources.SelectMany(x => x.UserClaims).Distinct(); var profileDataRequestContext = new ProfileDataRequestContext(requestValidationResult.Principal, requestValidationResult.Client, IdentityServerConstants.ProfileDataCallers.UserInfoEndpoint, requestedClaimTypes) { RequestedResources = validatedResources }; await ProfileService.GetProfileDataAsync(profileDataRequestContext); var profileClaims = profileDataRequestContext.IssuedClaims; var phoneNumberClaim = profileClaims.FirstOrDefault(x => x.Type == JwtClaimTypes.PhoneNumber); var phoneNumberVerifiedClaim = profileClaims.FirstOrDefault(x => x.Type == JwtClaimTypes.PhoneNumberVerified); if (string.IsNullOrWhiteSpace(phoneNumberClaim?.Value) || phoneNumberVerifiedClaim == null || (bool.TryParse(phoneNumberVerifiedClaim.Value, out var phoneNumberVerified) && !phoneNumberVerified)) { return(Error(OidcConstants.ProtectedResourceErrors.InvalidToken, "User does not have a phone number or the phone number is not verified.")); } var otpAuthenticatedValue = profileClaims.FirstOrDefault(x => x.Type == BasicClaimTypes.OtpAuthenticated)?.Value; var otpAuthenticated = !string.IsNullOrWhiteSpace(otpAuthenticatedValue) && bool.Parse(otpAuthenticatedValue); if (!otpAuthenticated) { // Send OTP code. void messageBuilder(TotpMessageBuilder message) { var builder = message.UsePrincipal(requestValidationResult.Principal).WithMessage(IdentityMessageDescriber.DeviceRegistrationCodeMessage(existingDevice?.Name, requestValidationResult.InteractionMode)); if (requestValidationResult.DeliveryChannel == TotpDeliveryChannel.Sms) { builder.UsingSms(); } else { builder.UsingViber(); } builder.WithPurpose(Constants.TrustedDeviceOtpPurpose(requestValidationResult.UserId, requestValidationResult.DeviceId)); } var totpResult = await TotpService.Send(messageBuilder); if (!totpResult.Success) { return(Error(totpResult.Error)); } } // Create endpoint response. var response = await Response.Generate(requestValidationResult); Logger.LogInformation($"[{nameof(InitRegistrationEndpoint)}] Trusted device registration initiation endpoint success."); return(new InitRegistrationResult(response)); }
public Task <IEnumerable <Claim> > GetAccessTokenClaimsAsync(ClaimsPrincipal subject, ResourceValidationResult resources, ValidatedRequest request) { return(Task.FromResult(AccessTokenClaims.AsEnumerable())); }
/// <summary> /// Returns claims for an identity 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); }