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);
        }
Beispiel #3
0
        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);
        }
Beispiel #5
0
        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;
        }
Beispiel #6
0
        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));
        }
Beispiel #8
0
        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"));
        }
Beispiel #11
0
        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);
        }
Beispiel #12
0
    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" });
        }
    }
Beispiel #13
0
        /// <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);
        }
Beispiel #14
0
        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);
        }
Beispiel #15
0
 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()));
 }
Beispiel #18
0
    /// <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);
    }
Beispiel #19
0
    /// <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);
                }
            }
        }
    }
Beispiel #20
0
        public override async Task <IEnumerable <Claim> > GetAccessTokenClaimsAsync(ClaimsPrincipal subject, ResourceValidationResult resourceResult, ValidatedRequest request)
        {
            var s = await base.GetAccessTokenClaimsAsync(subject, resourceResult, request);

            return(s);
        }
Beispiel #21
0
        /// <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);
        }
Beispiel #22
0
        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()));
 }
Beispiel #24
0
        /// <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);
        }