private async Task <TokenRequestValidationResult> ValidateCibaRequestRequestAsync(NameValueCollection parameters)
    {
        _logger.LogDebug("Start validation of CIBA request");

        /////////////////////////////////////////////
        // check if client is authorized for grant type
        /////////////////////////////////////////////
        if (!_validatedRequest.Client.AllowedGrantTypes.ToList().Contains(OidcConstants.GrantTypes.Ciba))
        {
            LogError("Client not authorized for CIBA flow");
            return(Invalid(OidcConstants.TokenErrors.UnauthorizedClient));
        }

        LicenseValidator.ValidateCiba();

        /////////////////////////////////////////////
        // validate authentication request id parameter
        /////////////////////////////////////////////
        var authRequestId = parameters.Get(OidcConstants.TokenRequest.AuthenticationRequestId);

        if (authRequestId.IsMissing())
        {
            LogError("Authentication request id is missing");
            return(Invalid(OidcConstants.TokenErrors.InvalidRequest));
        }

        if (authRequestId.Length > _options.InputLengthRestrictions.AuthenticationRequestId)
        {
            LogError("Authentication request id too long");
            return(Invalid(OidcConstants.TokenErrors.InvalidGrant));
        }

        /////////////////////////////////////////////
        // validate authentication request id
        /////////////////////////////////////////////
        var validationContext = new BackchannelAuthenticationRequestIdValidationContext
        {
            AuthenticationRequestId = authRequestId,
            Request = _validatedRequest
        };
        await _backchannelAuthenticationRequestIdValidator.ValidateAsync(validationContext);

        if (validationContext.Result.IsError)
        {
            return(validationContext.Result);
        }

        //////////////////////////////////////////////////////////
        // resource indicator
        //////////////////////////////////////////////////////////
        if (_validatedRequest.RequestedResourceIndicator != null &&
            _validatedRequest.BackChannelAuthenticationRequest.RequestedResourceIndicators?.Any() == true &&
            !_validatedRequest.BackChannelAuthenticationRequest.RequestedResourceIndicators.Contains(_validatedRequest.RequestedResourceIndicator))
        {
            return(Invalid(OidcConstants.AuthorizeErrors.InvalidTarget, "Resource indicator does not match any resource indicator in the original backchannel authentication request."));
        }

        //////////////////////////////////////////////////////////
        // resource and scope validation
        //////////////////////////////////////////////////////////
        var validatedResources = await _resourceValidator.ValidateRequestedResourcesAsync(new ResourceValidationRequest
        {
            Client             = _validatedRequest.Client,
            Scopes             = _validatedRequest.BackChannelAuthenticationRequest.AuthorizedScopes,
            ResourceIndicators = _validatedRequest.BackChannelAuthenticationRequest.RequestedResourceIndicators,
            // if we are issuing a refresh token, then we need to allow the non-isolated resource
            IncludeNonIsolatedApiResources = _validatedRequest.BackChannelAuthenticationRequest.RequestedScopes.Contains(OidcConstants.StandardScopes.OfflineAccess)
        });

        if (!validatedResources.Succeeded)
        {
            if (validatedResources.InvalidResourceIndicators.Any())
            {
                return(Invalid(OidcConstants.AuthorizeErrors.InvalidTarget, "Invalid resource indicator."));
            }
            if (validatedResources.InvalidScopes.Any())
            {
                return(Invalid(OidcConstants.AuthorizeErrors.InvalidScope, "Invalid scope."));
            }
        }

        LicenseValidator.ValidateResourceIndicators(_validatedRequest.RequestedResourceIndicator);
        _validatedRequest.ValidatedResources = validatedResources.FilterByResourceIndicator(_validatedRequest.RequestedResourceIndicator);


        _logger.LogDebug("Validation of CIBA token request success");

        return(Valid());
    }
    // todo: do we want to rework the semantics of these ignore params?
    // also seems like other workflows other than CC clients can omit scopes?
    private async Task <string> ValidateRequestedScopesAndResourcesAsync(NameValueCollection parameters, bool ignoreImplicitIdentityScopes = false, bool ignoreImplicitOfflineAccess = false)
    {
        var scopes = parameters.Get(OidcConstants.TokenRequest.Scope);

        if (scopes.IsMissing())
        {
            _logger.LogTrace("Client provided no scopes - checking allowed scopes list");

            if (!_validatedRequest.Client.AllowedScopes.IsNullOrEmpty())
            {
                // this finds all the scopes the client is allowed to access
                var clientAllowedScopes = new List <string>();
                if (!ignoreImplicitIdentityScopes)
                {
                    var resources = await _resourceStore.FindResourcesByScopeAsync(_validatedRequest.Client.AllowedScopes);

                    clientAllowedScopes.AddRange(resources.ToScopeNames().Where(x => _validatedRequest.Client.AllowedScopes.Contains(x)));
                }
                else
                {
                    var apiScopes = await _resourceStore.FindApiScopesByNameAsync(_validatedRequest.Client.AllowedScopes);

                    clientAllowedScopes.AddRange(apiScopes.Select(x => x.Name));
                }

                if (!ignoreImplicitOfflineAccess)
                {
                    if (_validatedRequest.Client.AllowOfflineAccess)
                    {
                        clientAllowedScopes.Add(IdentityServerConstants.StandardScopes.OfflineAccess);
                    }
                }

                scopes = clientAllowedScopes.Distinct().ToSpaceSeparatedString();
                _logger.LogTrace("Defaulting to: {scopes}", scopes);
            }
            else
            {
                LogError("No allowed scopes configured for client", new { clientId = _validatedRequest.Client.ClientId });
                return(OidcConstants.TokenErrors.InvalidScope);
            }
        }

        if (scopes.Length > _options.InputLengthRestrictions.Scope)
        {
            LogError("Scope parameter exceeds max allowed length");
            return(OidcConstants.TokenErrors.InvalidScope);
        }

        var requestedScopes = scopes.ParseScopesString();

        if (requestedScopes == null)
        {
            LogError("No scopes found in request");
            return(OidcConstants.TokenErrors.InvalidScope);
        }


        var resourceIndicators = _validatedRequest.RequestedResourceIndicator == null?
                                 Enumerable.Empty <string>() :
                                     new[] { _validatedRequest.RequestedResourceIndicator };

        var resourceValidationResult = await _resourceValidator.ValidateRequestedResourcesAsync(new ResourceValidationRequest {
            Client             = _validatedRequest.Client,
            Scopes             = requestedScopes,
            ResourceIndicators = resourceIndicators,
            // if the client is passing explicit scopes, we want to exclude the non-isolated resource scenaio from validation
            IncludeNonIsolatedApiResources = parameters.Get(OidcConstants.TokenRequest.Scope).IsMissing()
        });

        if (!resourceValidationResult.Succeeded)
        {
            if (resourceValidationResult.InvalidResourceIndicators.Any())
            {
                LogError("Invalid resource indicator");
                return(OidcConstants.TokenErrors.InvalidTarget);
            }

            if (resourceValidationResult.InvalidScopes.Any())
            {
                LogError("Invalid scopes requested");
            }
            else
            {
                LogError("Invalid scopes for client requested");
            }

            return(OidcConstants.TokenErrors.InvalidScope);
        }

        _validatedRequest.RequestedScopes = requestedScopes;

        LicenseValidator.ValidateResourceIndicators(_validatedRequest.RequestedResourceIndicator);
        _validatedRequest.ValidatedResources = resourceValidationResult.FilterByResourceIndicator(_validatedRequest.RequestedResourceIndicator);

        return(null);
    }
    private async Task <TokenRequestValidationResult> ValidateDeviceCodeRequestAsync(NameValueCollection parameters)
    {
        _logger.LogDebug("Start validation of device code request");

        /////////////////////////////////////////////
        // resource indicator not supported for device flow
        /////////////////////////////////////////////
        if (_validatedRequest.RequestedResourceIndicator != null)
        {
            LogError("Resource indicators not supported for device flow");
            return(Invalid(OidcConstants.TokenErrors.InvalidTarget));
        }

        /////////////////////////////////////////////
        // check if client is authorized for grant type
        /////////////////////////////////////////////
        if (!_validatedRequest.Client.AllowedGrantTypes.ToList().Contains(GrantType.DeviceFlow))
        {
            LogError("Client not authorized for device flow");
            return(Invalid(OidcConstants.TokenErrors.UnauthorizedClient));
        }

        /////////////////////////////////////////////
        // validate device code parameter
        /////////////////////////////////////////////
        var deviceCode = parameters.Get(OidcConstants.TokenRequest.DeviceCode);

        if (deviceCode.IsMissing())
        {
            LogError("Device code is missing");
            return(Invalid(OidcConstants.TokenErrors.InvalidRequest));
        }

        if (deviceCode.Length > _options.InputLengthRestrictions.DeviceCode)
        {
            LogError("Device code too long");
            return(Invalid(OidcConstants.TokenErrors.InvalidGrant));
        }

        /////////////////////////////////////////////
        // validate device code
        /////////////////////////////////////////////
        var deviceCodeContext = new DeviceCodeValidationContext {
            DeviceCode = deviceCode, Request = _validatedRequest
        };
        await _deviceCodeValidator.ValidateAsync(deviceCodeContext);

        if (deviceCodeContext.Result.IsError)
        {
            return(deviceCodeContext.Result);
        }

        //////////////////////////////////////////////////////////
        // scope validation
        //////////////////////////////////////////////////////////
        var validatedResources = await _resourceValidator.ValidateRequestedResourcesAsync(new ResourceValidationRequest
        {
            Client             = _validatedRequest.Client,
            Scopes             = _validatedRequest.DeviceCode.AuthorizedScopes,
            ResourceIndicators = null // not supported for device grant
        });

        if (!validatedResources.Succeeded)
        {
            if (validatedResources.InvalidResourceIndicators.Any())
            {
                return(Invalid(OidcConstants.AuthorizeErrors.InvalidTarget, "Invalid resource indicator."));
            }
            if (validatedResources.InvalidScopes.Any())
            {
                return(Invalid(OidcConstants.AuthorizeErrors.InvalidScope, "Invalid scope."));
            }
        }

        LicenseValidator.ValidateResourceIndicators(_validatedRequest.RequestedResourceIndicator);
        _validatedRequest.ValidatedResources = validatedResources;

        _logger.LogDebug("Validation of device code token request success");

        return(Valid());
    }
    private async Task <TokenRequestValidationResult> ValidateRefreshTokenRequestAsync(NameValueCollection parameters)
    {
        _logger.LogDebug("Start validation of refresh token request");

        var refreshTokenHandle = parameters.Get(OidcConstants.TokenRequest.RefreshToken);

        if (refreshTokenHandle.IsMissing())
        {
            LogError("Refresh token is missing");
            return(Invalid(OidcConstants.TokenErrors.InvalidRequest));
        }

        if (refreshTokenHandle.Length > _options.InputLengthRestrictions.RefreshToken)
        {
            LogError("Refresh token too long");
            return(Invalid(OidcConstants.TokenErrors.InvalidGrant));
        }

        var result = await _refreshTokenService.ValidateRefreshTokenAsync(refreshTokenHandle, _validatedRequest.Client);

        if (result.IsError)
        {
            LogWarning("Refresh token validation failed. aborting");
            return(Invalid(OidcConstants.TokenErrors.InvalidGrant));
        }

        _validatedRequest.RefreshToken       = result.RefreshToken;
        _validatedRequest.RefreshTokenHandle = refreshTokenHandle;
        _validatedRequest.Subject            = result.RefreshToken.Subject;

        //////////////////////////////////////////////////////////
        // resource indicator
        //////////////////////////////////////////////////////////
        var resourceIndicators = _validatedRequest.RefreshToken.AuthorizedResourceIndicators;

        if (_validatedRequest.RefreshToken.AuthorizedResourceIndicators != null)
        {
            // we had an authorization request so check current requested resource against original list
            if (_validatedRequest.RequestedResourceIndicator != null &&
                !_validatedRequest.RefreshToken.AuthorizedResourceIndicators.Contains(_validatedRequest.RequestedResourceIndicator))
            {
                return(Invalid(OidcConstants.AuthorizeErrors.InvalidTarget, "Resource indicator does not match any resource indicator in the original authorize request."));
            }
        }
        else if (!String.IsNullOrWhiteSpace(_validatedRequest.RequestedResourceIndicator))
        {
            resourceIndicators = new[] { _validatedRequest.RequestedResourceIndicator };
        }

        //////////////////////////////////////////////////////////
        // resource and scope validation
        //////////////////////////////////////////////////////////
        var validatedResources = await _resourceValidator.ValidateRequestedResourcesAsync(new ResourceValidationRequest
        {
            Client             = _validatedRequest.Client,
            Scopes             = _validatedRequest.RefreshToken.AuthorizedScopes,
            ResourceIndicators = resourceIndicators,
            // we're issuing refresh token, so we need to allow for non-isolated resource
            IncludeNonIsolatedApiResources = true,
        });

        if (!validatedResources.Succeeded)
        {
            if (validatedResources.InvalidResourceIndicators.Any())
            {
                return(Invalid(OidcConstants.AuthorizeErrors.InvalidTarget, "Invalid resource indicator."));
            }
            if (validatedResources.InvalidScopes.Any())
            {
                return(Invalid(OidcConstants.AuthorizeErrors.InvalidScope, "Invalid scope."));
            }
        }

        LicenseValidator.ValidateResourceIndicators(_validatedRequest.RequestedResourceIndicator);
        _validatedRequest.ValidatedResources = validatedResources.FilterByResourceIndicator(_validatedRequest.RequestedResourceIndicator);

        _logger.LogDebug("Validation of refresh token request success");
        // todo: more logging - similar to TokenValidator before

        return(Valid());
    }
    private async Task <TokenRequestValidationResult> ValidateAuthorizationCodeRequestAsync(NameValueCollection parameters)
    {
        _logger.LogDebug("Start validation of authorization code token request");

        /////////////////////////////////////////////
        // check if client is authorized for grant type
        /////////////////////////////////////////////
        if (!_validatedRequest.Client.AllowedGrantTypes.ToList().Contains(GrantType.AuthorizationCode) &&
            !_validatedRequest.Client.AllowedGrantTypes.ToList().Contains(GrantType.Hybrid))
        {
            LogError("Client not authorized for code flow");
            return(Invalid(OidcConstants.TokenErrors.UnauthorizedClient));
        }

        /////////////////////////////////////////////
        // validate authorization code
        /////////////////////////////////////////////
        var code = parameters.Get(OidcConstants.TokenRequest.Code);

        if (code.IsMissing())
        {
            LogError("Authorization code is missing");
            return(Invalid(OidcConstants.TokenErrors.InvalidGrant));
        }

        if (code.Length > _options.InputLengthRestrictions.AuthorizationCode)
        {
            LogError("Authorization code is too long");
            return(Invalid(OidcConstants.TokenErrors.InvalidGrant));
        }

        _validatedRequest.AuthorizationCodeHandle = code;

        var authZcode = await _authorizationCodeStore.GetAuthorizationCodeAsync(code);

        if (authZcode == null)
        {
            LogError("Invalid authorization code", new { code });
            return(Invalid(OidcConstants.TokenErrors.InvalidGrant));
        }

        /////////////////////////////////////////////
        // validate client binding
        /////////////////////////////////////////////
        if (authZcode.ClientId != _validatedRequest.Client.ClientId)
        {
            LogError("Client is trying to use a code from a different client", new { clientId = _validatedRequest.Client.ClientId, codeClient = authZcode.ClientId });
            return(Invalid(OidcConstants.TokenErrors.InvalidGrant));
        }

        // remove code from store
        // todo: set to consumed in the future?
        await _authorizationCodeStore.RemoveAuthorizationCodeAsync(code);

        if (authZcode.CreationTime.HasExceeded(authZcode.Lifetime, _clock.UtcNow.UtcDateTime))
        {
            LogError("Authorization code expired", new { code });
            return(Invalid(OidcConstants.TokenErrors.InvalidGrant));
        }

        /////////////////////////////////////////////
        // populate session id
        /////////////////////////////////////////////
        if (authZcode.SessionId.IsPresent())
        {
            _validatedRequest.SessionId = authZcode.SessionId;
        }

        /////////////////////////////////////////////
        // validate code expiration
        /////////////////////////////////////////////
        if (authZcode.CreationTime.HasExceeded(_validatedRequest.Client.AuthorizationCodeLifetime, _clock.UtcNow.UtcDateTime))
        {
            LogError("Authorization code is expired");
            return(Invalid(OidcConstants.TokenErrors.InvalidGrant));
        }

        _validatedRequest.AuthorizationCode = authZcode;
        _validatedRequest.Subject           = authZcode.Subject;

        /////////////////////////////////////////////
        // validate redirect_uri
        /////////////////////////////////////////////
        var redirectUri = parameters.Get(OidcConstants.TokenRequest.RedirectUri);

        if (redirectUri.IsMissing())
        {
            LogError("Redirect URI is missing");
            return(Invalid(OidcConstants.TokenErrors.UnauthorizedClient));
        }

        if (redirectUri.Equals(_validatedRequest.AuthorizationCode.RedirectUri, StringComparison.Ordinal) == false)
        {
            LogError("Invalid redirect_uri", new { redirectUri, expectedRedirectUri = _validatedRequest.AuthorizationCode.RedirectUri });
            return(Invalid(OidcConstants.TokenErrors.InvalidGrant));
        }

        /////////////////////////////////////////////
        // validate scopes are present
        /////////////////////////////////////////////
        if (_validatedRequest.AuthorizationCode.RequestedScopes == null ||
            !_validatedRequest.AuthorizationCode.RequestedScopes.Any())
        {
            LogError("Authorization code has no associated scopes");
            return(Invalid(OidcConstants.TokenErrors.InvalidRequest));
        }

        //////////////////////////////////////////////////////////
        // resource indicator
        //////////////////////////////////////////////////////////
        if (_validatedRequest.RequestedResourceIndicator != null &&
            _validatedRequest.AuthorizationCode.RequestedResourceIndicators?.Any() == true &&
            !_validatedRequest.AuthorizationCode.RequestedResourceIndicators.Contains(_validatedRequest.RequestedResourceIndicator))
        {
            return(Invalid(OidcConstants.AuthorizeErrors.InvalidTarget, "Resource indicator does not match any resource indicator in the original authorize request."));
        }

        //////////////////////////////////////////////////////////
        // resource and scope validation
        //////////////////////////////////////////////////////////
        var validatedResources = await _resourceValidator.ValidateRequestedResourcesAsync(new ResourceValidationRequest {
            Client             = _validatedRequest.Client,
            Scopes             = _validatedRequest.AuthorizationCode.RequestedScopes,
            ResourceIndicators = _validatedRequest.AuthorizationCode.RequestedResourceIndicators,
            // if we are issuing a refresh token, then we need to allow the non-isolated resource
            IncludeNonIsolatedApiResources = _validatedRequest.AuthorizationCode.RequestedScopes.Contains(OidcConstants.StandardScopes.OfflineAccess)
        });

        if (!validatedResources.Succeeded)
        {
            if (validatedResources.InvalidResourceIndicators.Any())
            {
                return(Invalid(OidcConstants.AuthorizeErrors.InvalidTarget, "Invalid resource indicator."));
            }
            if (validatedResources.InvalidScopes.Any())
            {
                return(Invalid(OidcConstants.AuthorizeErrors.InvalidScope, "Invalid scope."));
            }
        }

        LicenseValidator.ValidateResourceIndicators(_validatedRequest.RequestedResourceIndicator);
        _validatedRequest.ValidatedResources = validatedResources.FilterByResourceIndicator(_validatedRequest.RequestedResourceIndicator);

        /////////////////////////////////////////////
        // validate PKCE parameters
        /////////////////////////////////////////////
        var codeVerifier = parameters.Get(OidcConstants.TokenRequest.CodeVerifier);

        if (_validatedRequest.Client.RequirePkce || _validatedRequest.AuthorizationCode.CodeChallenge.IsPresent())
        {
            _logger.LogDebug("Client required a proof key for code exchange. Starting PKCE validation");

            var proofKeyResult = ValidateAuthorizationCodeWithProofKeyParameters(codeVerifier, _validatedRequest.AuthorizationCode);
            if (proofKeyResult.IsError)
            {
                return(proofKeyResult);
            }

            _validatedRequest.CodeVerifier = codeVerifier;
        }
        else
        {
            if (codeVerifier.IsPresent())
            {
                LogError("Unexpected code_verifier: {codeVerifier}. This happens when the client is trying to use PKCE, but it is not enabled. Set RequirePkce to true.", codeVerifier);
                return(Invalid(OidcConstants.TokenErrors.InvalidGrant));
            }
        }

        /////////////////////////////////////////////
        // make sure user is enabled
        /////////////////////////////////////////////
        var isActiveCtx = new IsActiveContext(_validatedRequest.AuthorizationCode.Subject, _validatedRequest.Client, IdentityServerConstants.ProfileIsActiveCallers.AuthorizationCodeValidation);
        await _profile.IsActiveAsync(isActiveCtx);

        if (isActiveCtx.IsActive == false)
        {
            LogError("User has been disabled", new { subjectId = _validatedRequest.AuthorizationCode.Subject.GetSubjectId() });
            return(Invalid(OidcConstants.TokenErrors.InvalidGrant));
        }

        _logger.LogDebug("Validation of authorization code token request success");

        return(Valid());
    }
    private async Task <AuthorizeRequestValidationResult> ValidateScopeAndResourceAsync(ValidatedAuthorizeRequest request)
    {
        //////////////////////////////////////////////////////////
        // scope must be present
        //////////////////////////////////////////////////////////
        var scope = request.Raw.Get(OidcConstants.AuthorizeRequest.Scope);

        if (scope.IsMissing())
        {
            LogError("scope is missing", request);
            return(Invalid(request, description: "Invalid scope"));
        }

        if (scope.Length > _options.InputLengthRestrictions.Scope)
        {
            LogError("scopes too long.", request);
            return(Invalid(request, description: "Invalid scope"));
        }

        request.RequestedScopes = scope.FromSpaceSeparatedString().Distinct().ToList();
        request.IsOpenIdRequest = request.RequestedScopes.Contains(IdentityServerConstants.StandardScopes.OpenId);

        //////////////////////////////////////////////////////////
        // check scope vs response_type plausability
        //////////////////////////////////////////////////////////
        var requirement = Constants.ResponseTypeToScopeRequirement[request.ResponseType];

        if (requirement == Constants.ScopeRequirement.Identity ||
            requirement == Constants.ScopeRequirement.IdentityOnly)
        {
            if (request.IsOpenIdRequest == false)
            {
                LogError("response_type requires the openid scope", request);
                return(Invalid(request, description: "Missing openid scope"));
            }
        }


        //////////////////////////////////////////////////////////
        // check for resource indicators and valid format
        //////////////////////////////////////////////////////////
        var resourceIndicators = request.Raw.GetValues(OidcConstants.TokenRequest.Resource) ?? Enumerable.Empty <string>();

        if (resourceIndicators?.Any(x => x.Length > _options.InputLengthRestrictions.ResourceIndicatorMaxLength) == true)
        {
            return(Invalid(request, OidcConstants.AuthorizeErrors.InvalidTarget, "Resource indicator maximum length exceeded"));
        }

        if (!resourceIndicators.AreValidResourceIndicatorFormat(_logger))
        {
            return(Invalid(request, OidcConstants.AuthorizeErrors.InvalidTarget, "Invalid resource indicator format"));
        }

        // we don't want to allow resource indicators when "token" is requested to authorize endpoint
        if (request.GrantType == GrantType.Implicit && resourceIndicators.Any())
        {
            // todo: correct error?
            return(Invalid(request, OidcConstants.AuthorizeErrors.InvalidTarget, "Resource indicators not allowed for response_type 'token'."));
        }

        request.RequestedResourceIndiators = resourceIndicators;

        //////////////////////////////////////////////////////////
        // check if scopes are valid/supported and check for resource scopes
        //////////////////////////////////////////////////////////
        var validatedResources = await _resourceValidator.ValidateRequestedResourcesAsync(new ResourceValidationRequest
        {
            Client                         = request.Client,
            Scopes                         = request.RequestedScopes,
            ResourceIndicators             = resourceIndicators,
            IncludeNonIsolatedApiResources = request.RequestedScopes.Contains(OidcConstants.StandardScopes.OfflineAccess),
        });

        if (!validatedResources.Succeeded)
        {
            if (validatedResources.InvalidResourceIndicators.Any())
            {
                return(Invalid(request, OidcConstants.AuthorizeErrors.InvalidTarget, "Invalid resource indicator"));
            }

            if (validatedResources.InvalidScopes.Any())
            {
                return(Invalid(request, OidcConstants.AuthorizeErrors.InvalidScope, "Invalid scope"));
            }
        }

        LicenseValidator.ValidateResourceIndicators(resourceIndicators);

        if (validatedResources.Resources.IdentityResources.Any() && !request.IsOpenIdRequest)
        {
            LogError("Identity related scope requests, but no openid scope", request);
            return(Invalid(request, OidcConstants.AuthorizeErrors.InvalidScope, "Identity scopes requested, but openid scope is missing"));
        }

        if (validatedResources.Resources.ApiScopes.Any())
        {
            request.IsApiResourceRequest = true;
        }

        //////////////////////////////////////////////////////////
        // check id vs resource scopes and response types plausability
        //////////////////////////////////////////////////////////
        var responseTypeValidationCheck = true;

        switch (requirement)
        {
        case Constants.ScopeRequirement.Identity:
            if (!validatedResources.Resources.IdentityResources.Any())
            {
                _logger.LogError("Requests for id_token response type must include identity scopes");
                responseTypeValidationCheck = false;
            }
            break;

        case Constants.ScopeRequirement.IdentityOnly:
            if (!validatedResources.Resources.IdentityResources.Any() || validatedResources.Resources.ApiScopes.Any())
            {
                _logger.LogError("Requests for id_token response type only must not include resource scopes");
                responseTypeValidationCheck = false;
            }
            break;

        case Constants.ScopeRequirement.ResourceOnly:
            if (validatedResources.Resources.IdentityResources.Any() || !validatedResources.Resources.ApiScopes.Any())
            {
                _logger.LogError("Requests for token response type only must include resource scopes, but no identity scopes.");
                responseTypeValidationCheck = false;
            }
            break;
        }

        if (!responseTypeValidationCheck)
        {
            return(Invalid(request, OidcConstants.AuthorizeErrors.InvalidScope, "Invalid scope for response type"));
        }

        request.ValidatedResources = validatedResources;

        return(Valid(request));
    }
    public async Task <BackchannelAuthenticationRequestValidationResult> ValidateRequestAsync(NameValueCollection parameters, ClientSecretValidationResult clientValidationResult)
    {
        using var activity = Tracing.BasicActivitySource.StartActivity("BackchannelAuthenticationRequestValidator.ValidateRequest");

        if (clientValidationResult == null)
        {
            throw new ArgumentNullException(nameof(clientValidationResult));
        }

        _logger.LogDebug("Start backchannel authentication request validation");

        _validatedRequest = new ValidatedBackchannelAuthenticationRequest
        {
            Raw = parameters ?? throw new ArgumentNullException(nameof(parameters)),
                        Options = _options
        };
        _validatedRequest.SetClient(clientValidationResult.Client, clientValidationResult.Secret, clientValidationResult.Confirmation);

        //////////////////////////////////////////////////////////
        // Client must be configured for CIBA
        //////////////////////////////////////////////////////////
        if (!clientValidationResult.Client.AllowedGrantTypes.Contains(OidcConstants.GrantTypes.Ciba))
        {
            LogError("Client {clientId} not configured with the CIBA grant type.", clientValidationResult.Client.ClientId);
            return(Invalid(OidcConstants.BackchannelAuthenticationRequestErrors.UnauthorizedClient, "Unauthorized client"));
        }

        LicenseValidator.ValidateCiba();

        //////////////////////////////////////////////////////////
        // load request object
        //////////////////////////////////////////////////////////
        var jwtRequest = _validatedRequest.Raw.Get(OidcConstants.BackchannelAuthenticationRequest.Request);

        // check length restrictions
        if (jwtRequest.IsPresent())
        {
            if (jwtRequest.Length >= _options.InputLengthRestrictions.Jwt)
            {
                LogError("request value is too long");
                return(Invalid(OidcConstants.AuthorizeErrors.InvalidRequestObject, "Invalid request value"));
            }
        }

        _validatedRequest.RequestObject = jwtRequest;

        //////////////////////////////////////////////////////////
        // validate request object
        //////////////////////////////////////////////////////////
        var roValidationResult = await TryValidateRequestObjectAsync();

        if (!roValidationResult.Success)
        {
            return(roValidationResult.ErrorResult);
        }

        if (_validatedRequest.Client.RequireRequestObject &&
            !_validatedRequest.RequestObjectValues.Any())
        {
            LogError("Client is configured for RequireRequestObject but none present");
            return(Invalid(OidcConstants.BackchannelAuthenticationRequestErrors.InvalidRequest));
        }

        //////////////////////////////////////////////////////////
        // scope must be present
        //////////////////////////////////////////////////////////
        var scope = _validatedRequest.Raw.Get(OidcConstants.BackchannelAuthenticationRequest.Scope);

        if (scope.IsMissing())
        {
            LogError("Missing scope");
            return(Invalid(OidcConstants.BackchannelAuthenticationRequestErrors.InvalidRequest, "Missing scope"));
        }

        if (scope.Length > _options.InputLengthRestrictions.Scope)
        {
            LogError("scopes too long.");
            return(Invalid(OidcConstants.BackchannelAuthenticationRequestErrors.InvalidRequest, "Invalid scope"));
        }

        _validatedRequest.RequestedScopes = scope.FromSpaceSeparatedString().Distinct().ToList();

        //////////////////////////////////////////////////////////
        // openid scope required
        //////////////////////////////////////////////////////////
        if (!_validatedRequest.RequestedScopes.Contains(IdentityServerConstants.StandardScopes.OpenId))
        {
            LogError("openid scope missing.");
            return(Invalid(OidcConstants.BackchannelAuthenticationRequestErrors.InvalidRequest, "Missing the openid scope"));
        }

        //////////////////////////////////////////////////////////
        // check for resource indicators and valid format
        //////////////////////////////////////////////////////////
        var resourceIndicators = _validatedRequest.Raw.GetValues("resource") ?? Enumerable.Empty <string>();

        if (resourceIndicators?.Any(x => x.Length > _options.InputLengthRestrictions.ResourceIndicatorMaxLength) == true)
        {
            return(Invalid(OidcConstants.BackchannelAuthenticationRequestErrors.InvalidTarget, "Resource indicator maximum length exceeded"));
        }

        if (!resourceIndicators.AreValidResourceIndicatorFormat(_logger))
        {
            return(Invalid(OidcConstants.BackchannelAuthenticationRequestErrors.InvalidTarget, "Invalid resource indicator format"));
        }

        _validatedRequest.RequestedResourceIndiators = resourceIndicators?.ToList();

        //////////////////////////////////////////////////////////
        // check if scopes are valid/supported and check for resource scopes
        //////////////////////////////////////////////////////////
        var validatedResources = await _resourceValidator.ValidateRequestedResourcesAsync(new ResourceValidationRequest
        {
            Client                         = _validatedRequest.Client,
            Scopes                         = _validatedRequest.RequestedScopes,
            ResourceIndicators             = resourceIndicators,
            IncludeNonIsolatedApiResources = _validatedRequest.RequestedScopes.Contains(OidcConstants.StandardScopes.OfflineAccess),
        });

        if (!validatedResources.Succeeded)
        {
            if (validatedResources.InvalidResourceIndicators.Any())
            {
                return(Invalid(OidcConstants.BackchannelAuthenticationRequestErrors.InvalidTarget, "Invalid resource indicator"));
            }

            if (validatedResources.InvalidScopes.Any())
            {
                return(Invalid(OidcConstants.BackchannelAuthenticationRequestErrors.InvalidScope, "Invalid scope"));
            }
        }

        LicenseValidator.ValidateResourceIndicators(resourceIndicators);
        _validatedRequest.ValidatedResources = validatedResources;


        //////////////////////////////////////////////////////////
        // check requested_expiry
        //////////////////////////////////////////////////////////
        var requestLifetime = _validatedRequest.Client.CibaLifetime ?? _options.Ciba.DefaultLifetime;
        var requestedExpiry = _validatedRequest.Raw.Get(OidcConstants.BackchannelAuthenticationRequest.RequestedExpiry);

        if (requestedExpiry.IsPresent())
        {
            // Int32.MaxValue == 2147483647, which is 10 characters in length
            // so using 9 so we don't overflow below on the Int32.Parse
            if (requestedExpiry.Length > 9)
            {
                LogError("requested_expiry too long");
                return(Invalid(OidcConstants.BackchannelAuthenticationRequestErrors.InvalidRequest, "Invalid requested_expiry"));
            }

            if (Int32.TryParse(requestedExpiry, out var expiryValue) &&
                expiryValue > 0 &&
                expiryValue <= requestLifetime)
            {
                _validatedRequest.Expiry = expiryValue;
            }
            else
            {
                LogError("requested_expiry value out of valid range");
                return(Invalid(OidcConstants.BackchannelAuthenticationRequestErrors.InvalidRequest, "Invalid requested_expiry"));
            }
        }
        else
        {
            _validatedRequest.Expiry = requestLifetime;
        }


        //////////////////////////////////////////////////////////
        // check acr_values
        //////////////////////////////////////////////////////////
        var acrValues = _validatedRequest.Raw.Get(OidcConstants.BackchannelAuthenticationRequest.AcrValues);

        if (acrValues.IsPresent())
        {
            if (acrValues.Length > _options.InputLengthRestrictions.AcrValues)
            {
                LogError("Acr values too long");
                return(Invalid(OidcConstants.BackchannelAuthenticationRequestErrors.InvalidRequest, "Invalid acr_values"));
            }

            _validatedRequest.AuthenticationContextReferenceClasses = acrValues.FromSpaceSeparatedString().Distinct().ToList();

            //////////////////////////////////////////////////////////
            // check custom acr_values: idp and tenant
            //////////////////////////////////////////////////////////
            var tenant = _validatedRequest.AuthenticationContextReferenceClasses.FirstOrDefault(x => x.StartsWith(KnownAcrValues.Tenant));
            if (tenant != null)
            {
                _validatedRequest.AuthenticationContextReferenceClasses.Remove(tenant);
                tenant = tenant.Substring(KnownAcrValues.Tenant.Length);
                _validatedRequest.Tenant = tenant;
            }

            var idp = _validatedRequest.AuthenticationContextReferenceClasses.FirstOrDefault(x => x.StartsWith(KnownAcrValues.HomeRealm));
            if (idp != null)
            {
                _validatedRequest.AuthenticationContextReferenceClasses.Remove(idp);
                idp = idp.Substring(KnownAcrValues.HomeRealm.Length);

                // check if idp is present but client does not allow it, and then ignore it
                if (_validatedRequest.Client.IdentityProviderRestrictions != null &&
                    _validatedRequest.Client.IdentityProviderRestrictions.Any())
                {
                    if (!_validatedRequest.Client.IdentityProviderRestrictions.Contains(idp))
                    {
                        _logger.LogWarning("idp requested ({idp}) is not in client restriction list.", idp);
                        idp = null;
                    }
                }

                _validatedRequest.IdP = idp;
            }
        }


        //////////////////////////////////////////////////////////
        // login hints
        //////////////////////////////////////////////////////////
        var loginHint      = _validatedRequest.Raw.Get(OidcConstants.BackchannelAuthenticationRequest.LoginHint);
        var loginHintToken = _validatedRequest.Raw.Get(OidcConstants.BackchannelAuthenticationRequest.LoginHintToken);
        var idTokenHint    = _validatedRequest.Raw.Get(OidcConstants.BackchannelAuthenticationRequest.IdTokenHint);

        var loginHintCount = 0;

        if (loginHint.IsPresent())
        {
            loginHintCount++;
        }
        if (loginHintToken.IsPresent())
        {
            loginHintCount++;
        }
        if (idTokenHint.IsPresent())
        {
            loginHintCount++;
        }

        if (loginHintCount == 0)
        {
            LogError("Missing login_hint_token, id_token_hint, or login_hint");
            return(Invalid(OidcConstants.BackchannelAuthenticationRequestErrors.InvalidRequest, "Missing login_hint_token, id_token_hint, or login_hint"));
        }
        else if (loginHintCount > 1)
        {
            LogError("Too many of login_hint_token, id_token_hint, or login_hint");
            return(Invalid(OidcConstants.BackchannelAuthenticationRequestErrors.InvalidRequest, "Too many of login_hint_token, id_token_hint, or login_hint"));
        }

        //////////////////////////////////////////////////////////
        // check login_hint
        //////////////////////////////////////////////////////////
        if (loginHint.IsPresent())
        {
            if (loginHint.Length > _options.InputLengthRestrictions.LoginHint)
            {
                LogError("Login hint too long");
                return(Invalid(OidcConstants.BackchannelAuthenticationRequestErrors.InvalidRequest, "Invalid login_hint"));
            }

            _validatedRequest.LoginHint = loginHint;
        }

        //////////////////////////////////////////////////////////
        // check login_hint_token
        //////////////////////////////////////////////////////////
        if (loginHintToken.IsPresent())
        {
            if (loginHintToken.Length > _options.InputLengthRestrictions.LoginHintToken)
            {
                LogError("Login hint token too long");
                return(Invalid(OidcConstants.BackchannelAuthenticationRequestErrors.InvalidRequest, "Invalid login_hint_token"));
            }

            _validatedRequest.LoginHintToken = loginHintToken;
        }

        //////////////////////////////////////////////////////////
        // check id_token_hint
        //////////////////////////////////////////////////////////
        if (idTokenHint.IsPresent())
        {
            if (idTokenHint.Length > _options.InputLengthRestrictions.IdTokenHint)
            {
                LogError("id token hint too long");
                return(Invalid(OidcConstants.BackchannelAuthenticationRequestErrors.InvalidRequest, "Invalid id_token_hint"));
            }

            var idTokenHintValidationResult = await _tokenValidator.ValidateIdentityTokenAsync(idTokenHint, _validatedRequest.ClientId, false);

            if (idTokenHintValidationResult.IsError)
            {
                LogError("id token hint failed to validate: " + idTokenHintValidationResult.Error, idTokenHintValidationResult.ErrorDescription);
                return(Invalid(OidcConstants.BackchannelAuthenticationRequestErrors.InvalidRequest, "Invalid id_token_hint"));
            }

            _validatedRequest.IdTokenHint       = idTokenHint;
            _validatedRequest.IdTokenHintClaims = idTokenHintValidationResult.Claims;
        }

        //////////////////////////////////////////////////////////
        // check user_code
        //////////////////////////////////////////////////////////
        var userCode = _validatedRequest.Raw.Get(OidcConstants.BackchannelAuthenticationRequest.UserCode);

        if (userCode.IsPresent())
        {
            if (userCode.Length > _options.InputLengthRestrictions.UserCode)
            {
                LogError("user_code too long");
                return(Invalid(OidcConstants.BackchannelAuthenticationRequestErrors.InvalidRequest, "Invalid user_code"));
            }

            _validatedRequest.UserCode = userCode;
        }

        //////////////////////////////////////////////////////////
        // check binding_message
        //////////////////////////////////////////////////////////
        var bindingMessage = _validatedRequest.Raw.Get(OidcConstants.BackchannelAuthenticationRequest.BindingMessage);

        if (bindingMessage.IsPresent())
        {
            if (bindingMessage.Length > _options.InputLengthRestrictions.BindingMessage)
            {
                LogError("binding_message too long");
                return(Invalid(OidcConstants.BackchannelAuthenticationRequestErrors.InvalidBindingMessage, "Invalid binding_message"));
            }

            _validatedRequest.BindingMessage = bindingMessage;
        }

        //////////////////////////////////////////////////////////
        // validate the login hint w/ custom validator
        //////////////////////////////////////////////////////////
        var userResult = await _backchannelAuthenticationUserValidator.ValidateRequestAsync(new BackchannelAuthenticationUserValidatorContext
        {
            Client            = _validatedRequest.Client,
            IdTokenHint       = _validatedRequest.IdTokenHint,
            LoginHint         = _validatedRequest.LoginHint,
            LoginHintToken    = _validatedRequest.LoginHintToken,
            IdTokenHintClaims = _validatedRequest.IdTokenHintClaims,
            UserCode          = _validatedRequest.UserCode,
            BindingMessage    = _validatedRequest.BindingMessage
        });

        if (userResult.IsError)
        {
            if (userResult.Error == OidcConstants.BackchannelAuthenticationRequestErrors.AccessDenied)
            {
                LogError("Request was denied access for that user");
                return(Invalid(OidcConstants.BackchannelAuthenticationRequestErrors.AccessDenied, userResult.ErrorDescription));
            }
            if (userResult.Error == OidcConstants.BackchannelAuthenticationRequestErrors.ExpiredLoginHintToken)
            {
                LogError("Expired login_hint_token");
                return(Invalid(OidcConstants.BackchannelAuthenticationRequestErrors.ExpiredLoginHintToken, userResult.ErrorDescription ?? "Expired login_hint_token"));
            }
            if (userResult.Error == OidcConstants.BackchannelAuthenticationRequestErrors.UnknownUserId)
            {
                LogError("Unknown user id");
                return(Invalid(OidcConstants.BackchannelAuthenticationRequestErrors.UnknownUserId, userResult.ErrorDescription));
            }
            if (userResult.Error == OidcConstants.BackchannelAuthenticationRequestErrors.MissingUserCode)
            {
                LogError("Missing user_code");
                return(Invalid(OidcConstants.BackchannelAuthenticationRequestErrors.MissingUserCode, userResult.ErrorDescription));
            }
            if (userResult.Error == OidcConstants.BackchannelAuthenticationRequestErrors.InvalidUserCode)
            {
                LogError("Invalid user_code");
                return(Invalid(OidcConstants.BackchannelAuthenticationRequestErrors.InvalidUserCode, userResult.ErrorDescription));
            }
            if (userResult.Error == OidcConstants.BackchannelAuthenticationRequestErrors.InvalidBindingMessage)
            {
                LogError("Invalid binding_message");
                return(Invalid(OidcConstants.BackchannelAuthenticationRequestErrors.InvalidBindingMessage, userResult.ErrorDescription));
            }

            LogError("Unexpected error from IBackchannelAuthenticationUserValidator: {error}", userResult.Error);
            return(Invalid(OidcConstants.BackchannelAuthenticationRequestErrors.UnknownUserId));
        }

        if (userResult.Subject == null || !userResult.Subject.HasClaim(x => x.Type == JwtClaimTypes.Subject))
        {
            LogError("No subject or subject id returned from IBackchannelAuthenticationUserValidator");
            return(Invalid(OidcConstants.BackchannelAuthenticationRequestErrors.UnknownUserId));
        }

        _validatedRequest.Subject = userResult.Subject;

        LogSuccess();
        return(new BackchannelAuthenticationRequestValidationResult(_validatedRequest));
    }