Fix #5 - better error message for missing tokens

This fix changes the model for error messaging in antiforgery. Now only
the token store will report a detailed error message including the names
of form field and cookie. Other components will give more generic errors
and assume that this was handled by the token store.

This way you still see an error if the user creates a token store that
doesn't throw, but it's a generic error that doesn't give incorrect
information.
This commit is contained in:
Ryan Nowak 2015-07-30 15:04:50 -07:00 committed by unknown
parent fbe9dc58e8
commit 9bcecf3994
10 changed files with 239 additions and 211 deletions

View File

@ -76,12 +76,8 @@ namespace Microsoft.AspNet.Antiforgery
{
CheckSSLConfig(context);
// Extract cookie & form tokens
var cookieToken = _tokenStore.GetCookieToken(context);
var formToken = await _tokenStore.GetFormTokenAsync(context);
// Validate
_tokenGenerator.ValidateTokens(context, cookieToken, formToken);
var tokens = await _tokenStore.GetRequestTokensAsync(context);
ValidateTokens(context, tokens);
}
/// <inheritdoc />
@ -89,9 +85,23 @@ namespace Microsoft.AspNet.Antiforgery
{
CheckSSLConfig(context);
if (string.IsNullOrEmpty(antiforgeryTokenSet.CookieToken))
{
throw new ArgumentException(
Resources.Antiforgery_CookieToken_MustBeProvided_Generic,
nameof(antiforgeryTokenSet));
}
if (string.IsNullOrEmpty(antiforgeryTokenSet.FormToken))
{
throw new ArgumentException(
Resources.Antiforgery_FormToken_MustBeProvided_Generic,
nameof(antiforgeryTokenSet));
}
// Extract cookie & form tokens
var deserializedCookieToken = DeserializeToken(antiforgeryTokenSet.CookieToken);
var deserializedFormToken = DeserializeToken(antiforgeryTokenSet.FormToken);
var deserializedCookieToken = _tokenSerializer.Deserialize(antiforgeryTokenSet.CookieToken);
var deserializedFormToken = _tokenSerializer.Deserialize(antiforgeryTokenSet.FormToken);
// Validate
_tokenGenerator.ValidateTokens(
@ -154,26 +164,6 @@ namespace Microsoft.AspNet.Antiforgery
}
}
private AntiforgeryToken DeserializeToken(string serializedToken)
{
return (!string.IsNullOrEmpty(serializedToken))
? _tokenSerializer.Deserialize(serializedToken)
: null;
}
private AntiforgeryToken DeserializeTokenDoesNotThrow(string serializedToken)
{
try
{
return DeserializeToken(serializedToken);
}
catch
{
// ignore failures since we'll just generate a new token
return null;
}
}
private AntiforgeryToken GetCookieTokenDoesNotThrow(HttpContext context)
{
try

View File

@ -6,22 +6,18 @@ using System.Diagnostics;
using System.Security.Claims;
using System.Security.Principal;
using Microsoft.AspNet.Http;
using Microsoft.Framework.OptionsModel;
namespace Microsoft.AspNet.Antiforgery
{
public class DefaultAntiforgeryTokenGenerator : IAntiforgeryTokenGenerator
{
private readonly IClaimUidExtractor _claimUidExtractor;
private readonly AntiforgeryOptions _options;
private readonly IAntiforgeryAdditionalDataProvider _additionalDataProvider;
public DefaultAntiforgeryTokenGenerator(
IOptions<AntiforgeryOptions> optionsAccessor,
IClaimUidExtractor claimUidExtractor,
IAntiforgeryAdditionalDataProvider additionalDataProvider)
{
_options = optionsAccessor.Options;
_claimUidExtractor = claimUidExtractor;
_additionalDataProvider = additionalDataProvider;
}
@ -96,23 +92,24 @@ namespace Microsoft.AspNet.Antiforgery
AntiforgeryToken sessionToken,
AntiforgeryToken fieldToken)
{
// Were the tokens even present at all?
if (sessionToken == null)
{
throw new InvalidOperationException(
Resources.FormatAntiforgeryToken_CookieMissing(_options.CookieName));
throw new ArgumentNullException(
nameof(sessionToken),
Resources.Antiforgery_CookieToken_MustBeProvided_Generic);
}
if (fieldToken == null)
{
throw new InvalidOperationException(
Resources.FormatAntiforgeryToken_FormFieldMissing(_options.FormFieldName));
throw new ArgumentNullException(
nameof(fieldToken),
Resources.Antiforgery_FormToken_MustBeProvided_Generic);
}
// Do the tokens have the correct format?
if (!sessionToken.IsSessionToken || fieldToken.IsSessionToken)
{
throw new InvalidOperationException(
Resources.FormatAntiforgeryToken_TokensSwapped(_options.CookieName, _options.FormFieldName));
throw new InvalidOperationException(Resources.AntiforgeryToken_TokensSwapped);
}
// Are the security tokens embedded in each incoming token identical?

View File

@ -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 System.Diagnostics;
using System.Threading.Tasks;
using Microsoft.AspNet.Http;
@ -43,17 +44,24 @@ namespace Microsoft.AspNet.Antiforgery
return _tokenSerializer.Deserialize(requestCookie);
}
public async Task<AntiforgeryToken> GetFormTokenAsync(HttpContext httpContext)
public async Task<AntiforgeryTokenSet> GetRequestTokensAsync([NotNull] HttpContext httpContext)
{
var form = await httpContext.Request.ReadFormAsync();
var value = form[_options.FormFieldName];
if (string.IsNullOrEmpty(value))
var requestCookie = httpContext.Request.Cookies[_options.CookieName];
if (string.IsNullOrEmpty(requestCookie))
{
// did not exist
return null;
throw new InvalidOperationException(
Resources.FormatAntiforgery_CookieToken_MustBeProvided(_options.CookieName));
}
return _tokenSerializer.Deserialize(value);
var form = await httpContext.Request.ReadFormAsync();
var formField = form[_options.FormFieldName];
if (string.IsNullOrEmpty(formField))
{
throw new InvalidOperationException(
Resources.FormatAntiforgery_FormToken_MustBeProvided(_options.FormFieldName));
}
return new AntiforgeryTokenSet(formField, requestCookie);
}
public void SaveCookieToken(HttpContext httpContext, AntiforgeryToken token)

View File

@ -1,8 +1,8 @@
// 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.Security.Claims;
using Microsoft.AspNet.Http;
using Microsoft.Framework.Internal;
namespace Microsoft.AspNet.Antiforgery
{
@ -17,7 +17,7 @@ namespace Microsoft.AspNet.Antiforgery
// Given a cookie token, generates a corresponding form token.
// The incoming cookie token must be valid.
AntiforgeryToken GenerateFormToken(
HttpContext httpContext,
[NotNull] HttpContext httpContext,
AntiforgeryToken cookieToken);
// Determines whether an existing cookie token is valid (well-formed).
@ -26,8 +26,8 @@ namespace Microsoft.AspNet.Antiforgery
// Validates a (cookie, form) token pair.
void ValidateTokens(
HttpContext httpContext,
AntiforgeryToken cookieToken,
AntiforgeryToken formToken);
[NotNull] HttpContext httpContext,
[NotNull] AntiforgeryToken cookieToken,
[NotNull] AntiforgeryToken formToken);
}
}

View File

@ -3,14 +3,23 @@
using System.Threading.Tasks;
using Microsoft.AspNet.Http;
using Microsoft.Framework.Internal;
namespace Microsoft.AspNet.Antiforgery
{
// Provides an abstraction around how tokens are persisted and retrieved for a request
public interface IAntiforgeryTokenStore
{
AntiforgeryToken GetCookieToken(HttpContext httpContext);
Task<AntiforgeryToken> GetFormTokenAsync(HttpContext httpContext);
void SaveCookieToken(HttpContext httpContext, AntiforgeryToken token);
AntiforgeryToken GetCookieToken([NotNull] HttpContext httpContext);
/// <summary>
/// Gets the cookie and form tokens from the request. Will throw an exception if either token is
/// not present.
/// </summary>
/// <param name="httpContext">The <see cref="HttpContext"/> for the current request.</param>
/// <returns>The <see cref="AntiforgeryTokenSet"/>.</returns>
Task<AntiforgeryTokenSet> GetRequestTokensAsync([NotNull] HttpContext httpContext);
void SaveCookieToken([NotNull] HttpContext httpContext, [NotNull] AntiforgeryToken token);
}
}

View File

@ -58,22 +58,6 @@ namespace Microsoft.AspNet.Antiforgery
return GetString("AntiforgeryToken_ClaimUidMismatch");
}
/// <summary>
/// The required antiforgery cookie "{0}" is not present.
/// </summary>
internal static string AntiforgeryToken_CookieMissing
{
get { return GetString("AntiforgeryToken_CookieMissing"); }
}
/// <summary>
/// The required antiforgery cookie "{0}" is not present.
/// </summary>
internal static string FormatAntiforgeryToken_CookieMissing(object p0)
{
return string.Format(CultureInfo.CurrentCulture, GetString("AntiforgeryToken_CookieMissing"), p0);
}
/// <summary>
/// The antiforgery token could not be decrypted.
/// </summary>
@ -90,22 +74,6 @@ namespace Microsoft.AspNet.Antiforgery
return GetString("AntiforgeryToken_DeserializationFailed");
}
/// <summary>
/// The required antiforgery form field "{0}" is not present.
/// </summary>
internal static string AntiforgeryToken_FormFieldMissing
{
get { return GetString("AntiforgeryToken_FormFieldMissing"); }
}
/// <summary>
/// The required antiforgery form field "{0}" is not present.
/// </summary>
internal static string FormatAntiforgeryToken_FormFieldMissing(object p0)
{
return string.Format(CultureInfo.CurrentCulture, GetString("AntiforgeryToken_FormFieldMissing"), p0);
}
/// <summary>
/// The antiforgery cookie token and form field token do not match.
/// </summary>
@ -123,7 +91,7 @@ namespace Microsoft.AspNet.Antiforgery
}
/// <summary>
/// Validation of the provided antiforgery token failed. The cookie "{0}" and the form field "{1}" were swapped.
/// Validation of the provided antiforgery token failed. The cookie token and the form token were swapped.
/// </summary>
internal static string AntiforgeryToken_TokensSwapped
{
@ -131,11 +99,11 @@ namespace Microsoft.AspNet.Antiforgery
}
/// <summary>
/// Validation of the provided antiforgery token failed. The cookie "{0}" and the form field "{1}" were swapped.
/// Validation of the provided antiforgery token failed. The cookie token and the form token were swapped.
/// </summary>
internal static string FormatAntiforgeryToken_TokensSwapped(object p0, object p1)
internal static string FormatAntiforgeryToken_TokensSwapped()
{
return string.Format(CultureInfo.CurrentCulture, GetString("AntiforgeryToken_TokensSwapped"), p0, p1);
return GetString("AntiforgeryToken_TokensSwapped");
}
/// <summary>
@ -170,6 +138,70 @@ namespace Microsoft.AspNet.Antiforgery
return string.Format(CultureInfo.CurrentCulture, GetString("AntiforgeryWorker_RequireSSL"), p0, p1, p2);
}
/// <summary>
/// The required antiforgery cookie "{0}" is not present.
/// </summary>
internal static string Antiforgery_CookieToken_MustBeProvided
{
get { return GetString("Antiforgery_CookieToken_MustBeProvided"); }
}
/// <summary>
/// The required antiforgery cookie "{0}" is not present.
/// </summary>
internal static string FormatAntiforgery_CookieToken_MustBeProvided(object p0)
{
return string.Format(CultureInfo.CurrentCulture, GetString("Antiforgery_CookieToken_MustBeProvided"), p0);
}
/// <summary>
/// The cookie token must be provided.
/// </summary>
internal static string Antiforgery_CookieToken_MustBeProvided_Generic
{
get { return GetString("Antiforgery_CookieToken_MustBeProvided_Generic"); }
}
/// <summary>
/// The cookie token must be provided.
/// </summary>
internal static string FormatAntiforgery_CookieToken_MustBeProvided_Generic()
{
return GetString("Antiforgery_CookieToken_MustBeProvided_Generic");
}
/// <summary>
/// The required antiforgery form field "{0}" is not present.
/// </summary>
internal static string Antiforgery_FormToken_MustBeProvided
{
get { return GetString("Antiforgery_FormToken_MustBeProvided"); }
}
/// <summary>
/// The required antiforgery form field "{0}" is not present.
/// </summary>
internal static string FormatAntiforgery_FormToken_MustBeProvided(object p0)
{
return string.Format(CultureInfo.CurrentCulture, GetString("Antiforgery_FormToken_MustBeProvided"), p0);
}
/// <summary>
/// The form token must be provided.
/// </summary>
internal static string Antiforgery_FormToken_MustBeProvided_Generic
{
get { return GetString("Antiforgery_FormToken_MustBeProvided_Generic"); }
}
/// <summary>
/// The form token must be provided.
/// </summary>
internal static string FormatAntiforgery_FormToken_MustBeProvided_Generic()
{
return GetString("Antiforgery_FormToken_MustBeProvided_Generic");
}
/// <summary>
/// Value cannot be null or empty.
/// </summary>

View File

@ -127,20 +127,14 @@
<data name="AntiforgeryToken_ClaimUidMismatch" xml:space="preserve">
<value>The provided antiforgery token was meant for a different claims-based user than the current user.</value>
</data>
<data name="AntiforgeryToken_CookieMissing" xml:space="preserve">
<value>The required antiforgery cookie "{0}" is not present.</value>
</data>
<data name="AntiforgeryToken_DeserializationFailed" xml:space="preserve">
<value>The antiforgery token could not be decrypted.</value>
</data>
<data name="AntiforgeryToken_FormFieldMissing" xml:space="preserve">
<value>The required antiforgery form field "{0}" is not present.</value>
</data>
<data name="AntiforgeryToken_SecurityTokenMismatch" xml:space="preserve">
<value>The antiforgery cookie token and form field token do not match.</value>
</data>
<data name="AntiforgeryToken_TokensSwapped" xml:space="preserve">
<value>Validation of the provided antiforgery token failed. The cookie "{0}" and the form field "{1}" were swapped.</value>
<value>Validation of the provided antiforgery token failed. The cookie token and the form token were swapped.</value>
</data>
<data name="AntiforgeryToken_UsernameMismatch" xml:space="preserve">
<value>The provided antiforgery token was meant for user "{0}", but the current user is "{1}".</value>
@ -149,6 +143,18 @@
<value>The antiforgery system has the configuration value {0}.{1} = {2}, but the current request is not an SSL request.</value>
<comment>0 = nameof(AntiforgeryOptions), 1 = nameof(RequireSsl), 2 = bool.TrueString</comment>
</data>
<data name="Antiforgery_CookieToken_MustBeProvided" xml:space="preserve">
<value>The required antiforgery cookie "{0}" is not present.</value>
</data>
<data name="Antiforgery_CookieToken_MustBeProvided_Generic" xml:space="preserve">
<value>The cookie token must be provided.</value>
</data>
<data name="Antiforgery_FormToken_MustBeProvided" xml:space="preserve">
<value>The required antiforgery form field "{0}" is not present.</value>
</data>
<data name="Antiforgery_FormToken_MustBeProvided_Generic" xml:space="preserve">
<value>The form token must be provided.</value>
</data>
<data name="ArgumentCannotBeNullOrEmpty" xml:space="preserve">
<value>Value cannot be null or empty.</value>
</data>

View File

@ -318,7 +318,7 @@ namespace Microsoft.AspNet.Antiforgery
}
[Fact]
public void Validate_FromInvalidStrings_Throws()
public void ValidateTokens_FromInvalidStrings_Throws()
{
// Arrange
var context = CreateMockContext(new AntiforgeryOptions());
@ -344,7 +344,7 @@ namespace Microsoft.AspNet.Antiforgery
}
[Fact]
public void Validate_FromValidStrings_TokensValidatedSuccessfully()
public void ValidateTokens_FromValidStrings_TokensValidatedSuccessfully()
{
// Arrange
var context = CreateMockContext(new AntiforgeryOptions());
@ -369,16 +369,37 @@ namespace Microsoft.AspNet.Antiforgery
}
[Fact]
public async Task Validate_FromStore_Failure()
public void ValidateTokens_MissingCookieInTokenSet_Throws()
{
// Arrange
var context = CreateMockContext(new AntiforgeryOptions());
var antiforgery = GetAntiforgery(context);
var tokenSet = new AntiforgeryTokenSet("hi", cookieToken: null);
// Act
var exception = Assert.Throws<ArgumentException>(
() => antiforgery.ValidateTokens(context.HttpContext, tokenSet));
// Assert
var trimmed = exception.Message.Substring(0, exception.Message.IndexOf(Environment.NewLine));
Assert.Equal("The cookie token must be provided.", trimmed);
}
[Fact]
public async Task ValidateRequestAsync_FromStore_Failure()
{
// Arrange
var context = CreateMockContext(new AntiforgeryOptions());
context.TokenGenerator.Setup(o => o.ValidateTokens(
context.HttpContext,
context.TestTokenSet.OldCookieToken, context.TestTokenSet.FormToken))
.Throws(new InvalidOperationException("my-message"));
context.TokenSerializer = null;
context.TokenGenerator
.Setup(o => o.ValidateTokens(
context.HttpContext,
context.TestTokenSet.OldCookieToken,
context.TestTokenSet.FormToken))
.Throws(new InvalidOperationException("my-message"));
var antiforgery = GetAntiforgery(context);
// Act & assert
@ -388,16 +409,18 @@ namespace Microsoft.AspNet.Antiforgery
}
[Fact]
public async Task Validate_FromStore_Success()
public async Task ValidateRequestAsync_FromStore_Success()
{
// Arrange
var context = CreateMockContext(new AntiforgeryOptions());
context.TokenGenerator.Setup(o => o.ValidateTokens(
context.HttpContext,
context.TestTokenSet.OldCookieToken, context.TestTokenSet.FormToken))
.Verifiable();
context.TokenSerializer = null;
context.TokenGenerator
.Setup(o => o.ValidateTokens(
context.HttpContext,
context.TestTokenSet.OldCookieToken,
context.TestTokenSet.FormToken))
.Verifiable();
var antiforgery = GetAntiforgery(context);
// Act
@ -482,8 +505,11 @@ namespace Microsoft.AspNet.Antiforgery
var mockTokenStore = new Mock<IAntiforgeryTokenStore>(MockBehavior.Strict);
mockTokenStore.Setup(o => o.GetCookieToken(context))
.Returns(oldCookieToken);
mockTokenStore.Setup(o => o.GetFormTokenAsync(context))
.Returns(Task.FromResult(formToken));
mockTokenStore.Setup(o => o.GetRequestTokensAsync(context))
.Returns(() => Task.FromResult(new AntiforgeryTokenSet(
testTokenSet.FormTokenString,
testTokenSet.OldCookieTokenString)));
if (saveNewCookie)
{
@ -502,11 +528,13 @@ namespace Microsoft.AspNet.Antiforgery
var formToken = testTokenSet.FormToken;
var mockSerializer = new Mock<IAntiforgeryTokenSerializer>(MockBehavior.Strict);
mockSerializer.Setup(o => o.Serialize(formToken))
.Returns("serialized-form-token");
mockSerializer.Setup(o => o.Deserialize("serialized-old-cookie-token"))
.Returns(testTokenSet.FormTokenString);
mockSerializer.Setup(o => o.Deserialize(testTokenSet.FormTokenString))
.Returns(formToken);
mockSerializer.Setup(o => o.Deserialize(testTokenSet.OldCookieTokenString))
.Returns(oldCookieToken);
mockSerializer.Setup(o => o.Serialize(newCookieToken))
.Returns("serialized-new-cookie-token");
.Returns(testTokenSet.NewCookieTokenString);
return mockSerializer;
}
@ -558,8 +586,11 @@ namespace Microsoft.AspNet.Antiforgery
return new TestTokenSet()
{
FormToken = new AntiforgeryToken() { IsSessionToken = false },
FormTokenString = "serialized-form-token",
OldCookieToken = new AntiforgeryToken() { IsSessionToken = isOldCookieTokenSessionToken },
OldCookieTokenString = "serialized-old-cookie-token",
NewCookieToken = new AntiforgeryToken() { IsSessionToken = isNewCookieSessionToken },
NewCookieTokenString = "serialized-new-cookie-token",
};
}

View File

@ -19,7 +19,6 @@ namespace Microsoft.AspNet.Antiforgery
{
// Arrange
var tokenProvider = new DefaultAntiforgeryTokenGenerator(
optionsAccessor: new TestOptionsManager(),
claimUidExtractor: null,
additionalDataProvider: null);
@ -40,7 +39,6 @@ namespace Microsoft.AspNet.Antiforgery
Assert.False(httpContext.User.Identity.IsAuthenticated);
var tokenProvider = new DefaultAntiforgeryTokenGenerator(
optionsAccessor: new TestOptionsManager(),
claimUidExtractor: null,
additionalDataProvider: null);
@ -74,7 +72,6 @@ namespace Microsoft.AspNet.Antiforgery
var claimUidExtractor = new Mock<IClaimUidExtractor>().Object;
var tokenProvider = new DefaultAntiforgeryTokenGenerator(
optionsAccessor: new TestOptionsManager(),
claimUidExtractor: claimUidExtractor,
additionalDataProvider: null);
@ -109,7 +106,6 @@ namespace Microsoft.AspNet.Antiforgery
var claimUidExtractor = new Mock<IClaimUidExtractor>().Object;
var tokenProvider = new DefaultAntiforgeryTokenGenerator(
optionsAccessor: new TestOptionsManager(),
claimUidExtractor: claimUidExtractor,
additionalDataProvider: mockAdditionalDataProvider.Object);
@ -148,7 +144,6 @@ namespace Microsoft.AspNet.Antiforgery
.Returns(base64ClaimUId);
var tokenProvider = new DefaultAntiforgeryTokenGenerator(
optionsAccessor: new TestOptionsManager(),
claimUidExtractor: mockClaimUidExtractor.Object,
additionalDataProvider: null);
@ -182,7 +177,6 @@ namespace Microsoft.AspNet.Antiforgery
var claimUidExtractor = new Mock<IClaimUidExtractor>().Object;
var tokenProvider = new DefaultAntiforgeryTokenGenerator(
optionsAccessor: new TestOptionsManager(),
claimUidExtractor: claimUidExtractor,
additionalDataProvider: null);
@ -209,7 +203,6 @@ namespace Microsoft.AspNet.Antiforgery
};
var tokenProvider = new DefaultAntiforgeryTokenGenerator(
optionsAccessor: new TestOptionsManager(),
claimUidExtractor: null,
additionalDataProvider: null);
@ -226,7 +219,6 @@ namespace Microsoft.AspNet.Antiforgery
// Arrange
AntiforgeryToken cookieToken = null;
var tokenProvider = new DefaultAntiforgeryTokenGenerator(
optionsAccessor: new TestOptionsManager(),
claimUidExtractor: null,
additionalDataProvider: null);
@ -247,7 +239,6 @@ namespace Microsoft.AspNet.Antiforgery
};
var tokenProvider = new DefaultAntiforgeryTokenGenerator(
optionsAccessor: new TestOptionsManager(),
claimUidExtractor: null,
additionalDataProvider: null);
@ -268,21 +259,16 @@ namespace Microsoft.AspNet.Antiforgery
var fieldtoken = new AntiforgeryToken() { IsSessionToken = false };
var options = new AntiforgeryOptions()
{
CookieName = "my-cookie-name"
};
var tokenProvider = new DefaultAntiforgeryTokenGenerator(
optionsAccessor: new TestOptionsManager(options),
claimUidExtractor: null,
additionalDataProvider: null);
// Act & assert
var ex =
Assert.Throws<InvalidOperationException>(
() => tokenProvider.ValidateTokens(httpContext, null, fieldtoken));
Assert.Equal(@"The required antiforgery cookie ""my-cookie-name"" is not present.", ex.Message);
var ex = Assert.Throws<ArgumentNullException>(
() => tokenProvider.ValidateTokens(httpContext, null, fieldtoken));
var trimmed = ex.Message.Substring(0, ex.Message.IndexOf(Environment.NewLine));
Assert.Equal(@"The cookie token must be provided.", trimmed);
}
[Fact]
@ -294,21 +280,16 @@ namespace Microsoft.AspNet.Antiforgery
var sessionToken = new AntiforgeryToken() { IsSessionToken = true };
var options = new AntiforgeryOptions()
{
FormFieldName = "my-form-field-name"
};
var tokenProvider = new DefaultAntiforgeryTokenGenerator(
optionsAccessor: new TestOptionsManager(options),
claimUidExtractor: null,
additionalDataProvider: null);
// Act & assert
var ex =
Assert.Throws<InvalidOperationException>(
() => tokenProvider.ValidateTokens(httpContext, sessionToken, null));
Assert.Equal(@"The required antiforgery form field ""my-form-field-name"" is not present.", ex.Message);
var ex = Assert.Throws<ArgumentNullException>(
() => tokenProvider.ValidateTokens(httpContext, sessionToken, null));
var trimmed = ex.Message.Substring(0, ex.Message.IndexOf(Environment.NewLine));
Assert.Equal("The form token must be provided.", trimmed);
}
[Fact]
@ -321,14 +302,7 @@ namespace Microsoft.AspNet.Antiforgery
var sessionToken = new AntiforgeryToken() { IsSessionToken = true };
var fieldtoken = new AntiforgeryToken() { IsSessionToken = false };
var options = new AntiforgeryOptions()
{
CookieName = "my-cookie-name",
FormFieldName = "my-form-field-name"
};
var tokenProvider = new DefaultAntiforgeryTokenGenerator(
optionsAccessor: new TestOptionsManager(options),
claimUidExtractor: null,
additionalDataProvider: null);
@ -338,7 +312,7 @@ namespace Microsoft.AspNet.Antiforgery
() => tokenProvider.ValidateTokens(httpContext, fieldtoken, fieldtoken));
Assert.Equal(
"Validation of the provided antiforgery token failed. " +
@"The cookie ""my-cookie-name"" and the form field ""my-form-field-name"" were swapped.",
@"The cookie token and the form token were swapped.",
ex1.Message);
var ex2 =
@ -346,7 +320,7 @@ namespace Microsoft.AspNet.Antiforgery
() => tokenProvider.ValidateTokens(httpContext, sessionToken, sessionToken));
Assert.Equal(
"Validation of the provided antiforgery token failed. " +
@"The cookie ""my-cookie-name"" and the form field ""my-form-field-name"" were swapped.",
@"The cookie token and the form token were swapped.",
ex2.Message);
}
@ -361,7 +335,6 @@ namespace Microsoft.AspNet.Antiforgery
var fieldtoken = new AntiforgeryToken() { IsSessionToken = false };
var tokenProvider = new DefaultAntiforgeryTokenGenerator(
optionsAccessor: new TestOptionsManager(),
claimUidExtractor: null,
additionalDataProvider: null);
@ -399,7 +372,6 @@ namespace Microsoft.AspNet.Antiforgery
.Returns((string)null);
var tokenProvider = new DefaultAntiforgeryTokenGenerator(
optionsAccessor: new TestOptionsManager(),
claimUidExtractor: mockClaimUidExtractor.Object,
additionalDataProvider: null);
@ -434,7 +406,6 @@ namespace Microsoft.AspNet.Antiforgery
.Returns(Convert.ToBase64String(differentToken.GetData()));
var tokenProvider = new DefaultAntiforgeryTokenGenerator(
optionsAccessor: new TestOptionsManager(),
claimUidExtractor: mockClaimUidExtractor.Object,
additionalDataProvider: null);
@ -468,7 +439,6 @@ namespace Microsoft.AspNet.Antiforgery
.Returns(false);
var tokenProvider = new DefaultAntiforgeryTokenGenerator(
optionsAccessor: new TestOptionsManager(),
claimUidExtractor: null,
additionalDataProvider: mockAdditionalDataProvider.Object);
@ -500,7 +470,6 @@ namespace Microsoft.AspNet.Antiforgery
.Returns(true);
var tokenProvider = new DefaultAntiforgeryTokenGenerator(
optionsAccessor: new TestOptionsManager(),
claimUidExtractor: null,
additionalDataProvider: mockAdditionalDataProvider.Object);
@ -533,7 +502,6 @@ namespace Microsoft.AspNet.Antiforgery
.Returns(true);
var tokenProvider = new DefaultAntiforgeryTokenGenerator(
optionsAccessor: new TestOptionsManager(),
claimUidExtractor: new Mock<IClaimUidExtractor>().Object,
additionalDataProvider: mockAdditionalDataProvider.Object);
@ -565,7 +533,6 @@ namespace Microsoft.AspNet.Antiforgery
.Returns(Convert.ToBase64String(fieldtoken.ClaimUid.GetData()));
var tokenProvider = new DefaultAntiforgeryTokenGenerator(
optionsAccessor: new TestOptionsManager(),
claimUidExtractor: mockClaimUidExtractor.Object,
additionalDataProvider: null);

View File

@ -7,6 +7,7 @@ using System.Collections.Generic;
using System.Threading;
using System.Threading.Tasks;
using Microsoft.AspNet.Http;
using Microsoft.AspNet.Http.Internal;
using Microsoft.Framework.DependencyInjection;
using Moq;
using Xunit;
@ -160,20 +161,16 @@ namespace Microsoft.AspNet.Antiforgery
}
[Fact]
public async Task GetFormToken_FormFieldIsEmpty_ReturnsNull()
public async Task GetRequestTokens_CookieIsEmpty_Throws()
{
// Arrange
var mockHttpContext = new Mock<HttpContext>();
var requestContext = new Mock<HttpRequest>();
var formCollection = new Mock<IFormCollection>();
formCollection.Setup(f => f["form-field-name"]).Returns(string.Empty);
requestContext.Setup(o => o.ReadFormAsync(CancellationToken.None))
.Returns(Task.FromResult(formCollection.Object));
mockHttpContext.Setup(o => o.Request)
.Returns(requestContext.Object);
var httpContext = new DefaultHttpContext();
httpContext.Request.Form = new FormCollection(new Dictionary<string, string[]>());
httpContext.Request.Cookies = new ReadableStringCollection(new Dictionary<string, string[]>());
var options = new AntiforgeryOptions()
{
CookieName = "cookie-name",
FormFieldName = "form-field-name",
};
@ -182,81 +179,72 @@ namespace Microsoft.AspNet.Antiforgery
tokenSerializer: null);
// Act
var token = await tokenStore.GetFormTokenAsync(mockHttpContext.Object);
var exception = await Assert.ThrowsAsync<InvalidOperationException>(
async () => await tokenStore.GetRequestTokensAsync(httpContext));
// Assert
Assert.Null(token);
// Assert
Assert.Equal("The required antiforgery cookie \"cookie-name\" is not present.", exception.Message);
}
[Fact]
public async Task GetFormToken_FormFieldIsInvalid_PropagatesException()
public async Task GetRequestTokens_FormFieldIsEmpty_Throws()
{
// Arrange
var formCollection = new Mock<IFormCollection>();
formCollection.Setup(f => f["form-field-name"]).Returns("invalid-value");
var requestContext = new Mock<HttpRequest>();
requestContext.Setup(o => o.ReadFormAsync(CancellationToken.None))
.Returns(Task.FromResult(formCollection.Object));
var mockHttpContext = new Mock<HttpContext>();
mockHttpContext.Setup(o => o.Request)
.Returns(requestContext.Object);
var expectedException = new InvalidOperationException("some exception");
var mockSerializer = new Mock<IAntiforgeryTokenSerializer>();
mockSerializer.Setup(o => o.Deserialize("invalid-value"))
.Throws(expectedException);
var httpContext = new DefaultHttpContext();
httpContext.Request.Form = new FormCollection(new Dictionary<string, string[]>());
httpContext.Request.Cookies = new ReadableStringCollection(new Dictionary<string, string[]>()
{
{ "cookie-name", new string[] { "cookie-value" } },
});
var options = new AntiforgeryOptions()
{
CookieName = "cookie-name",
FormFieldName = "form-field-name",
};
var tokenStore = new DefaultAntiforgeryTokenStore(
optionsAccessor: new TestOptionsManager(options),
tokenSerializer: mockSerializer.Object);
tokenSerializer: null);
// Act & assert
// Act
var exception = await Assert.ThrowsAsync<InvalidOperationException>(
async () => await tokenStore.GetFormTokenAsync(mockHttpContext.Object));
Assert.Same(expectedException, exception);
async () => await tokenStore.GetRequestTokensAsync(httpContext));
// Assert
Assert.Equal("The required antiforgery form field \"form-field-name\" is not present.", exception.Message);
}
[Fact]
public async Task GetFormToken_FormFieldIsValid_ReturnsToken()
{
// Arrange
var expectedToken = new AntiforgeryToken();
// Arrange
var mockHttpContext = new Mock<HttpContext>();
var requestContext = new Mock<HttpRequest>();
var formCollection = new Mock<IFormCollection>();
formCollection.Setup(f => f["form-field-name"]).Returns("valid-value");
requestContext.Setup(o => o.ReadFormAsync(CancellationToken.None))
.Returns(Task.FromResult(formCollection.Object));
mockHttpContext.Setup(o => o.Request)
.Returns(requestContext.Object);
var mockSerializer = new Mock<IAntiforgeryTokenSerializer>();
mockSerializer.Setup(o => o.Deserialize("valid-value"))
.Returns(expectedToken);
var httpContext = new DefaultHttpContext();
httpContext.Request.Form = new FormCollection(new Dictionary<string, string[]>()
{
{ "form-field-name", new string[] { "form-value" } },
});
httpContext.Request.Cookies = new ReadableStringCollection(new Dictionary<string, string[]>()
{
{ "cookie-name", new string[] { "cookie-value" } },
});
var options = new AntiforgeryOptions()
{
CookieName = "cookie-name",
FormFieldName = "form-field-name",
};
var tokenStore = new DefaultAntiforgeryTokenStore(
optionsAccessor: new TestOptionsManager(options),
tokenSerializer: mockSerializer.Object);
tokenSerializer: null);
// Act
var retVal = await tokenStore.GetFormTokenAsync(mockHttpContext.Object);
var tokens = await tokenStore.GetRequestTokensAsync(httpContext);
// Assert
Assert.Same(expectedToken, retVal);
Assert.Equal("cookie-value", tokens.CookieToken);
Assert.Equal("form-value", tokens.FormToken);
}
[Theory]