// Copyright (c) .NET Foundation. All rights reserved. // Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. using System.Collections.Generic; using System.Linq; using System.Threading.Tasks; using Microsoft.IdentityModel.Protocols.OpenIdConnect; using Moq; using Xunit; namespace Microsoft.AspNetCore.Identity.Service { public class AuthorizationRequestFactoryTest { public static ProtocolErrorProvider ProtocolErrorProvider = new ProtocolErrorProvider(); [Fact] public async Task FailsToCreateAuthorizationRequest_IfState_HasMultipleValues() { // Arrange var parameters = new Dictionary { [OpenIdConnectParameterNames.State] = new[] { "a", "b" } }; var expectedError = new AuthorizationRequestError(ProtocolErrorProvider.TooManyParameters(OpenIdConnectParameterNames.State), null, null); var factory = CreateAuthorizationRequestFactory(); // Act var result = await factory.CreateAuthorizationRequestAsync(parameters); // Assert Assert.False(result.IsValid); Assert.Equal(expectedError, result.Error, IdentityServiceErrorComparer.Instance); Assert.Null(result.Error.RedirectUri); Assert.Null(result.Error.ResponseMode); } [Fact] public async Task FailsToCreateAuthorizationRequest_IfClientId_IsMissing() { // Arrange var parameters = new Dictionary { [OpenIdConnectParameterNames.State] = new[] { "state" } }; var expectedError = new AuthorizationRequestError(ProtocolErrorProvider.MissingRequiredParameter(OpenIdConnectParameterNames.ClientId), 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.Null(result.Error.RedirectUri); Assert.Null(result.Error.ResponseMode); } [Fact] public async Task FailsToCreateAuthorizationRequest_IfMultipleClientIds_ArePresent() { // Arrange var parameters = new Dictionary { [OpenIdConnectParameterNames.State] = new[] { "state" }, [OpenIdConnectParameterNames.ClientId] = new[] { "a", "b" } }; var expectedError = new AuthorizationRequestError(ProtocolErrorProvider.TooManyParameters(OpenIdConnectParameterNames.ClientId), 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.Null(result.Error.RedirectUri); Assert.Null(result.Error.ResponseMode); } [Fact] public async Task FailsToCreateAuthorizationRequest_IfClientIdValidation_Fails() { // Arrange var parameters = new Dictionary { [OpenIdConnectParameterNames.State] = new[] { "state" }, [OpenIdConnectParameterNames.ClientId] = new[] { "a" } }; var expectedError = new AuthorizationRequestError(ProtocolErrorProvider.InvalidClientId("a"), null, null); expectedError.Message.State = "state"; var factory = CreateAuthorizationRequestFactory(validClientId: false); // Act var result = await factory.CreateAuthorizationRequestAsync(parameters); // Assert Assert.False(result.IsValid); Assert.Equal(expectedError, result.Error, IdentityServiceErrorComparer.Instance); Assert.Null(result.Error.RedirectUri); Assert.Null(result.Error.ResponseMode); } [Fact] public async Task FailsToCreateAuthorizationRequest_IfMultipleRedirectUris_ArePresent() { // Arrange var parameters = new Dictionary { [OpenIdConnectParameterNames.State] = new[] { "state" }, [OpenIdConnectParameterNames.ClientId] = new[] { "a" }, [OpenIdConnectParameterNames.RedirectUri] = new[] { "a", "b" } }; var expectedError = new AuthorizationRequestError(ProtocolErrorProvider.TooManyParameters(OpenIdConnectParameterNames.RedirectUri), 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.Null(result.Error.RedirectUri); Assert.Null(result.Error.ResponseMode); } [Fact] public async Task FailsToCreateAuthorizationRequest_RedirectUri_IsNotAbsolute() { // Arrange var parameters = new Dictionary { [OpenIdConnectParameterNames.State] = new[] { "state" }, [OpenIdConnectParameterNames.ClientId] = new[] { "a" }, [OpenIdConnectParameterNames.RedirectUri] = new[] { "/callback" } }; var expectedError = new AuthorizationRequestError(ProtocolErrorProvider.InvalidUriFormat("/callback"), null, null); expectedError.Message.State = "state"; var factory = CreateAuthorizationRequestFactory(validRedirectUri: false); // Act var result = await factory.CreateAuthorizationRequestAsync(parameters); // Assert Assert.False(result.IsValid); Assert.Equal(expectedError, result.Error, IdentityServiceErrorComparer.Instance); Assert.Null(result.Error.RedirectUri); Assert.Null(result.Error.ResponseMode); } [Fact] public async Task FailsToCreateAuthorizationRequest_RedirectUris_ContainsFragment() { // Arrange var parameters = new Dictionary { [OpenIdConnectParameterNames.State] = new[] { "state" }, [OpenIdConnectParameterNames.ClientId] = new[] { "a" }, [OpenIdConnectParameterNames.RedirectUri] = new[] { "http://www.example.com/callback#fragment" } }; var expectedError = new AuthorizationRequestError(ProtocolErrorProvider.InvalidUriFormat("http://www.example.com/callback#fragment"), null, null); expectedError.Message.State = "state"; var factory = CreateAuthorizationRequestFactory(validRedirectUri: false); // Act var result = await factory.CreateAuthorizationRequestAsync(parameters); // Assert Assert.False(result.IsValid); Assert.Equal(expectedError, result.Error, IdentityServiceErrorComparer.Instance); Assert.Null(result.Error.RedirectUri); Assert.Null(result.Error.ResponseMode); } [Fact] public async Task FailsToCreateAuthorizationRequest_RedirectUris_IsNotValid() { // Arrange var parameters = new Dictionary { [OpenIdConnectParameterNames.State] = new[] { "state" }, [OpenIdConnectParameterNames.ClientId] = new[] { "a" }, [OpenIdConnectParameterNames.RedirectUri] = new[] { "http://www.example.com/callback" } }; var expectedError = new AuthorizationRequestError(ProtocolErrorProvider.InvalidRedirectUri("http://www.example.com/callback"), null, null); expectedError.Message.State = "state"; var factory = CreateAuthorizationRequestFactory(validRedirectUri: false); // Act var result = await factory.CreateAuthorizationRequestAsync(parameters); // Assert Assert.False(result.IsValid); Assert.Equal(expectedError, result.Error, IdentityServiceErrorComparer.Instance); Assert.Null(result.Error.RedirectUri); Assert.Null(result.Error.ResponseMode); } [Fact] public async Task FailsToCreateAuthorizationRequest_ResponseTypeIsMissing() { // Arrange var parameters = new Dictionary { [OpenIdConnectParameterNames.State] = new[] { "state" }, [OpenIdConnectParameterNames.ClientId] = new[] { "a" }, [OpenIdConnectParameterNames.RedirectUri] = new[] { "http://www.example.com/callback" } }; var expectedError = new AuthorizationRequestError(ProtocolErrorProvider.MissingRequiredParameter(OpenIdConnectParameterNames.ResponseType), 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.Query, result.Error.ResponseMode); } [Fact] public async Task FailsToCreateAuthorizationRequest_ResponseType_HasMultipleValues() { // Arrange var parameters = new Dictionary { [OpenIdConnectParameterNames.State] = new[] { "state" }, [OpenIdConnectParameterNames.ClientId] = new[] { "a" }, [OpenIdConnectParameterNames.RedirectUri] = new[] { "http://www.example.com/callback" }, [OpenIdConnectParameterNames.ResponseType] = new[] { "code", "token id_token" } }; var expectedError = new AuthorizationRequestError(ProtocolErrorProvider.TooManyParameters(OpenIdConnectParameterNames.ResponseType), 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.Query, result.Error.ResponseMode); } [Fact] public async Task FailsToCreateAuthorizationRequest_ResponseType_HasInvalidValues() { // Arrange var parameters = new Dictionary { [OpenIdConnectParameterNames.State] = new[] { "state" }, [OpenIdConnectParameterNames.ClientId] = new[] { "a" }, [OpenIdConnectParameterNames.RedirectUri] = new[] { "http://www.example.com/callback" }, [OpenIdConnectParameterNames.ResponseType] = new[] { "invalid" } }; var expectedError = new AuthorizationRequestError(ProtocolErrorProvider.InvalidParameterValue("invalid", OpenIdConnectParameterNames.ResponseType), 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.Query, result.Error.ResponseMode); } [Fact] public async Task FailsToCreateAuthorizationRequest_ResponseType_ContainsOtherValuesAlongWithNone() { // Arrange var parameters = new Dictionary { [OpenIdConnectParameterNames.State] = new[] { "state" }, [OpenIdConnectParameterNames.ClientId] = new[] { "a" }, [OpenIdConnectParameterNames.RedirectUri] = new[] { "http://www.example.com/callback" }, [OpenIdConnectParameterNames.ResponseType] = new[] { "code none" } }; var expectedError = new AuthorizationRequestError(ProtocolErrorProvider.ResponseTypeNoneNotAllowed(), 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.Fragment, result.Error.ResponseMode); } [Fact] public async Task FailsToCreateAuthorizationRequest_ResponseMode_ContainsMultipleValues() { // Arrange var parameters = new Dictionary { [OpenIdConnectParameterNames.State] = new[] { "state" }, [OpenIdConnectParameterNames.ClientId] = new[] { "a" }, [OpenIdConnectParameterNames.RedirectUri] = new[] { "http://www.example.com/callback" }, [OpenIdConnectParameterNames.ResponseType] = new[] { "code" }, [OpenIdConnectParameterNames.ResponseMode] = new[] { "query", "fragment" } }; var expectedError = new AuthorizationRequestError(ProtocolErrorProvider.TooManyParameters(OpenIdConnectParameterNames.ResponseMode), 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.Query, result.Error.ResponseMode); } [Theory] [InlineData("code", "query")] [InlineData("token", "fragment")] [InlineData("id_token", "fragment")] [InlineData("code id_token", "fragment")] [InlineData("code token", "fragment")] [InlineData("code token id_token", "fragment")] public async Task FailsToCreateAuthorizationRequest_ResponseMode_ContainsInvalidValues(string responseType, string errorResponseMode) { // Arrange var parameters = new Dictionary { [OpenIdConnectParameterNames.State] = new[] { "state" }, [OpenIdConnectParameterNames.ClientId] = new[] { "a" }, [OpenIdConnectParameterNames.RedirectUri] = new[] { "http://www.example.com/callback" }, [OpenIdConnectParameterNames.ResponseType] = new[] { responseType }, [OpenIdConnectParameterNames.ResponseMode] = new[] { "invalid" } }; var expectedError = new AuthorizationRequestError(ProtocolErrorProvider.InvalidParameterValue("invalid", OpenIdConnectParameterNames.ResponseMode), 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(errorResponseMode, result.Error.ResponseMode); } [Theory] [InlineData("token", "query")] [InlineData("id_token", "query")] [InlineData("code id_token", "query")] [InlineData("code token", "query")] [InlineData("code token id_token", "query")] public async Task FailsToCreateAuthorizationRequest_ResponseModeAndResponseType_AreIncompatible(string responseType, string responseMode) { // Arrange var parameters = new Dictionary { [OpenIdConnectParameterNames.State] = new[] { "state" }, [OpenIdConnectParameterNames.ClientId] = new[] { "a" }, [OpenIdConnectParameterNames.RedirectUri] = new[] { "http://www.example.com/callback" }, [OpenIdConnectParameterNames.ResponseType] = new[] { responseType }, [OpenIdConnectParameterNames.ResponseMode] = new[] { responseMode } }; var expectedError = new AuthorizationRequestError(ProtocolErrorProvider.InvalidResponseTypeModeCombination(responseType, responseMode), 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.Query, result.Error.ResponseMode); } [Theory] [InlineData("token")] [InlineData("id_token")] [InlineData("token id_token")] [InlineData("code token")] [InlineData("code id_token")] [InlineData("code token id_token")] public async Task FailsToCreateAuthorizationRequest_NonceIsRequired_ForHybridAndImplicitFlows(string responseType) { // Arrange var parameters = new Dictionary { [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" } }; var expectedError = new AuthorizationRequestError(ProtocolErrorProvider.MissingRequiredParameter(OpenIdConnectParameterNames.Nonce), 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); } [Theory] [InlineData("code")] [InlineData("token")] [InlineData("id_token")] [InlineData("code token")] [InlineData("code id_token")] [InlineData("token id_token")] [InlineData("code token id_token")] public async Task FailsToCreateAuthorizationRequest_NonceFails_IfMultipleNoncesArePresent(string responseType) { // Arrange var parameters = new Dictionary { [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.Nonce] = new[] { "asdf", "qwert" } }; var expectedError = new AuthorizationRequestError(ProtocolErrorProvider.TooManyParameters(OpenIdConnectParameterNames.Nonce), 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); } [Theory] [InlineData(null)] [InlineData("")] [InlineData(" ")] public async Task FailsToCreateAuthorizationRequest_IfScope_IsMissingOrEmpty(string scope) { // Arrange var parameters = new Dictionary { [OpenIdConnectParameterNames.State] = new[] { "state" }, [OpenIdConnectParameterNames.ClientId] = new[] { "a" }, [OpenIdConnectParameterNames.RedirectUri] = new[] { "http://www.example.com/callback" }, [OpenIdConnectParameterNames.ResponseType] = new[] { "code" }, [OpenIdConnectParameterNames.ResponseMode] = new[] { "form_post" }, }; if (scope != null) { parameters[OpenIdConnectParameterNames.Scope] = new[] { scope }; } var expectedError = new AuthorizationRequestError(ProtocolErrorProvider.MissingRequiredParameter(OpenIdConnectParameterNames.Scope), 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); } [Theory] [InlineData("id_token")] [InlineData("code id_token")] [InlineData("token id_token")] [InlineData("code id_token token")] public async Task FailsToCreateAuthorizationRequest_IfRequestAsksForIdToken_ButOpenIdScopeIsMissing(string responseType) { // Arrange var parameters = new Dictionary { [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); } [Fact] public async Task FailsToCreateAuthorizationRequest_Scope_HasMultipleValues() { // Arrange var parameters = new Dictionary { [OpenIdConnectParameterNames.State] = new[] { "state" }, [OpenIdConnectParameterNames.ClientId] = new[] { "a" }, [OpenIdConnectParameterNames.RedirectUri] = new[] { "http://www.example.com/callback" }, [OpenIdConnectParameterNames.ResponseType] = new[] { "code" }, [OpenIdConnectParameterNames.ResponseMode] = new[] { "form_post" }, [OpenIdConnectParameterNames.Scope] = new[] { "openid", "profile" }, }; var expectedError = new AuthorizationRequestError(ProtocolErrorProvider.TooManyParameters(OpenIdConnectParameterNames.Scope), 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); } [Fact] public async Task FailsToCreateAuthorizationRequest_IfScopesResolver_DeterminesThereAreInvalidScopes() { // Arrange var parameters = new Dictionary { [OpenIdConnectParameterNames.State] = new[] { "state" }, [OpenIdConnectParameterNames.ClientId] = new[] { "a" }, [OpenIdConnectParameterNames.RedirectUri] = new[] { "http://www.example.com/callback" }, [OpenIdConnectParameterNames.ResponseType] = new[] { "code" }, [OpenIdConnectParameterNames.ResponseMode] = new[] { "form_post" }, [OpenIdConnectParameterNames.Scope] = new[] { "openid invalid" }, }; var expectedError = new AuthorizationRequestError(ProtocolErrorProvider.InvalidScope("openid"), null, null); expectedError.Message.State = "state"; var factory = CreateAuthorizationRequestFactory(validClientId: true, validRedirectUri: true, validScopes: false); // 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); } [Fact] public async Task FailsToCreateAuthorizationRequest_Prompt_IncludesNoneAndOtherValues() { // Arrange var parameters = new Dictionary { [OpenIdConnectParameterNames.ClientId] = new[] { "a" }, [OpenIdConnectParameterNames.RedirectUri] = new[] { "http://www.example.com/callback" }, [OpenIdConnectParameterNames.ResponseType] = new[] { "code" }, [OpenIdConnectParameterNames.ResponseMode] = new[] { "form_post" }, [OpenIdConnectParameterNames.Nonce] = new[] { "asdf" }, [OpenIdConnectParameterNames.Scope] = new[] { " openid profile " }, [OpenIdConnectParameterNames.State] = new[] { "state" }, [OpenIdConnectParameterNames.Prompt] = new[] { "none consent " } }; var expectedError = new AuthorizationRequestError(ProtocolErrorProvider.PromptNoneMustBeTheOnlyValue("none consent"), 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); } [Fact] public async Task FailsToCreateAuthorizationRequest_Prompt_IncludesUnknownValue() { // Arrange var parameters = new Dictionary { [OpenIdConnectParameterNames.ClientId] = new[] { "a" }, [OpenIdConnectParameterNames.RedirectUri] = new[] { "http://www.example.com/callback" }, [OpenIdConnectParameterNames.ResponseType] = new[] { "code" }, [OpenIdConnectParameterNames.ResponseMode] = new[] { "form_post" }, [OpenIdConnectParameterNames.Nonce] = new[] { "asdf" }, [OpenIdConnectParameterNames.Scope] = new[] { " openid profile " }, [OpenIdConnectParameterNames.State] = new[] { "state" }, [OpenIdConnectParameterNames.Prompt] = new[] { "login consent select_account unknown" } }; var expectedError = new AuthorizationRequestError(ProtocolErrorProvider.InvalidPromptValue("unknown"), 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); } [Theory] [InlineData("none")] [InlineData("login")] [InlineData("consent")] [InlineData("select_account")] [InlineData("login consent")] [InlineData("login select_account")] [InlineData("consent select_account")] [InlineData("login consent select_account")] public async Task SuccessfullyCreatesARequest_WithAnyValidCombinationOfPromptValues(string promptValues) { // Arrange var parameters = new Dictionary { [OpenIdConnectParameterNames.ClientId] = new[] { "a" }, [OpenIdConnectParameterNames.RedirectUri] = new[] { "http://www.example.com/callback" }, [OpenIdConnectParameterNames.ResponseType] = new[] { "code" }, [OpenIdConnectParameterNames.ResponseMode] = new[] { "form_post" }, [OpenIdConnectParameterNames.Nonce] = new[] { "asdf" }, [OpenIdConnectParameterNames.Scope] = new[] { " openid profile " }, [OpenIdConnectParameterNames.State] = new[] { "state" }, [OpenIdConnectParameterNames.Prompt] = new[] { promptValues } }; var expectedError = new AuthorizationRequestError(ProtocolErrorProvider.InvalidPromptValue("unknown"), null, null); var factory = CreateAuthorizationRequestFactory(); // Act var result = await factory.CreateAuthorizationRequestAsync(parameters); // Assert Assert.True(result.IsValid); } [Fact] public async Task FailsToCreateAuthorizationRequest_CodeChallenge_HasMultipleValues() { // Arrange var parameters = new Dictionary { [OpenIdConnectParameterNames.ClientId] = new[] { "a" }, [OpenIdConnectParameterNames.RedirectUri] = new[] { "http://www.example.com/callback" }, [OpenIdConnectParameterNames.ResponseType] = new[] { "code" }, [OpenIdConnectParameterNames.ResponseMode] = new[] { "form_post" }, [OpenIdConnectParameterNames.Nonce] = new[] { "asdf" }, [OpenIdConnectParameterNames.Scope] = new[] { " openid profile " }, [OpenIdConnectParameterNames.State] = new[] { "state" }, [ProofOfKeyForCodeExchangeParameterNames.CodeChallenge] = new[] { "challenge1", "challenge2" } }; var expectedError = new AuthorizationRequestError(ProtocolErrorProvider.TooManyParameters(ProofOfKeyForCodeExchangeParameterNames.CodeChallenge), 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); } [Theory] [InlineData("tooshort")] [InlineData("toolong_aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa")] public async Task FailsToCreateAuthorizationRequest_CodeChallenge_DoesNotHave43Characters(string challenge) { // Arrange var parameters = new Dictionary { [OpenIdConnectParameterNames.ClientId] = new[] { "a" }, [OpenIdConnectParameterNames.RedirectUri] = new[] { "http://www.example.com/callback" }, [OpenIdConnectParameterNames.ResponseType] = new[] { "code" }, [OpenIdConnectParameterNames.ResponseMode] = new[] { "form_post" }, [OpenIdConnectParameterNames.Nonce] = new[] { "asdf" }, [OpenIdConnectParameterNames.Scope] = new[] { " openid profile " }, [OpenIdConnectParameterNames.State] = new[] { "state" }, [ProofOfKeyForCodeExchangeParameterNames.CodeChallenge] = new[] { challenge } }; var expectedError = new AuthorizationRequestError(ProtocolErrorProvider.InvalidCodeChallenge(), "http://www.example.com/callback", "form_post"); 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); } [Fact] public async Task FailsToCreateAuthorizationRequest_CodeChallengeMethod_IsMissing() { // Arrange var parameters = new Dictionary { [OpenIdConnectParameterNames.ClientId] = new[] { "a" }, [OpenIdConnectParameterNames.RedirectUri] = new[] { "http://www.example.com/callback" }, [OpenIdConnectParameterNames.ResponseType] = new[] { "code" }, [OpenIdConnectParameterNames.ResponseMode] = new[] { "form_post" }, [OpenIdConnectParameterNames.Nonce] = new[] { "asdf" }, [OpenIdConnectParameterNames.Scope] = new[] { " openid profile " }, [OpenIdConnectParameterNames.State] = new[] { "state" }, [ProofOfKeyForCodeExchangeParameterNames.CodeChallenge] = new[] { "0123456789012345678901234567890123456789012" } }; var expectedError = new AuthorizationRequestError(ProtocolErrorProvider.MissingRequiredParameter(ProofOfKeyForCodeExchangeParameterNames.CodeChallengeMethod), "http://www.example.com/callback", "form_post"); 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); } [Fact] public async Task FailsToCreateAuthorizationRequest_CodeChallengeMethod_HasMultipleValues() { // Arrange var parameters = new Dictionary { [OpenIdConnectParameterNames.ClientId] = new[] { "a" }, [OpenIdConnectParameterNames.RedirectUri] = new[] { "http://www.example.com/callback" }, [OpenIdConnectParameterNames.ResponseType] = new[] { "code" }, [OpenIdConnectParameterNames.ResponseMode] = new[] { "form_post" }, [OpenIdConnectParameterNames.Nonce] = new[] { "asdf" }, [OpenIdConnectParameterNames.Scope] = new[] { " openid profile " }, [OpenIdConnectParameterNames.State] = new[] { "state" }, [ProofOfKeyForCodeExchangeParameterNames.CodeChallenge] = new[] { "0123456789012345678901234567890123456789012" }, [ProofOfKeyForCodeExchangeParameterNames.CodeChallengeMethod] = new[] { "S256", "plain" } }; var expectedError = new AuthorizationRequestError(ProtocolErrorProvider.TooManyParameters(ProofOfKeyForCodeExchangeParameterNames.CodeChallengeMethod), "http://www.example.com/callback", "form_post"); 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); } [Fact] public async Task FailsToCreateAuthorizationRequest_CodeChallengeMethod_IsNotSHA256() { // Arrange var parameters = new Dictionary { [OpenIdConnectParameterNames.ClientId] = new[] { "a" }, [OpenIdConnectParameterNames.RedirectUri] = new[] { "http://www.example.com/callback" }, [OpenIdConnectParameterNames.ResponseType] = new[] { "code" }, [OpenIdConnectParameterNames.ResponseMode] = new[] { "form_post" }, [OpenIdConnectParameterNames.Nonce] = new[] { "asdf" }, [OpenIdConnectParameterNames.Scope] = new[] { " openid profile " }, [OpenIdConnectParameterNames.State] = new[] { "state" }, [ProofOfKeyForCodeExchangeParameterNames.CodeChallenge] = new[] { "0123456789012345678901234567890123456789012" }, [ProofOfKeyForCodeExchangeParameterNames.CodeChallengeMethod] = new[] { "plain" } }; var expectedError = new AuthorizationRequestError(ProtocolErrorProvider.InvalidCodeChallengeMethod("plain"), 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); } [Fact] public async Task CreatesAnAuthorizationRequest_IfAllParameters_AreCorrect() { // Arrange var parameters = new Dictionary { [OpenIdConnectParameterNames.ClientId] = new[] { "a" }, [OpenIdConnectParameterNames.RedirectUri] = new[] { "http://www.example.com/callback" }, [OpenIdConnectParameterNames.ResponseType] = new[] { "code" }, [OpenIdConnectParameterNames.ResponseMode] = new[] { "form_post" }, [OpenIdConnectParameterNames.Nonce] = new[] { "asdf" }, [OpenIdConnectParameterNames.Scope] = new[] { " openid profile " }, [OpenIdConnectParameterNames.State] = new[] { "state" }, }; var factory = CreateAuthorizationRequestFactory(); // Act var result = await factory.CreateAuthorizationRequestAsync(parameters); // Assert Assert.True(result.IsValid); var request = result.Message; Assert.NotNull(request); Assert.Equal("a", request.ClientId); Assert.Equal("http://www.example.com/callback", request.RedirectUri); Assert.Equal("code", request.ResponseType); Assert.Equal(OpenIdConnectResponseMode.FormPost, request.ResponseMode); Assert.Equal("asdf", request.Nonce); Assert.Equal("state", request.State); Assert.Equal(new[] { ApplicationScope.OpenId, ApplicationScope.Profile }, result.RequestGrants.Scopes); Assert.Equal(new[] { TokenTypes.AuthorizationCode }, result.RequestGrants.Tokens); Assert.Equal(OpenIdConnectResponseMode.FormPost, result.RequestGrants.ResponseMode); Assert.Equal("http://www.example.com/callback", result.RequestGrants.RedirectUri); Assert.Empty(result.RequestGrants.Claims); } [Fact] public async Task CreateAuthorizationRequest_AsignsRegisteredRedirectUriIfMissing() { // Arrange var parameters = new Dictionary { [OpenIdConnectParameterNames.ClientId] = new[] { "a" }, [OpenIdConnectParameterNames.ResponseType] = new[] { "code" }, [OpenIdConnectParameterNames.ResponseMode] = new[] { "form_post" }, [OpenIdConnectParameterNames.Nonce] = new[] { "asdf" }, [OpenIdConnectParameterNames.Scope] = new[] { " openid profile " }, [OpenIdConnectParameterNames.State] = new[] { "state" }, }; var factory = CreateAuthorizationRequestFactory(); // Act var result = await factory.CreateAuthorizationRequestAsync(parameters); // Assert Assert.True(result.IsValid); var request = result.Message; Assert.NotNull(request); Assert.Equal("a", request.ClientId); Assert.Null(request.RedirectUri); Assert.Equal("code", request.ResponseType); Assert.Equal(OpenIdConnectResponseMode.FormPost, request.ResponseMode); Assert.Equal("asdf", request.Nonce); Assert.Equal("state", request.State); Assert.Equal(new[] { ApplicationScope.OpenId, ApplicationScope.Profile }, result.RequestGrants.Scopes); Assert.Equal(new[] { TokenTypes.AuthorizationCode }, result.RequestGrants.Tokens); Assert.Equal(OpenIdConnectResponseMode.FormPost, result.RequestGrants.ResponseMode); Assert.Equal("http://www.example.com/registered", result.RequestGrants.RedirectUri); Assert.Empty(result.RequestGrants.Claims); } private static IAuthorizationRequestFactory CreateAuthorizationRequestFactory(bool validClientId = true, bool validRedirectUri = true, bool validScopes = true) { var clientIdValidator = new Mock(); clientIdValidator .Setup(c => c.ValidateClientIdAsync(It.IsAny())) .ReturnsAsync(validClientId); var redirectUriValidatorMock = new Mock(); redirectUriValidatorMock .Setup(m => m.ResolveRedirectUriAsync(It.IsAny(), It.IsAny())) .ReturnsAsync((string clientId, string redirectUrl) => validRedirectUri ? RedirectUriResolutionResult.Valid(redirectUrl ?? "http://www.example.com/registered") : RedirectUriResolutionResult.Invalid(ProtocolErrorProvider.InvalidRedirectUri(redirectUrl))); var scopeValidatorMock = new Mock(); scopeValidatorMock .Setup(m => m.ResolveScopesAsync(It.IsAny(), It.IsAny>())) .ReturnsAsync( (string clientId, IEnumerable scopes) => validScopes ? ScopeResolutionResult.Valid(scopes.Select(s => ApplicationScope.CanonicalScopes.TryGetValue(s, out var parsedScope) ? parsedScope : new ApplicationScope(clientId, s))) : ScopeResolutionResult.Invalid(ProtocolErrorProvider.InvalidScope(scopes.First()))); return new AuthorizationRequestFactory( clientIdValidator.Object, redirectUriValidatorMock.Object, scopeValidatorMock.Object, Enumerable.Empty(), new ProtocolErrorProvider()); } } }