private void LogError(string message, string detail, ValidatedAuthorizeRequest request)
        {
            var requestDetails = new AuthorizeRequestValidationLog(request, _options.Logging.AuthorizeRequestSensitiveValuesFilter);

            _logger.LogError(message + ": {detail}\n{@requestDetails}", detail, requestDetails);
        }
 private AuthorizeRequestValidationResult Invalid(ValidatedAuthorizeRequest request, string error = OidcConstants.AuthorizeErrors.InvalidRequest, string description = null)
 {
     return(new AuthorizeRequestValidationResult(request, error, description));
 }
 private AuthorizeRequestValidationResult Valid(ValidatedAuthorizeRequest request)
 {
     return(new AuthorizeRequestValidationResult(request));
 }
        private async Task <AuthorizeRequestValidationResult> ValidateScopeAsync(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();

            if (request.RequestedScopes.Contains(IdentityServerConstants.StandardScopes.OpenId))
            {
                request.IsOpenIdRequest = true;
            }

            //////////////////////////////////////////////////////////
            // 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 if scopes are valid/supported and check for resource scopes
            //////////////////////////////////////////////////////////
            var validatedResources = await _resourceValidator.ValidateRequestedResourcesAsync(new ResourceValidationRequest
            {
                Client = request.Client,
                Scopes = request.RequestedScopes
            });

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

            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));
        }
        private async Task <AuthorizeRequestValidationResult> ValidateOptionalParametersAsync(ValidatedAuthorizeRequest request)
        {
            //////////////////////////////////////////////////////////
            // check nonce
            //////////////////////////////////////////////////////////
            var nonce = request.Raw.Get(OidcConstants.AuthorizeRequest.Nonce);

            if (nonce.IsPresent())
            {
                if (nonce.Length > _options.InputLengthRestrictions.Nonce)
                {
                    LogError("Nonce too long", request);
                    return(Invalid(request, description: "Invalid nonce"));
                }

                request.Nonce = nonce;
            }
            else
            {
                if (request.GrantType == GrantType.Implicit ||
                    request.GrantType == GrantType.Hybrid)
                {
                    // only openid requests require nonce
                    if (request.IsOpenIdRequest)
                    {
                        LogError("Nonce required for implicit and hybrid flow with openid scope", request);
                        return(Invalid(request, description: "Invalid nonce"));
                    }
                }
            }


            //////////////////////////////////////////////////////////
            // check prompt
            //////////////////////////////////////////////////////////
            var prompt = request.Raw.Get(OidcConstants.AuthorizeRequest.Prompt);

            if (prompt.IsPresent())
            {
                var prompts = prompt.Split(' ', StringSplitOptions.RemoveEmptyEntries);
                if (prompts.All(p => Constants.SupportedPromptModes.Contains(p)))
                {
                    if (prompts.Contains(OidcConstants.PromptModes.None) && prompts.Length > 1)
                    {
                        LogError("prompt contains 'none' and other values. 'none' should be used by itself.", request);
                        return(Invalid(request, description: "Invalid prompt"));
                    }

                    request.PromptModes = prompts;
                }
                else
                {
                    _logger.LogDebug("Unsupported prompt mode - ignored: " + prompt);
                }
            }

            //////////////////////////////////////////////////////////
            // check ui locales
            //////////////////////////////////////////////////////////
            var uilocales = request.Raw.Get(OidcConstants.AuthorizeRequest.UiLocales);

            if (uilocales.IsPresent())
            {
                if (uilocales.Length > _options.InputLengthRestrictions.UiLocale)
                {
                    LogError("UI locale too long", request);
                    return(Invalid(request, description: "Invalid ui_locales"));
                }

                request.UiLocales = uilocales;
            }

            //////////////////////////////////////////////////////////
            // check display
            //////////////////////////////////////////////////////////
            var display = request.Raw.Get(OidcConstants.AuthorizeRequest.Display);

            if (display.IsPresent())
            {
                if (Constants.SupportedDisplayModes.Contains(display))
                {
                    request.DisplayMode = display;
                }

                _logger.LogDebug("Unsupported display mode - ignored: " + display);
            }

            //////////////////////////////////////////////////////////
            // check max_age
            //////////////////////////////////////////////////////////
            var maxAge = request.Raw.Get(OidcConstants.AuthorizeRequest.MaxAge);

            if (maxAge.IsPresent())
            {
                if (int.TryParse(maxAge, out var seconds))
                {
                    if (seconds >= 0)
                    {
                        request.MaxAge = seconds;
                    }
                    else
                    {
                        LogError("Invalid max_age.", request);
                        return(Invalid(request, description: "Invalid max_age"));
                    }
                }
                else
                {
                    LogError("Invalid max_age.", request);
                    return(Invalid(request, description: "Invalid max_age"));
                }
            }

            //////////////////////////////////////////////////////////
            // check login_hint
            //////////////////////////////////////////////////////////
            var loginHint = request.Raw.Get(OidcConstants.AuthorizeRequest.LoginHint);

            if (loginHint.IsPresent())
            {
                if (loginHint.Length > _options.InputLengthRestrictions.LoginHint)
                {
                    LogError("Login hint too long", request);
                    return(Invalid(request, description: "Invalid login_hint"));
                }

                request.LoginHint = loginHint;
            }

            //////////////////////////////////////////////////////////
            // check acr_values
            //////////////////////////////////////////////////////////
            var acrValues = request.Raw.Get(OidcConstants.AuthorizeRequest.AcrValues);

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

                request.AuthenticationContextReferenceClasses = acrValues.FromSpaceSeparatedString().Distinct().ToList();
            }

            //////////////////////////////////////////////////////////
            // check custom acr_values: idp
            //////////////////////////////////////////////////////////
            var idp = request.GetIdP();

            if (idp.IsPresent())
            {
                // if idp is present but client does not allow it, strip it from the request message
                if (request.Client.IdentityProviderRestrictions != null && request.Client.IdentityProviderRestrictions.Any())
                {
                    if (!request.Client.IdentityProviderRestrictions.Contains(idp))
                    {
                        _logger.LogWarning("idp requested ({idp}) is not in client restriction list.", idp);
                        request.RemoveIdP();
                    }
                }
            }

            //////////////////////////////////////////////////////////
            // check session cookie
            //////////////////////////////////////////////////////////
            if (_options.Endpoints.EnableCheckSessionEndpoint)
            {
                if (request.Subject.IsAuthenticated())
                {
                    var sessionId = await _userSession.GetSessionIdAsync();

                    if (sessionId.IsPresent())
                    {
                        request.SessionId = sessionId;
                    }
                    else
                    {
                        LogError("Check session endpoint enabled, but SessionId is missing", request);
                    }
                }
                else
                {
                    request.SessionId = ""; // empty string for anonymous users
                }
            }

            return(Valid(request));
        }
        private AuthorizeRequestValidationResult ValidateCoreParameters(ValidatedAuthorizeRequest request)
        {
            //////////////////////////////////////////////////////////
            // check state
            //////////////////////////////////////////////////////////
            var state = request.Raw.Get(OidcConstants.AuthorizeRequest.State);

            if (state.IsPresent())
            {
                request.State = state;
            }

            //////////////////////////////////////////////////////////
            // response_type must be present and supported
            //////////////////////////////////////////////////////////
            var responseType = request.Raw.Get(OidcConstants.AuthorizeRequest.ResponseType);

            if (responseType.IsMissing())
            {
                LogError("Missing response_type", request);
                return(Invalid(request, OidcConstants.AuthorizeErrors.UnsupportedResponseType, "Missing response_type"));
            }

            // The responseType may come in in an unconventional order.
            // Use an IEqualityComparer that doesn't care about the order of multiple values.
            // Per https://tools.ietf.org/html/rfc6749#section-3.1.1 -
            // 'Extension response types MAY contain a space-delimited (%x20) list of
            // values, where the order of values does not matter (e.g., response
            // type "a b" is the same as "b a").'
            // http://openid.net/specs/oauth-v2-multiple-response-types-1_0-03.html#terminology -
            // 'If a response type contains one of more space characters (%20), it is compared
            // as a space-delimited list of values in which the order of values does not matter.'
            if (!Constants.SupportedResponseTypes.Contains(responseType, _responseTypeEqualityComparer))
            {
                LogError("Response type not supported", responseType, request);
                return(Invalid(request, OidcConstants.AuthorizeErrors.UnsupportedResponseType, "Response type not supported"));
            }

            // Even though the responseType may have come in in an unconventional order,
            // we still need the request's ResponseType property to be set to the
            // conventional, supported response type.
            request.ResponseType = Constants.SupportedResponseTypes.First(
                supportedResponseType => _responseTypeEqualityComparer.Equals(supportedResponseType, responseType));

            //////////////////////////////////////////////////////////
            // match response_type to grant type
            //////////////////////////////////////////////////////////
            request.GrantType = Constants.ResponseTypeToGrantTypeMapping[request.ResponseType];

            // set default response mode for flow; this is needed for any client error processing below
            request.ResponseMode = Constants.AllowedResponseModesForGrantType[request.GrantType].First();

            //////////////////////////////////////////////////////////
            // check if flow is allowed at authorize endpoint
            //////////////////////////////////////////////////////////
            if (!Constants.AllowedGrantTypesForAuthorizeEndpoint.Contains(request.GrantType))
            {
                LogError("Invalid grant type", request.GrantType, request);
                return(Invalid(request, description: "Invalid response_type"));
            }

            //////////////////////////////////////////////////////////
            // check if PKCE is required and validate parameters
            //////////////////////////////////////////////////////////
            if (request.GrantType == GrantType.AuthorizationCode || request.GrantType == GrantType.Hybrid)
            {
                _logger.LogDebug("Checking for PKCE parameters");

                /////////////////////////////////////////////////////////////////////////////
                // validate code_challenge and code_challenge_method
                /////////////////////////////////////////////////////////////////////////////
                var proofKeyResult = ValidatePkceParameters(request);

                if (proofKeyResult.IsError)
                {
                    return(proofKeyResult);
                }
            }

            //////////////////////////////////////////////////////////
            // check response_mode parameter and set response_mode
            //////////////////////////////////////////////////////////

            // check if response_mode parameter is present and valid
            var responseMode = request.Raw.Get(OidcConstants.AuthorizeRequest.ResponseMode);

            if (responseMode.IsPresent())
            {
                if (Constants.SupportedResponseModes.Contains(responseMode))
                {
                    if (Constants.AllowedResponseModesForGrantType[request.GrantType].Contains(responseMode))
                    {
                        request.ResponseMode = responseMode;
                    }
                    else
                    {
                        LogError("Invalid response_mode for response_type", responseMode, request);
                        return(Invalid(request, OidcConstants.AuthorizeErrors.InvalidRequest, description: "Invalid response_mode for response_type"));
                    }
                }
                else
                {
                    LogError("Unsupported response_mode", responseMode, request);
                    return(Invalid(request, OidcConstants.AuthorizeErrors.UnsupportedResponseType, description: "Invalid response_mode"));
                }
            }


            //////////////////////////////////////////////////////////
            // check if grant type is allowed for client
            //////////////////////////////////////////////////////////
            if (!request.Client.AllowedGrantTypes.Contains(request.GrantType))
            {
                LogError("Invalid grant type for client", request.GrantType, request);
                return(Invalid(request, OidcConstants.AuthorizeErrors.UnauthorizedClient, "Invalid grant type for client"));
            }

            //////////////////////////////////////////////////////////
            // check if response type contains an access token,
            // and if client is allowed to request access token via browser
            //////////////////////////////////////////////////////////
            var responseTypes = responseType.FromSpaceSeparatedString();

            if (responseTypes.Contains(OidcConstants.ResponseTypes.Token))
            {
                if (!request.Client.AllowAccessTokensViaBrowser)
                {
                    LogError("Client requested access token - but client is not configured to receive access tokens via browser", request);
                    return(Invalid(request, description: "Client not configured to receive access tokens via browser"));
                }
            }

            return(Valid(request));
        }
        private AuthorizeRequestValidationResult ValidatePkceParameters(ValidatedAuthorizeRequest request)
        {
            var fail = Invalid(request);

            var codeChallenge = request.Raw.Get(OidcConstants.AuthorizeRequest.CodeChallenge);

            if (codeChallenge.IsMissing())
            {
                if (request.Client.RequirePkce)
                {
                    LogError("code_challenge is missing", request);
                    fail.ErrorDescription = "code challenge required";
                }
                else
                {
                    _logger.LogDebug("No PKCE used.");
                    return(Valid(request));
                }

                return(fail);
            }

            if (codeChallenge.Length < _options.InputLengthRestrictions.CodeChallengeMinLength ||
                codeChallenge.Length > _options.InputLengthRestrictions.CodeChallengeMaxLength)
            {
                LogError("code_challenge is either too short or too long", request);
                fail.ErrorDescription = "Invalid code_challenge";
                return(fail);
            }

            request.CodeChallenge = codeChallenge;

            var codeChallengeMethod = request.Raw.Get(OidcConstants.AuthorizeRequest.CodeChallengeMethod);

            if (codeChallengeMethod.IsMissing())
            {
                _logger.LogDebug("Missing code_challenge_method, defaulting to plain");
                codeChallengeMethod = OidcConstants.CodeChallengeMethods.Plain;
            }

            if (!Constants.SupportedCodeChallengeMethods.Contains(codeChallengeMethod))
            {
                LogError("Unsupported code_challenge_method", codeChallengeMethod, request);
                fail.ErrorDescription = "Transform algorithm not supported";
                return(fail);
            }

            // check if plain method is allowed
            if (codeChallengeMethod == OidcConstants.CodeChallengeMethods.Plain)
            {
                if (!request.Client.AllowPlainTextPkce)
                {
                    LogError("code_challenge_method of plain is not allowed", request);
                    fail.ErrorDescription = "Transform algorithm not supported";
                    return(fail);
                }
            }

            request.CodeChallengeMethod = codeChallengeMethod;

            return(Valid(request));
        }
 public static string GetTenant(this ValidatedAuthorizeRequest request)
 {
     return(request.GetPrefixedAcrValue(Constants.KnownAcrValues.Tenant));
 }
        private async Task <AuthorizeRequestValidationResult> ValidateRequestObjectAsync(ValidatedAuthorizeRequest request)
        {
            //////////////////////////////////////////////////////////
            // validate request object
            /////////////////////////////////////////////////////////
            if (request.RequestObject.IsPresent())
            {
                // validate the request JWT for this client
                var jwtRequestValidationResult = await _jwtRequestValidator.ValidateAsync(request.Client, request.RequestObject);

                if (jwtRequestValidationResult.IsError)
                {
                    LogError("request JWT validation failure", request);
                    return(Invalid(request, error: OidcConstants.AuthorizeErrors.InvalidRequestObject, description: "Invalid JWT request"));
                }

                // validate response_type match
                var responseType = request.Raw.Get(OidcConstants.AuthorizeRequest.ResponseType);
                if (responseType != null)
                {
                    if (jwtRequestValidationResult.Payload.TryGetValue(OidcConstants.AuthorizeRequest.ResponseType, out var payloadResponseType))
                    {
                        if (payloadResponseType != responseType)
                        {
                            LogError("response_type in JWT payload does not match response_type in request", request);
                            return(Invalid(request, description: "Invalid JWT request"));
                        }
                    }
                }

                // validate client_id mismatch
                if (jwtRequestValidationResult.Payload.TryGetValue(OidcConstants.AuthorizeRequest.ClientId, out var payloadClientId))
                {
                    if (!string.Equals(request.Client.ClientId, payloadClientId, StringComparison.Ordinal))
                    {
                        LogError("client_id in JWT payload does not match client_id in request", request);
                        return(Invalid(request, description: "Invalid JWT request"));
                    }
                }
                else
                {
                    LogError("client_id is missing in JWT payload", request);
                    return(Invalid(request, error: OidcConstants.AuthorizeErrors.InvalidRequestObject, description: "Invalid JWT request"));
                }

                var ignoreKeys = new[]
                {
                    JwtClaimTypes.Issuer,
                    JwtClaimTypes.Audience
                };

                // merge jwt payload values into original request parameters
                foreach (var key in jwtRequestValidationResult.Payload.Keys)
                {
                    if (ignoreKeys.Contains(key))
                    {
                        continue;
                    }

                    var value = jwtRequestValidationResult.Payload[key];

                    var qsValue = request.Raw.Get(key);
                    if (qsValue != null)
                    {
                        if (!string.Equals(value, qsValue, StringComparison.Ordinal))
                        {
                            LogError("parameter mismatch between request object and query string parameter.", request);
                            return(Invalid(request, description: "Parameter mismatch in JWT request"));
                        }
                    }

                    request.Raw.Set(key, value);
                }

                request.RequestObjectValues = jwtRequestValidationResult.Payload;
            }

            return(Valid(request));
        }
 public static void RemoveIdP(this ValidatedAuthorizeRequest request)
 {
     request.RemovePrefixedAcrValue(Constants.KnownAcrValues.HomeRealm);
 }
 public static string GetIdP(this ValidatedAuthorizeRequest request)
 {
     return(request.GetPrefixedAcrValue(Constants.KnownAcrValues.HomeRealm));
 }
 public static void RemovePrompt(this ValidatedAuthorizeRequest request)
 {
     request.PromptModes = Enumerable.Empty <string>();
     request.Raw.Remove(OidcConstants.AuthorizeRequest.Prompt);
 }
        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();

            if (request.RequestedScopes.Contains(IdentityServerConstants.StandardScopes.OpenId))
            {
                request.IsOpenIdRequest = true;
            }

            //////////////////////////////////////////////////////////
            // 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));
        }
        private async Task <AuthorizeRequestValidationResult> ValidateRequestObjectAsync(ValidatedAuthorizeRequest request)
        {
            //////////////////////////////////////////////////////////
            // validate request object
            /////////////////////////////////////////////////////////
            if (request.RequestObject.IsPresent())
            {
                // validate the request JWT for this client
                var jwtRequestValidationResult = await _jwtRequestValidator.ValidateAsync(request.Client, request.RequestObject);

                if (jwtRequestValidationResult.IsError)
                {
                    LogError("request JWT validation failure", request);
                    return(Invalid(request, error: OidcConstants.AuthorizeErrors.InvalidRequestObject, description: "Invalid JWT request"));
                }

                // validate response_type match
                var responseType = request.Raw.Get(OidcConstants.AuthorizeRequest.ResponseType);
                if (responseType != null)
                {
                    var payloadResponseType =
                        jwtRequestValidationResult.Payload.SingleOrDefault(c =>
                                                                           c.Type == OidcConstants.AuthorizeRequest.ResponseType)?.Value;

                    if (!string.IsNullOrEmpty(payloadResponseType))
                    {
                        if (payloadResponseType != responseType)
                        {
                            LogError("response_type in JWT payload does not match response_type in request", request);
                            return(Invalid(request, description: "Invalid JWT request"));
                        }
                    }
                }

                // validate client_id mismatch
                var payloadClientId =
                    jwtRequestValidationResult.Payload.SingleOrDefault(c =>
                                                                       c.Type == OidcConstants.AuthorizeRequest.ClientId)?.Value;

                if (!string.IsNullOrEmpty(payloadClientId))
                {
                    if (!string.Equals(request.Client.ClientId, payloadClientId, StringComparison.Ordinal))
                    {
                        LogError("client_id in JWT payload does not match client_id in request", request);
                        return(Invalid(request, description: "Invalid JWT request"));
                    }
                }
                else
                {
                    LogError("client_id is missing in JWT payload", request);
                    return(Invalid(request, error: OidcConstants.AuthorizeErrors.InvalidRequestObject, description: "Invalid JWT request"));
                }

                var ignoreKeys = new[]
                {
                    JwtClaimTypes.Issuer,
                    JwtClaimTypes.Audience
                };

                // merge jwt payload values into original request parameters
                // 1. clear the keys in the raw collection for every key found in the request object
                foreach (var claimType in jwtRequestValidationResult.Payload.Select(c => c.Type).Distinct())
                {
                    var qsValue = request.Raw.Get(claimType);
                    if (qsValue != null)
                    {
                        request.Raw.Remove(claimType);
                    }
                }

                // 2. copy over the value
                foreach (var claim in jwtRequestValidationResult.Payload)
                {
                    request.Raw.Add(claim.Type, claim.Value);
                }

                var ruri = request.Raw.Get(OidcConstants.AuthorizeRequest.RequestUri);
                if (ruri != null)
                {
                    request.Raw.Remove(OidcConstants.AuthorizeRequest.RequestUri);
                    request.Raw.Add(OidcConstants.AuthorizeRequest.Request, request.RequestObject);
                }


                request.RequestObjectValues = jwtRequestValidationResult.Payload;
            }

            return(Valid(request));
        }