Add support for proof of key for code exchange
This commit is contained in:
parent
be8232d3f9
commit
6a3fca86b5
|
|
@ -29,5 +29,7 @@ namespace Microsoft.AspNetCore.Identity.Service
|
|||
public const string GrantedToken = "g_token";
|
||||
public const string TenantId = "tid";
|
||||
public const string Resource = "rid";
|
||||
public const string CodeChallenge = "c_chall";
|
||||
public const string CodeChallengeMethod = "c_chall_m";
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -0,0 +1,11 @@
|
|||
// 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.
|
||||
|
||||
namespace Microsoft.AspNetCore.Identity.Service
|
||||
{
|
||||
public static class ProofOfKeyForCodeExchangeChallengeMethods
|
||||
{
|
||||
public const string Plain = "plain";
|
||||
public const string SHA256 = "S256";
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,12 @@
|
|||
// 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.
|
||||
|
||||
namespace Microsoft.AspNetCore.Identity.Service
|
||||
{
|
||||
public static class ProofOfKeyForCodeExchangeParameterNames
|
||||
{
|
||||
public const string CodeChallengeMethod = "code_challenge_method";
|
||||
public const string CodeChallenge = "code_challenge";
|
||||
public const string CodeVerifier = "code_verifier";
|
||||
}
|
||||
}
|
||||
|
|
@ -1,10 +1,12 @@
|
|||
// 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.Diagnostics;
|
||||
using Microsoft.IdentityModel.Protocols.OpenIdConnect;
|
||||
|
||||
namespace Microsoft.AspNetCore.Identity.Service
|
||||
{
|
||||
[DebuggerDisplay("{DebuggerDisplay(),nq}")]
|
||||
public class AuthorizationRequestError
|
||||
{
|
||||
public AuthorizationRequestError(OpenIdConnectMessage error, string redirectUri, string responseMode)
|
||||
|
|
@ -19,5 +21,8 @@ namespace Microsoft.AspNetCore.Identity.Service
|
|||
public string RedirectUri { get; set; }
|
||||
|
||||
public string ResponseMode { get; set; }
|
||||
|
||||
private string DebuggerDisplay() =>
|
||||
$"{Message.Error} - {Message.ErrorDescription} - {RedirectUri} - {ResponseMode} - {Message.State}";
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -29,6 +29,8 @@ namespace Microsoft.AspNetCore.Identity.Service
|
|||
public string ClientId => GetClaimValue(IdentityServiceClaimTypes.ClientId);
|
||||
public string Resource => GetClaimValue(IdentityServiceClaimTypes.Resource);
|
||||
public string RedirectUri => GetClaimValue(IdentityServiceClaimTypes.RedirectUri);
|
||||
public string CodeChallenge => GetClaimValue(IdentityServiceClaimTypes.CodeChallenge);
|
||||
public string CodeChallengeMethod => GetClaimValue(IdentityServiceClaimTypes.CodeChallengeMethod);
|
||||
public IEnumerable<string> Scopes => GetClaimValuesOrEmpty(IdentityServiceClaimTypes.Scope);
|
||||
public IEnumerable<string> GrantedTokens => GetClaimValuesOrEmpty(IdentityServiceClaimTypes.GrantedToken);
|
||||
public string Nonce => GetClaimValue(IdentityServiceClaimTypes.Nonce);
|
||||
|
|
|
|||
|
|
@ -192,6 +192,43 @@ namespace Microsoft.AspNetCore.Identity.Service
|
|||
}
|
||||
}
|
||||
|
||||
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;
|
||||
|
||||
|
|
@ -394,5 +431,6 @@ namespace Microsoft.AspNetCore.Identity.Service
|
|||
|
||||
return (clientId, resolvedUriResult.Uri, null);
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -0,0 +1,30 @@
|
|||
// 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.Threading.Tasks;
|
||||
using Microsoft.AspNetCore.Identity.Service.Claims;
|
||||
|
||||
namespace Microsoft.AspNetCore.Identity.Service.Core.Claims
|
||||
{
|
||||
public class ProofOfKeyForCodeExchangeTokenClaimsProvider : ITokenClaimsProvider
|
||||
{
|
||||
public int Order => 100;
|
||||
|
||||
public Task OnGeneratingClaims(TokenGeneratingContext context)
|
||||
{
|
||||
if(context.IsContextForTokenTypes(TokenTypes.AuthorizationCode) &&
|
||||
context.RequestParameters.Parameters.ContainsKey(ProofOfKeyForCodeExchangeParameterNames.CodeChallenge))
|
||||
{
|
||||
context.AddClaimToCurrentToken(
|
||||
IdentityServiceClaimTypes.CodeChallenge,
|
||||
context.RequestParameters.Parameters[ProofOfKeyForCodeExchangeParameterNames.CodeChallenge]);
|
||||
|
||||
context.AddClaimToCurrentToken(
|
||||
IdentityServiceClaimTypes.CodeChallengeMethod,
|
||||
context.RequestParameters.Parameters[ProofOfKeyForCodeExchangeParameterNames.CodeChallengeMethod]);
|
||||
}
|
||||
|
||||
return Task.CompletedTask;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -1,6 +1,7 @@
|
|||
// 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;
|
||||
using Microsoft.IdentityModel.Protocols.OpenIdConnect;
|
||||
|
||||
namespace Microsoft.AspNetCore.Identity.Service
|
||||
|
|
@ -78,6 +79,13 @@ namespace Microsoft.AspNetCore.Identity.Service
|
|||
$"The token is not yet active or it has expired.");
|
||||
}
|
||||
|
||||
public virtual OpenIdConnectMessage InvalidCodeVerifier()
|
||||
{
|
||||
return CreateError(
|
||||
IdentityServiceErrorCodes.InvalidRequest,
|
||||
$"The code_verifier is missing or invalid.");
|
||||
}
|
||||
|
||||
public virtual OpenIdConnectMessage MultipleResourcesNotSupported(string resourceName, string name)
|
||||
{
|
||||
return CreateError(
|
||||
|
|
@ -143,6 +151,20 @@ namespace Microsoft.AspNetCore.Identity.Service
|
|||
$"The prompt value 'none' can't be used in conjunction with other prompt values '{promptValue}'");
|
||||
}
|
||||
|
||||
public virtual OpenIdConnectMessage InvalidCodeChallengeMethod(string challengeMethod)
|
||||
{
|
||||
return CreateError(
|
||||
IdentityServiceErrorCodes.InvalidRequest,
|
||||
$"The code challenge method '{challengeMethod ?? "plain"}' is invalid. Only S256 is supported.");
|
||||
}
|
||||
|
||||
public virtual OpenIdConnectMessage InvalidCodeChallenge()
|
||||
{
|
||||
return CreateError(
|
||||
IdentityServiceErrorCodes.InvalidRequest,
|
||||
$"The provided code challenge must be 43 characters long.");
|
||||
}
|
||||
|
||||
private OpenIdConnectMessage CreateError(string code, string description, string uri = null) =>
|
||||
new OpenIdConnectMessage
|
||||
{
|
||||
|
|
|
|||
|
|
@ -4,13 +4,17 @@
|
|||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
using System.Threading.Tasks;
|
||||
using Microsoft.IdentityModel.Protocols.OpenIdConnect;
|
||||
using Microsoft.IdentityModel.Tokens;
|
||||
|
||||
namespace Microsoft.AspNetCore.Identity.Service
|
||||
{
|
||||
public class TokenRequestFactory : ITokenRequestFactory
|
||||
{
|
||||
private static bool[] ValidCodeVerifierCharacters = CreateCodeVerifierValidCharacters();
|
||||
|
||||
private readonly IClientIdValidator _clientIdValidator;
|
||||
private readonly ITokenManager _tokenManager;
|
||||
private readonly IRedirectUriResolver _redirectUriValidator;
|
||||
|
|
@ -185,16 +189,18 @@ namespace Microsoft.AspNetCore.Identity.Service
|
|||
string clientId,
|
||||
AuthorizationGrant consentGrant)
|
||||
{
|
||||
if (!(consentGrant.Token is AuthorizationCode code))
|
||||
{
|
||||
throw new InvalidOperationException("Granted token must be an authorization code.");
|
||||
}
|
||||
|
||||
var (redirectUri, redirectUriError) = RequestParametersHelper.ValidateOptionalParameterIsUnique(requestParameters, OpenIdConnectParameterNames.RedirectUri, _errorProvider);
|
||||
if (redirectUriError != null)
|
||||
{
|
||||
return redirectUriError;
|
||||
}
|
||||
|
||||
var tokenRedirectUri = consentGrant
|
||||
.Token.SingleOrDefault(c =>
|
||||
string.Equals(c.Type, IdentityServiceClaimTypes.RedirectUri, StringComparison.Ordinal))?.Value;
|
||||
|
||||
var tokenRedirectUri = code.RedirectUri;
|
||||
if (redirectUri == null && tokenRedirectUri != null)
|
||||
{
|
||||
return _errorProvider.MissingRequiredParameter(OpenIdConnectParameterNames.RedirectUri);
|
||||
|
|
@ -211,8 +217,48 @@ namespace Microsoft.AspNetCore.Identity.Service
|
|||
return _errorProvider.InvalidRedirectUri(redirectUri);
|
||||
}
|
||||
|
||||
if (code.CodeChallenge != null)
|
||||
{
|
||||
if (!ProofOfKeyForCodeExchangeChallengeMethods.SHA256.Equals(code.CodeChallengeMethod, StringComparison.Ordinal))
|
||||
{
|
||||
throw new InvalidOperationException("Unsupported code challenge method.");
|
||||
}
|
||||
|
||||
var (verifier, verifierError) = RequestParametersHelper.ValidateParameterIsUnique(requestParameters, ProofOfKeyForCodeExchangeParameterNames.CodeVerifier, _errorProvider);
|
||||
if (verifierError != null)
|
||||
{
|
||||
return verifierError;
|
||||
}
|
||||
|
||||
// code-verifier = [a-zA-Z0-9\-._~]{43,128}
|
||||
if (verifier.Length < 43 || verifier.Length > 128)
|
||||
{
|
||||
return _errorProvider.InvalidCodeVerifier();
|
||||
}
|
||||
|
||||
for (var i = 0; i < verifier.Length; i++)
|
||||
{
|
||||
if (verifier[i] > 127 || !ValidCodeVerifierCharacters[verifier[i]])
|
||||
{
|
||||
return _errorProvider.InvalidCodeVerifier();
|
||||
}
|
||||
}
|
||||
|
||||
if (!string.Equals(code.CodeChallenge, GetComputedChallenge(verifier), StringComparison.Ordinal))
|
||||
{
|
||||
return _errorProvider.InvalidCodeVerifier();
|
||||
}
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
private string GetComputedChallenge(string verifier)
|
||||
{
|
||||
using (var hash = CryptographyHelpers.CreateSHA256())
|
||||
{
|
||||
return Base64UrlEncoder.Encode(hash.ComputeHash(Encoding.ASCII.GetBytes(verifier)));
|
||||
}
|
||||
}
|
||||
|
||||
private string GetGrantTypeParameter(IDictionary<string, string[]> parameters, string grantType)
|
||||
{
|
||||
|
|
@ -226,5 +272,32 @@ namespace Microsoft.AspNetCore.Identity.Service
|
|||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
// "0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ~-._"
|
||||
private static bool[] CreateCodeVerifierValidCharacters()
|
||||
{
|
||||
var result = new bool[128];
|
||||
for (var i = 0x41; i <= 0x5A; i++)
|
||||
{
|
||||
result[i] = true;
|
||||
}
|
||||
|
||||
for (var i = 0x61; i <= 0x7A; i++)
|
||||
{
|
||||
result[i] = true;
|
||||
}
|
||||
|
||||
for (var i = 0x30; i <= 0x39; i++)
|
||||
{
|
||||
result[i] = true;
|
||||
}
|
||||
|
||||
result['-'] = true;
|
||||
result['.'] = true;
|
||||
result['_'] = true;
|
||||
result['~'] = true;
|
||||
|
||||
return result;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -14,6 +14,7 @@ using Microsoft.AspNetCore.Identity.Service;
|
|||
using Microsoft.AspNetCore.Identity.Service.Claims;
|
||||
using Microsoft.AspNetCore.Identity.Service.Configuration;
|
||||
using Microsoft.AspNetCore.Identity.Service.Core;
|
||||
using Microsoft.AspNetCore.Identity.Service.Core.Claims;
|
||||
using Microsoft.AspNetCore.Identity.Service.Metadata;
|
||||
using Microsoft.AspNetCore.Identity.Service.Serialization;
|
||||
using Microsoft.Extensions.DependencyInjection.Extensions;
|
||||
|
|
@ -82,6 +83,7 @@ namespace Microsoft.Extensions.DependencyInjection
|
|||
services.AddSingleton<ITokenClaimsProvider, ScopesTokenClaimsProvider>();
|
||||
services.AddSingleton<ITokenClaimsProvider, TimestampsTokenClaimsProvider>();
|
||||
services.AddSingleton<ITokenClaimsProvider, TokenHashTokenClaimsProvider>();
|
||||
services.AddSingleton<ITokenClaimsProvider, ProofOfKeyForCodeExchangeTokenClaimsProvider>();
|
||||
services.AddSingleton<ProtocolErrorProvider>();
|
||||
services.AddSingleton<ISigningCredentialsSource, DeveloperCertificateSigningCredentialsSource>();
|
||||
services.AddSingleton<DeveloperCertificateSigningCredentialsSource>();
|
||||
|
|
|
|||
|
|
@ -718,6 +718,165 @@ namespace Microsoft.AspNetCore.Identity.Service
|
|||
Assert.True(result.IsValid);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public async Task FailsToCreateAuthorizationRequest_CodeChallenge_HasMultipleValues()
|
||||
{
|
||||
// Arrange
|
||||
var parameters = new Dictionary<string, string[]>
|
||||
{
|
||||
[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<string, string[]>
|
||||
{
|
||||
[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<string, string[]>
|
||||
{
|
||||
[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<string, string[]>
|
||||
{
|
||||
[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<string, string[]>
|
||||
{
|
||||
[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()
|
||||
{
|
||||
|
|
|
|||
|
|
@ -0,0 +1,92 @@
|
|||
// 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.Security.Claims;
|
||||
using System.Threading.Tasks;
|
||||
using Microsoft.IdentityModel.Protocols.OpenIdConnect;
|
||||
using Xunit;
|
||||
|
||||
namespace Microsoft.AspNetCore.Identity.Service.Core.Claims
|
||||
{
|
||||
public class ProofOfKeyForCodeExchangeTokenClaimsProviderTest
|
||||
{
|
||||
[Fact]
|
||||
public async Task OnGeneratingClaims_AddsCodeChallengeAndChallengeMethod_ToTheAuthorizationCode()
|
||||
{
|
||||
// Arrange
|
||||
var context = new TokenGeneratingContext(
|
||||
new ClaimsPrincipal(),
|
||||
new ClaimsPrincipal(),
|
||||
new OpenIdConnectMessage(new Dictionary<string, string[]>
|
||||
{
|
||||
[ProofOfKeyForCodeExchangeParameterNames.CodeChallenge] = new[] { "challenge" },
|
||||
[ProofOfKeyForCodeExchangeParameterNames.CodeChallengeMethod] = new[] { "S256" },
|
||||
}),
|
||||
new RequestGrants());
|
||||
|
||||
context.InitializeForToken(TokenTypes.AuthorizationCode);
|
||||
|
||||
var provider = new ProofOfKeyForCodeExchangeTokenClaimsProvider();
|
||||
|
||||
// Act
|
||||
await provider.OnGeneratingClaims(context);
|
||||
|
||||
// Assert
|
||||
Assert.Contains(context.CurrentClaims, c => c.Type == IdentityServiceClaimTypes.CodeChallenge && c.Value == "challenge");
|
||||
Assert.Contains(context.CurrentClaims, c => c.Type == IdentityServiceClaimTypes.CodeChallengeMethod && c.Value == "S256");
|
||||
}
|
||||
|
||||
[Theory]
|
||||
[InlineData(TokenTypes.AccessToken)]
|
||||
[InlineData(TokenTypes.IdToken)]
|
||||
[InlineData(TokenTypes.RefreshToken)]
|
||||
public async Task OnGeneratingClaims_DoesNothing_ForOtherTokenTypes(string tokenType)
|
||||
{
|
||||
// Arrange
|
||||
var context = new TokenGeneratingContext(
|
||||
new ClaimsPrincipal(),
|
||||
new ClaimsPrincipal(),
|
||||
new OpenIdConnectMessage(new Dictionary<string, string[]>
|
||||
{
|
||||
[ProofOfKeyForCodeExchangeParameterNames.CodeChallenge] = new[] { "challenge" },
|
||||
[ProofOfKeyForCodeExchangeParameterNames.CodeChallengeMethod] = new[] { "S256" },
|
||||
}),
|
||||
new RequestGrants());
|
||||
|
||||
context.InitializeForToken(tokenType);
|
||||
|
||||
var provider = new ProofOfKeyForCodeExchangeTokenClaimsProvider();
|
||||
|
||||
// Act
|
||||
await provider.OnGeneratingClaims(context);
|
||||
|
||||
// Assert
|
||||
Assert.Empty(context.CurrentClaims);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public async Task OnGeneratingClaims_DoesNothing_IfChallengeNotPresent()
|
||||
{
|
||||
// Arrange
|
||||
var context = new TokenGeneratingContext(
|
||||
new ClaimsPrincipal(),
|
||||
new ClaimsPrincipal(),
|
||||
new OpenIdConnectMessage(new Dictionary<string, string[]>
|
||||
{
|
||||
[ProofOfKeyForCodeExchangeParameterNames.CodeChallengeMethod] = new[] { "S256" },
|
||||
}),
|
||||
new RequestGrants());
|
||||
|
||||
context.InitializeForToken(TokenTypes.AuthorizationCode);
|
||||
|
||||
var provider = new ProofOfKeyForCodeExchangeTokenClaimsProvider();
|
||||
|
||||
// Act
|
||||
await provider.OnGeneratingClaims(context);
|
||||
|
||||
// Assert
|
||||
Assert.Empty(context.CurrentClaims);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -144,7 +144,7 @@ namespace Microsoft.AspNetCore.Identity.Service
|
|||
Mock.Of<IClientIdValidator>(),
|
||||
Mock.Of<IRedirectUriResolver>(), Mock.Of<IScopeResolver>(),
|
||||
Enumerable.Empty<ITokenRequestValidator>(),
|
||||
GetTestTokenManager(GetValidToken()),
|
||||
GetTestTokenManager(GetValidAuthorizationCode()),
|
||||
new TimeStampManager(), new ProtocolErrorProvider());
|
||||
|
||||
var expectedError = ProtocolErrorProvider.MissingRequiredParameter(OpenIdConnectParameterNames.ClientId);
|
||||
|
|
@ -173,7 +173,7 @@ namespace Microsoft.AspNetCore.Identity.Service
|
|||
GetClientIdValidator(isClientIdValid: true, areClientCredentialsValid: true),
|
||||
Mock.Of<IRedirectUriResolver>(), Mock.Of<IScopeResolver>(),
|
||||
Enumerable.Empty<ITokenRequestValidator>(),
|
||||
GetTestTokenManager(GetValidToken()),
|
||||
GetTestTokenManager(GetValidAuthorizationCode()),
|
||||
new TimeStampManager(), new ProtocolErrorProvider());
|
||||
|
||||
var expectedError = ProtocolErrorProvider.InvalidGrant();
|
||||
|
|
@ -200,7 +200,7 @@ namespace Microsoft.AspNetCore.Identity.Service
|
|||
GetClientIdValidator(isClientIdValid: false),
|
||||
Mock.Of<IRedirectUriResolver>(), Mock.Of<IScopeResolver>(),
|
||||
Enumerable.Empty<ITokenRequestValidator>(),
|
||||
GetTestTokenManager(GetValidToken()),
|
||||
GetTestTokenManager(GetValidAuthorizationCode()),
|
||||
new TimeStampManager(), new ProtocolErrorProvider());
|
||||
|
||||
var expectedError = ProtocolErrorProvider.InvalidClientId("clientId");
|
||||
|
|
@ -227,7 +227,7 @@ namespace Microsoft.AspNetCore.Identity.Service
|
|||
GetClientIdValidator(isClientIdValid: true, areClientCredentialsValid: false),
|
||||
Mock.Of<IRedirectUriResolver>(), Mock.Of<IScopeResolver>(),
|
||||
Enumerable.Empty<ITokenRequestValidator>(),
|
||||
GetTestTokenManager(GetValidToken()),
|
||||
GetTestTokenManager(GetValidAuthorizationCode()),
|
||||
new TimeStampManager(), new ProtocolErrorProvider());
|
||||
|
||||
var expectedError = ProtocolErrorProvider.InvalidClientCredentials();
|
||||
|
|
@ -258,7 +258,7 @@ namespace Microsoft.AspNetCore.Identity.Service
|
|||
Mock.Of<IRedirectUriResolver>(),
|
||||
Mock.Of<IScopeResolver>(),
|
||||
Enumerable.Empty<ITokenRequestValidator>(),
|
||||
GetTestTokenManager(GetValidToken()),
|
||||
GetTestTokenManager(GetValidAuthorizationCode()),
|
||||
new TimeStampManager(), new ProtocolErrorProvider());
|
||||
|
||||
var expectedError = ProtocolErrorProvider.TooManyParameters(OpenIdConnectParameterNames.Scope);
|
||||
|
|
@ -289,7 +289,7 @@ namespace Microsoft.AspNetCore.Identity.Service
|
|||
Mock.Of<IRedirectUriResolver>(),
|
||||
GetScopeResolver(hasInvalidScopes: true),
|
||||
Enumerable.Empty<ITokenRequestValidator>(),
|
||||
GetTestTokenManager(GetValidToken(),null,null,Enumerable.Empty<string>(), new[] { "openid" }),
|
||||
GetTestTokenManager(GetValidAuthorizationCode(), null, null, Enumerable.Empty<string>(), new[] { "openid" }),
|
||||
new TimeStampManager(), new ProtocolErrorProvider());
|
||||
|
||||
var expectedError = ProtocolErrorProvider.InvalidScope("invalid");
|
||||
|
|
@ -320,7 +320,7 @@ namespace Microsoft.AspNetCore.Identity.Service
|
|||
Mock.Of<IRedirectUriResolver>(),
|
||||
GetScopeResolver(hasInvalidScopes: false),
|
||||
Enumerable.Empty<ITokenRequestValidator>(),
|
||||
GetTestTokenManager(GetValidToken(), null, null, Enumerable.Empty<string>(), new[] { "openid" }),
|
||||
GetTestTokenManager(GetValidAuthorizationCode(), null, null, Enumerable.Empty<string>(), new[] { "openid" }),
|
||||
new TimeStampManager(), new ProtocolErrorProvider());
|
||||
|
||||
var expectedError = ProtocolErrorProvider.UnauthorizedScope();
|
||||
|
|
@ -350,7 +350,7 @@ namespace Microsoft.AspNetCore.Identity.Service
|
|||
GetRedirectUriValidator(isRedirectUriValid: false),
|
||||
Mock.Of<IScopeResolver>(),
|
||||
Enumerable.Empty<ITokenRequestValidator>(),
|
||||
GetTestTokenManager(GetValidToken()),
|
||||
GetTestTokenManager(GetValidAuthorizationCode()),
|
||||
new TimeStampManager(), new ProtocolErrorProvider());
|
||||
|
||||
var expectedError = ProtocolErrorProvider.MissingRequiredParameter(OpenIdConnectParameterNames.RedirectUri);
|
||||
|
|
@ -364,6 +364,214 @@ namespace Microsoft.AspNetCore.Identity.Service
|
|||
Assert.Equal(expectedError, tokenRequest.Error, IdentityServiceErrorComparer.Instance);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public async Task CreateTokenRequestAsyncFails_IfCodeVerifierIsMissing()
|
||||
{
|
||||
// Arrange
|
||||
var requestParameters = new Dictionary<string, string[]>
|
||||
{
|
||||
[OpenIdConnectParameterNames.GrantType] = new[] { "authorization_code" },
|
||||
[OpenIdConnectParameterNames.Code] = new[] { "valid" },
|
||||
[OpenIdConnectParameterNames.ClientId] = new[] { "clientId" },
|
||||
[OpenIdConnectParameterNames.RedirectUri] = new[] { "https://www.example.com" },
|
||||
};
|
||||
|
||||
var tokenRequestFactory = new TokenRequestFactory(
|
||||
GetClientIdValidator(isClientIdValid: true, areClientCredentialsValid: true),
|
||||
GetRedirectUriValidator(isRedirectUriValid: true),
|
||||
Mock.Of<IScopeResolver>(),
|
||||
Enumerable.Empty<ITokenRequestValidator>(),
|
||||
GetTestTokenManager(GetValidAuthorizationCode(new[] {
|
||||
new Claim(IdentityServiceClaimTypes.CodeChallenge,"challenge"),
|
||||
new Claim(IdentityServiceClaimTypes.CodeChallengeMethod, ProofOfKeyForCodeExchangeChallengeMethods.SHA256),
|
||||
})),
|
||||
new TimeStampManager(), new ProtocolErrorProvider());
|
||||
|
||||
var expectedError = ProtocolErrorProvider.MissingRequiredParameter(ProofOfKeyForCodeExchangeParameterNames.CodeVerifier);
|
||||
|
||||
// Act
|
||||
var tokenRequest = await tokenRequestFactory.CreateTokenRequestAsync(requestParameters);
|
||||
|
||||
// Assert
|
||||
Assert.NotNull(tokenRequest);
|
||||
Assert.False(tokenRequest.IsValid);
|
||||
Assert.Equal(expectedError, tokenRequest.Error, IdentityServiceErrorComparer.Instance);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public async Task CreateTokenRequestAsyncFails_IfCodeVerifier_HasMultipleValues()
|
||||
{
|
||||
// Arrange
|
||||
var requestParameters = new Dictionary<string, string[]>
|
||||
{
|
||||
[OpenIdConnectParameterNames.GrantType] = new[] { "authorization_code" },
|
||||
[OpenIdConnectParameterNames.Code] = new[] { "valid" },
|
||||
[OpenIdConnectParameterNames.ClientId] = new[] { "clientId" },
|
||||
[OpenIdConnectParameterNames.RedirectUri] = new[] { "https://www.example.com" },
|
||||
[ProofOfKeyForCodeExchangeParameterNames.CodeVerifier] = new[] { "value1", "value2" },
|
||||
};
|
||||
|
||||
var tokenRequestFactory = new TokenRequestFactory(
|
||||
GetClientIdValidator(isClientIdValid: true, areClientCredentialsValid: true),
|
||||
GetRedirectUriValidator(isRedirectUriValid: true),
|
||||
Mock.Of<IScopeResolver>(),
|
||||
Enumerable.Empty<ITokenRequestValidator>(),
|
||||
GetTestTokenManager(GetValidAuthorizationCode(new[] {
|
||||
new Claim(IdentityServiceClaimTypes.CodeChallenge,"challenge"),
|
||||
new Claim(IdentityServiceClaimTypes.CodeChallengeMethod, ProofOfKeyForCodeExchangeChallengeMethods.SHA256),
|
||||
})),
|
||||
new TimeStampManager(), new ProtocolErrorProvider());
|
||||
|
||||
var expectedError = ProtocolErrorProvider.TooManyParameters(ProofOfKeyForCodeExchangeParameterNames.CodeVerifier);
|
||||
|
||||
// Act
|
||||
var tokenRequest = await tokenRequestFactory.CreateTokenRequestAsync(requestParameters);
|
||||
|
||||
// Assert
|
||||
Assert.NotNull(tokenRequest);
|
||||
Assert.False(tokenRequest.IsValid);
|
||||
Assert.Equal(expectedError, tokenRequest.Error, IdentityServiceErrorComparer.Instance);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public async Task CreateTokenRequestAsyncFails_IfCodeVerifierIsInvalid()
|
||||
{
|
||||
// Arrange
|
||||
var requestParameters = new Dictionary<string, string[]>
|
||||
{
|
||||
[OpenIdConnectParameterNames.GrantType] = new[] { "authorization_code" },
|
||||
[OpenIdConnectParameterNames.Code] = new[] { "valid" },
|
||||
[OpenIdConnectParameterNames.ClientId] = new[] { "clientId" },
|
||||
[OpenIdConnectParameterNames.RedirectUri] = new[] { "https://www.example.com" },
|
||||
[ProofOfKeyForCodeExchangeParameterNames.CodeVerifier] = new[] { "@" }
|
||||
};
|
||||
|
||||
var tokenRequestFactory = new TokenRequestFactory(
|
||||
GetClientIdValidator(isClientIdValid: true, areClientCredentialsValid: true),
|
||||
GetRedirectUriValidator(isRedirectUriValid: true),
|
||||
Mock.Of<IScopeResolver>(),
|
||||
Enumerable.Empty<ITokenRequestValidator>(),
|
||||
GetTestTokenManager(GetValidAuthorizationCode(new[] {
|
||||
new Claim(IdentityServiceClaimTypes.CodeChallenge,"challenge"),
|
||||
new Claim(IdentityServiceClaimTypes.CodeChallengeMethod, ProofOfKeyForCodeExchangeChallengeMethods.SHA256),
|
||||
})),
|
||||
new TimeStampManager(), new ProtocolErrorProvider());
|
||||
|
||||
var expectedError = ProtocolErrorProvider.InvalidCodeVerifier();
|
||||
|
||||
// Act
|
||||
var tokenRequest = await tokenRequestFactory.CreateTokenRequestAsync(requestParameters);
|
||||
|
||||
// Assert
|
||||
Assert.NotNull(tokenRequest);
|
||||
Assert.False(tokenRequest.IsValid);
|
||||
Assert.Equal(expectedError, tokenRequest.Error, IdentityServiceErrorComparer.Instance);
|
||||
}
|
||||
|
||||
[Theory]
|
||||
[InlineData("tooShort")]
|
||||
[InlineData("tooLooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooong")]
|
||||
public async Task CreateTokenRequestAsyncFails_IfTooShortOrTooLong(string verifier)
|
||||
{
|
||||
// Arrange
|
||||
var requestParameters = new Dictionary<string, string[]>
|
||||
{
|
||||
[OpenIdConnectParameterNames.GrantType] = new[] { "authorization_code" },
|
||||
[OpenIdConnectParameterNames.Code] = new[] { "valid" },
|
||||
[OpenIdConnectParameterNames.ClientId] = new[] { "clientId" },
|
||||
[OpenIdConnectParameterNames.RedirectUri] = new[] { "https://www.example.com" },
|
||||
[ProofOfKeyForCodeExchangeParameterNames.CodeVerifier] = new[] { verifier }
|
||||
};
|
||||
|
||||
var tokenRequestFactory = new TokenRequestFactory(
|
||||
GetClientIdValidator(isClientIdValid: true, areClientCredentialsValid: true),
|
||||
GetRedirectUriValidator(isRedirectUriValid: true),
|
||||
Mock.Of<IScopeResolver>(),
|
||||
Enumerable.Empty<ITokenRequestValidator>(),
|
||||
GetTestTokenManager(GetValidAuthorizationCode(new[] {
|
||||
new Claim(IdentityServiceClaimTypes.CodeChallenge,"challenge"),
|
||||
new Claim(IdentityServiceClaimTypes.CodeChallengeMethod, ProofOfKeyForCodeExchangeChallengeMethods.SHA256),
|
||||
})),
|
||||
new TimeStampManager(), new ProtocolErrorProvider());
|
||||
|
||||
var expectedError = ProtocolErrorProvider.InvalidCodeVerifier();
|
||||
|
||||
// Act
|
||||
var tokenRequest = await tokenRequestFactory.CreateTokenRequestAsync(requestParameters);
|
||||
|
||||
// Assert
|
||||
Assert.NotNull(tokenRequest);
|
||||
Assert.False(tokenRequest.IsValid);
|
||||
Assert.Equal(expectedError, tokenRequest.Error, IdentityServiceErrorComparer.Instance);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public async Task CreateTokenRequestAsyncFails_IfCodeVerifierDoesNotMatchChallenge()
|
||||
{
|
||||
// Arrange
|
||||
var requestParameters = new Dictionary<string, string[]>
|
||||
{
|
||||
[OpenIdConnectParameterNames.GrantType] = new[] { "authorization_code" },
|
||||
[OpenIdConnectParameterNames.Code] = new[] { "valid" },
|
||||
[OpenIdConnectParameterNames.ClientId] = new[] { "clientId" },
|
||||
[OpenIdConnectParameterNames.RedirectUri] = new[] { "https://www.example.com" },
|
||||
[ProofOfKeyForCodeExchangeParameterNames.CodeVerifier] = new[] { "0123456789012345678901234567890123456789012" }
|
||||
};
|
||||
|
||||
var tokenRequestFactory = new TokenRequestFactory(
|
||||
GetClientIdValidator(isClientIdValid: true, areClientCredentialsValid: true),
|
||||
GetRedirectUriValidator(isRedirectUriValid: true),
|
||||
Mock.Of<IScopeResolver>(),
|
||||
Enumerable.Empty<ITokenRequestValidator>(),
|
||||
GetTestTokenManager(GetValidAuthorizationCode(new[] {
|
||||
new Claim(IdentityServiceClaimTypes.CodeChallenge,"challenge"),
|
||||
new Claim(IdentityServiceClaimTypes.CodeChallengeMethod, ProofOfKeyForCodeExchangeChallengeMethods.SHA256),
|
||||
})),
|
||||
new TimeStampManager(), new ProtocolErrorProvider());
|
||||
|
||||
var expectedError = ProtocolErrorProvider.InvalidCodeVerifier();
|
||||
|
||||
// Act
|
||||
var tokenRequest = await tokenRequestFactory.CreateTokenRequestAsync(requestParameters);
|
||||
|
||||
// Assert
|
||||
Assert.NotNull(tokenRequest);
|
||||
Assert.False(tokenRequest.IsValid);
|
||||
Assert.Equal(expectedError, tokenRequest.Error, IdentityServiceErrorComparer.Instance);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public async Task CreateTokenRequestSucceeds_IfCodeVerifier_MatchesChallenge()
|
||||
{
|
||||
// Arrange
|
||||
var requestParameters = new Dictionary<string, string[]>
|
||||
{
|
||||
[OpenIdConnectParameterNames.GrantType] = new[] { "authorization_code" },
|
||||
[OpenIdConnectParameterNames.Code] = new[] { "valid" },
|
||||
[OpenIdConnectParameterNames.ClientId] = new[] { "clientId" },
|
||||
[OpenIdConnectParameterNames.RedirectUri] = new[] { "https://www.example.com" },
|
||||
[ProofOfKeyForCodeExchangeParameterNames.CodeVerifier] = new[] { "0123456789012345678901234567890123456789012" }
|
||||
};
|
||||
|
||||
var tokenRequestFactory = new TokenRequestFactory(
|
||||
GetClientIdValidator(isClientIdValid: true, areClientCredentialsValid: true),
|
||||
GetRedirectUriValidator(isRedirectUriValid: true),
|
||||
Mock.Of<IScopeResolver>(),
|
||||
Enumerable.Empty<ITokenRequestValidator>(),
|
||||
GetTestTokenManager(GetValidAuthorizationCode(new[] {
|
||||
new Claim(IdentityServiceClaimTypes.CodeChallenge,"_RpfHqw8pAZIomzVUE7sjRmHSM543WVdC4o-Kc4_3C0"),
|
||||
new Claim(IdentityServiceClaimTypes.CodeChallengeMethod, ProofOfKeyForCodeExchangeChallengeMethods.SHA256),
|
||||
})),
|
||||
new TimeStampManager(), new ProtocolErrorProvider());
|
||||
|
||||
// Act
|
||||
var tokenRequest = await tokenRequestFactory.CreateTokenRequestAsync(requestParameters);
|
||||
|
||||
// Assert
|
||||
Assert.NotNull(tokenRequest);
|
||||
Assert.True(tokenRequest.IsValid);
|
||||
}
|
||||
|
||||
private IRedirectUriResolver GetRedirectUriValidator(bool isRedirectUriValid)
|
||||
{
|
||||
var mock = new Mock<IRedirectUriResolver>();
|
||||
|
|
@ -390,7 +598,7 @@ namespace Microsoft.AspNetCore.Identity.Service
|
|||
GetClientIdValidator(isClientIdValid: true, areClientCredentialsValid: true),
|
||||
Mock.Of<IRedirectUriResolver>(), Mock.Of<IScopeResolver>(),
|
||||
Enumerable.Empty<ITokenRequestValidator>(),
|
||||
GetTestTokenManager(GetValidToken()),
|
||||
GetTestTokenManager(GetValidAuthorizationCode()),
|
||||
new TimeStampManager(), new ProtocolErrorProvider());
|
||||
|
||||
var expectedError = ProtocolErrorProvider.MissingRequiredParameter(OpenIdConnectParameterNames.RedirectUri);
|
||||
|
|
@ -419,21 +627,25 @@ namespace Microsoft.AspNetCore.Identity.Service
|
|||
return clientIdValidator.Object;
|
||||
}
|
||||
|
||||
private Token GetValidToken()
|
||||
private Token GetValidAuthorizationCode(IEnumerable<Claim> additionalClaims = null)
|
||||
{
|
||||
var notBefore = EpochTime.GetIntDate(DateTime.UtcNow - TimeSpan.FromMinutes(20)).ToString();
|
||||
var expires = EpochTime.GetIntDate(DateTime.UtcNow + TimeSpan.FromMinutes(10)).ToString();
|
||||
var issuedAt = EpochTime.GetIntDate(DateTime.UtcNow).ToString();
|
||||
var authorizedParty = "clientId";
|
||||
return new TestToken(new Claim[]
|
||||
|
||||
return new AuthorizationCode(new Claim[]
|
||||
{
|
||||
new Claim(IdentityServiceClaimTypes.TokenUniqueId, Guid.NewGuid().ToString()),
|
||||
new Claim(IdentityServiceClaimTypes.RedirectUri, "https://www.example.com"),
|
||||
new Claim(IdentityServiceClaimTypes.NotBefore,notBefore),
|
||||
new Claim(IdentityServiceClaimTypes.Expires,expires),
|
||||
new Claim(IdentityServiceClaimTypes.IssuedAt,issuedAt),
|
||||
new Claim(IdentityServiceClaimTypes.AuthorizedParty, authorizedParty)
|
||||
});
|
||||
new Claim(IdentityServiceClaimTypes.UserId,"userId"),
|
||||
new Claim(IdentityServiceClaimTypes.ClientId,"clientId"),
|
||||
new Claim(IdentityServiceClaimTypes.RedirectUri, "https://www.example.com"),
|
||||
new Claim(IdentityServiceClaimTypes.Scope, "openid"),
|
||||
new Claim(IdentityServiceClaimTypes.GrantedToken, "id_token")
|
||||
}
|
||||
.Concat(additionalClaims ?? Enumerable.Empty<Claim>()));
|
||||
}
|
||||
|
||||
private ITokenManager GetTestTokenManager(
|
||||
|
|
|
|||
Loading…
Reference in New Issue