Exemplo n.º 1
0
        public async Task FailsToCreateAuthorizationRequest_IfRequestAsksForIdToken_ButOpenIdScopeIsMissing(string responseType)
        {
            // Arrange
            var parameters =
                new Dictionary <string, string[]>
            {
                [OpenIdConnectParameterNames.State]        = new[] { "state" },
                [OpenIdConnectParameterNames.ClientId]     = new[] { "a" },
                [OpenIdConnectParameterNames.RedirectUri]  = new[] { "http://www.example.com/callback" },
                [OpenIdConnectParameterNames.ResponseType] = new[] { responseType },
                [OpenIdConnectParameterNames.ResponseMode] = new[] { "form_post" },
                [OpenIdConnectParameterNames.Scope]        = new[] { "offline_access" },
                [OpenIdConnectParameterNames.Nonce]        = new[] { "nonce" }
            };

            var expectedError = new AuthorizationRequestError(ProtocolErrorProvider.MissingOpenIdScope(), null, null);

            expectedError.Message.State = "state";

            var factory = CreateAuthorizationRequestFactory();

            // Act
            var result = await factory.CreateAuthorizationRequestAsync(parameters);

            // Assert
            Assert.False(result.IsValid);
            Assert.Equal(expectedError, result.Error, IdentityServiceErrorComparer.Instance);
            Assert.Equal("http://www.example.com/callback", result.Error.RedirectUri);
            Assert.Equal(OpenIdConnectResponseMode.FormPost, result.Error.ResponseMode);
        }
        public async Task <AuthorizationRequest> CreateAuthorizationRequestAsync(IDictionary <string, string[]> requestParameters)
        {
            // Parameters sent without a value MUST be treated as if they were
            // omitted from the request.The authorization server MUST ignore
            // unrecognized request parameters.Request and response parameters
            // MUST NOT be included more than once.

            // Validate that we only got send one state property as it needs to be included in all responses (including error ones)
            var(state, stateError) = RequestParametersHelper.ValidateOptionalParameterIsUnique(requestParameters, OpenIdConnectParameterNames.State, _errorProvider);


            // Start by validating the client_id and redirect_uri as any of them being invalid indicates that we need to
            // return a 400 response instead of a 302 response with the error. This is signaled by the result not containing
            // a url to redirect to.
            var(clientId, redirectUri, clientError) = await ValidateClientIdAndRedirectUri(requestParameters, state);

            if (clientError != null)
            {
                // Send first the state error if there was one.
                return(AuthorizationRequest.Invalid(new AuthorizationRequestError(
                                                        stateError ?? clientError,
                                                        redirectUri: null,
                                                        responseMode: null)));
            }

            // We need to determine what response mode to use to send the errors in case there are any.
            // In case the response type and response modes are valid, we should use those values when
            // notifying clients of the errors.
            // In case there is an issue with the response type or the response mode we need to determine
            // how to notify the relying party of the errors.
            // We can divide this in two situations:
            // The response mode is invalid:
            //  * We ignore the response mode and base our response based on the response type specified.
            //      If a token was requested we send the error response on the fragment of the redirect uri.
            //      If no token was requested we send the error response on the query of the redirect uri.
            // The response type is invalid:
            //  * We try to determine if this is a hybrid or implicit flow:
            //      If the invalid response type contained a request for an id_token or an access_token, or
            //      contained more than one space separated value, we send the response on the fragment,
            //      unless the response mode is specified and form_post.
            //      If the invalid response type only contained one value and we can not determine is an
            //      implicit request flow, we return the error on the query string unless the response mode
            //      is specified and form_post or fragment.

            var(responseType, parsedResponseType, tokenRequested, responseTypeError) = ValidateResponseType(requestParameters);
            var(responseMode, responseModeError) = ValidateResponseMode(requestParameters);

            var invalidCombinationError = ValidateResponseModeTypeCombination(responseType, tokenRequested, responseMode);

            if (responseModeError != null || responseMode == null)
            {
                responseMode = GetResponseMode(parsedResponseType, tokenRequested);
            }

            if (responseTypeError != null)
            {
                responseTypeError.State = state;
                return(AuthorizationRequest.Invalid(
                           new AuthorizationRequestError(stateError ?? responseTypeError, redirectUri, responseMode)));
            }

            if (responseModeError != null)
            {
                responseModeError.State = state;
                return(AuthorizationRequest.Invalid(
                           new AuthorizationRequestError(stateError ?? responseModeError, redirectUri, responseMode)));
            }

            if (invalidCombinationError != null)
            {
                invalidCombinationError.State = state;
                return(AuthorizationRequest.Invalid(
                           new AuthorizationRequestError(stateError ?? invalidCombinationError, redirectUri, responseMode)));
            }

            var(nonce, nonceError) = tokenRequested ?
                                     RequestParametersHelper.ValidateParameterIsUnique(requestParameters, OpenIdConnectParameterNames.Nonce, _errorProvider) :
                                     RequestParametersHelper.ValidateOptionalParameterIsUnique(requestParameters, OpenIdConnectParameterNames.Nonce, _errorProvider);

            if (nonceError != null)
            {
                nonceError.State = state;
                return(AuthorizationRequest.Invalid(new AuthorizationRequestError(nonceError, redirectUri, responseMode)));
            }

            var(scope, scopeError) = RequestParametersHelper.ValidateParameterIsUnique(requestParameters, OpenIdConnectParameterNames.Scope, _errorProvider);
            if (scopeError != null)
            {
                scopeError.State = state;
                return(AuthorizationRequest.Invalid(new AuthorizationRequestError(scopeError, redirectUri, responseMode)));
            }

            var parsedScope   = scope.Split(new[] { ' ' }, StringSplitOptions.RemoveEmptyEntries);
            var allWhiteSpace = true;

            for (int i = 0; i < parsedScope.Length; i++)
            {
                allWhiteSpace = string.IsNullOrWhiteSpace(parsedScope[i]);
                if (!allWhiteSpace)
                {
                    break;
                }
            }

            if (allWhiteSpace)
            {
                scopeError       = _errorProvider.MissingRequiredParameter(OpenIdConnectParameterNames.Scope);
                scopeError.State = state;

                return(AuthorizationRequest.Invalid(new AuthorizationRequestError(
                                                        scopeError,
                                                        redirectUri,
                                                        responseMode)));
            }

            if (parsedResponseType.Contains(OpenIdConnectResponseType.IdToken) && !parsedScope.Contains(OpenIdConnectScope.OpenId))
            {
                scopeError       = _errorProvider.MissingOpenIdScope();
                scopeError.State = state;

                return(AuthorizationRequest.Invalid(new AuthorizationRequestError(
                                                        scopeError,
                                                        redirectUri,
                                                        responseMode)));
            }

            var resolvedScopes = await _scopeValidator.ResolveScopesAsync(clientId, parsedScope);

            if (!resolvedScopes.IsValid)
            {
                resolvedScopes.Error.State = state;
                return(AuthorizationRequest.Invalid(new AuthorizationRequestError(resolvedScopes.Error, redirectUri, responseMode)));
            }

            var(prompt, promptError) = RequestParametersHelper.ValidateOptionalParameterIsUnique(requestParameters, OpenIdConnectParameterNames.Prompt, _errorProvider);
            if (promptError != null)
            {
                promptError.State = state;
                return(AuthorizationRequest.Invalid(new AuthorizationRequestError(promptError, redirectUri, responseMode)));
            }

            if (prompt != null)
            {
                var parsedPrompt = prompt.Split(new[] { ' ' }, StringSplitOptions.RemoveEmptyEntries);
                promptError = ValidatePrompt(parsedPrompt);
                if (promptError != null)
                {
                    promptError.State = state;
                    return(AuthorizationRequest.Invalid(new AuthorizationRequestError(promptError, redirectUri, responseMode)));
                }
            }

            var(codeChallenge, codeChallengeError) = RequestParametersHelper.ValidateOptionalParameterIsUnique(requestParameters, ProofOfKeyForCodeExchangeParameterNames.CodeChallenge, _errorProvider);
            if (codeChallengeError != null)
            {
                codeChallengeError.State = state;
                return(AuthorizationRequest.Invalid(new AuthorizationRequestError(codeChallengeError, redirectUri, responseMode)));
            }

            if (codeChallenge != null)
            {
                // The code challenge needs to be 43 characters long as its the result of Base64URLEncode(SHA256(code_verifier)).
                // We do this check here because the code challenge might get saved in the serialized authorization code and we
                // want to prevent it from getting unnecessarily big.
                if (codeChallenge.Length != 43)
                {
                    var invalidCodeChallenge = _errorProvider.InvalidCodeChallenge();
                    invalidCodeChallenge.State = state;
                    return(AuthorizationRequest.Invalid(new AuthorizationRequestError(
                                                            invalidCodeChallenge,
                                                            redirectUri,
                                                            responseMode)));
                }

                var(codeChallengeMethod, codeChallengeMethodError) = RequestParametersHelper.ValidateParameterIsUnique(requestParameters, ProofOfKeyForCodeExchangeParameterNames.CodeChallengeMethod, _errorProvider);
                if (codeChallengeMethodError != null)
                {
                    codeChallengeMethodError.State = state;
                    return(AuthorizationRequest.Invalid(new AuthorizationRequestError(codeChallengeMethodError, redirectUri, responseMode)));
                }

                if (!codeChallengeMethod.Equals(ProofOfKeyForCodeExchangeChallengeMethods.SHA256, StringComparison.Ordinal))
                {
                    var invalidChallengeMethod = _errorProvider.InvalidCodeChallengeMethod(codeChallengeMethod);
                    invalidChallengeMethod.State = state;
                    return(AuthorizationRequest.Invalid(new AuthorizationRequestError(invalidChallengeMethod, redirectUri, responseMode)));
                }
            }

            var result = new OpenIdConnectMessage(requestParameters);

            result.RequestType = OpenIdConnectRequestType.Authentication;

            var requestGrants = new RequestGrants
            {
                Tokens       = GetRequestedTokens(parsedResponseType, resolvedScopes.Scopes),
                Scopes       = resolvedScopes.Scopes.ToList(),
                ResponseMode = responseMode,
                RedirectUri  = redirectUri
            };

            return(await ValidateRequestAsync(AuthorizationRequest.Valid(result, requestGrants)));
        }