public TokenGeneratingContext(
            ClaimsPrincipal user,
            ClaimsPrincipal application,
            OpenIdConnectMessage requestParameters,
            RequestGrants requestGrants)
        {
            if (user == null)
            {
                throw new ArgumentNullException(nameof(user));
            }

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

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

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

            User              = user;
            Application       = application;
            RequestParameters = requestParameters;
            RequestGrants     = requestGrants;
        }
Beispiel #2
0
 public static TokenRequest Valid(
     OpenIdConnectMessage request,
     string userId,
     string clientId,
     RequestGrants grants)
 {
     return(new TokenRequest(request, userId, clientId, grants));
 }
Beispiel #3
0
 private TokenRequest(
     OpenIdConnectMessage request,
     string userId,
     string clientId,
     RequestGrants grants)
 {
     IsValid       = true;
     Request       = request;
     UserId        = userId;
     ClientId      = clientId;
     RequestGrants = grants;
 }
Beispiel #4
0
        public void CreateTokenGenerationContext_CorrectlyCreatesContext_FromTheAuthorizationRequest(
            string responseTypes,
            string expectedRequestedTokens)
        {
            // Arrange
            var authorizationRequest = new Dictionary <string, string[]>
            {
                [OpenIdConnectParameterNames.ClientId]     = new[] { "clientId" },
                [OpenIdConnectParameterNames.RedirectUri]  = new[] { "http://localhost:123/callback" },
                [OpenIdConnectParameterNames.ResponseType] = new[] { responseTypes },
                [OpenIdConnectParameterNames.ResponseMode] = new[] { OpenIdConnectResponseMode.FormPost },
                [OpenIdConnectParameterNames.Scope]        = new[] { "openid" },
                [OpenIdConnectParameterNames.Nonce]        = new[] { "asdf" },
                [OpenIdConnectParameterNames.State]        = new[] { "state" }
            };
            var message = new OpenIdConnectMessage(authorizationRequest);

            var requestGrants = new RequestGrants
            {
                RedirectUri  = "http://localhost:123/callback",
                ResponseMode = OpenIdConnectResponseMode.FormPost,
                Scopes       = new List <ApplicationScope> {
                    ApplicationScope.OpenId
                },
                Tokens = expectedRequestedTokens.Split(new[] { ' ' }, StringSplitOptions.RemoveEmptyEntries)
            };

            var request = AuthorizationRequest.Valid(message, requestGrants);

            var user        = new ClaimsPrincipal();
            var application = new ClaimsPrincipal();

            // Act
            var context = request.CreateTokenGeneratingContext(user, application);

            // Assert
            Assert.NotNull(context);
            Assert.Equal(expectedRequestedTokens.Split(new[] { ' ' }, StringSplitOptions.RemoveEmptyEntries), context.RequestGrants.Tokens);
            Assert.Equal("http://localhost:123/callback", context.RequestParameters.RedirectUri);
            Assert.Equal("openid", context.RequestParameters.Scope);
            Assert.Equal("asdf", context.RequestParameters.Nonce);
        }
        public async Task <TokenRequest> CreateTokenRequestAsync(IDictionary <string, string[]> requestParameters)
        {
            var(clientId, clientIdParameterError) = RequestParametersHelper.ValidateParameterIsUnique(requestParameters, OpenIdConnectParameterNames.ClientId, _errorProvider);
            if (clientIdParameterError != null)
            {
                return(TokenRequest.Invalid(clientIdParameterError));
            }

            if (!await _clientIdValidator.ValidateClientIdAsync(clientId))
            {
                return(TokenRequest.Invalid(_errorProvider.InvalidClientId(clientId)));
            }

            var(clientSecret, clientSecretParameterError) = RequestParametersHelper.ValidateOptionalParameterIsUnique(requestParameters, OpenIdConnectParameterNames.ClientSecret, _errorProvider);
            if (clientSecretParameterError != null)
            {
                return(TokenRequest.Invalid(clientSecretParameterError));
            }

            if (!await _clientIdValidator.ValidateClientCredentialsAsync(clientId, clientSecret))
            {
                return(TokenRequest.Invalid(_errorProvider.InvalidClientCredentials()));
            }

            var(grantType, grantTypeError) = RequestParametersHelper.ValidateParameterIsUnique(
                requestParameters,
                OpenIdConnectParameterNames.GrantType,
                _errorProvider);

            if (grantTypeError != null)
            {
                return(TokenRequest.Invalid(grantTypeError));
            }

            var grantTypeParameter = GetGrantTypeParameter(requestParameters, grantType);

            if (grantTypeParameter == null)
            {
                return(TokenRequest.Invalid(_errorProvider.InvalidGrantType(grantType)));
            }

            var(grantValue, grantValueError) = RequestParametersHelper.ValidateParameterIsUnique(
                requestParameters,
                grantTypeParameter,
                _errorProvider);

            if (grantValueError != null)
            {
                return(TokenRequest.Invalid(clientIdParameterError));
            }

            var message = new OpenIdConnectMessage(requestParameters)
            {
                RequestType = OpenIdConnectRequestType.Token
            };

            // TODO: File a bug to track we might want to redesign this if we want to consider other flows like
            // client credentials or resource owner credentials.
            var consentGrant = await _tokenManager.ExchangeTokenAsync(message);

            if (!consentGrant.IsValid)
            {
                return(TokenRequest.Invalid(consentGrant.Error));
            }

            if (!_timeStampManager.IsValidPeriod(consentGrant.Token.NotBefore, consentGrant.Token.Expires))
            {
                return(TokenRequest.Invalid(_errorProvider.InvalidLifetime()));
            }

            if (!string.Equals(clientId, consentGrant.ClientId, StringComparison.Ordinal))
            {
                return(TokenRequest.Invalid(_errorProvider.InvalidGrant()));
            }

            var(scope, requestScopesError) = RequestParametersHelper.ValidateOptionalParameterIsUnique(requestParameters, OpenIdConnectParameterNames.Scope, _errorProvider);
            if (requestScopesError != null)
            {
                return(TokenRequest.Invalid(requestScopesError));
            }

            var grantedScopes = consentGrant.GrantedScopes;

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

            if (parsedScope != null)
            {
                var scopeResolutionResult = await _scopeResolver.ResolveScopesAsync(clientId, parsedScope);

                if (!scopeResolutionResult.IsValid)
                {
                    return(TokenRequest.Invalid(scopeResolutionResult.Error));
                }

                if (grantType.Equals(OpenIdConnectGrantTypes.AuthorizationCode, StringComparison.Ordinal) ||
                    grantType.Equals(OpenIdConnectGrantTypes.RefreshToken, StringComparison.Ordinal))
                {
                    if (scopeResolutionResult.Scopes.Any(rs => !consentGrant.GrantedScopes.Contains(rs)))
                    {
                        return(TokenRequest.Invalid(_errorProvider.UnauthorizedScope()));
                    }
                }

                grantedScopes = scopeResolutionResult.Scopes;
            }

            if (grantType.Equals(OpenIdConnectGrantTypes.AuthorizationCode, StringComparison.Ordinal))
            {
                var authorizationCodeError = await ValidateAuthorizationCode(
                    requestParameters,
                    clientId,
                    consentGrant);

                if (authorizationCodeError != null)
                {
                    return(TokenRequest.Invalid(authorizationCodeError));
                }
            }

            var requestGrants = new RequestGrants
            {
                Tokens = consentGrant.GrantedTokens.ToList(),
                Claims = consentGrant.Token.ToList(),
                Scopes = grantedScopes.ToList()
            };

            return(await ValidateRequestAsync(TokenRequest.Valid(
                                                  message,
                                                  consentGrant.UserId,
                                                  consentGrant.ClientId,
                                                  requestGrants)));
        }
Beispiel #6
0
 public static AuthorizationRequest Valid(
     OpenIdConnectMessage request,
     RequestGrants requestGrants)
 {
     return(new AuthorizationRequest(request, requestGrants));
 }
Beispiel #7
0
 private AuthorizationRequest(OpenIdConnectMessage request, RequestGrants requestGrants)
 {
     IsValid       = true;
     Message       = request;
     RequestGrants = requestGrants;
 }
        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)));
        }