diff --git a/samples/AntiforgerySample/FormPostSampleMiddleware.cs b/samples/AntiforgerySample/FormPostSampleMiddleware.cs index aa9b41a872..ef0a8a5421 100644 --- a/samples/AntiforgerySample/FormPostSampleMiddleware.cs +++ b/samples/AntiforgerySample/FormPostSampleMiddleware.cs @@ -40,7 +40,7 @@ namespace AntiforgerySample "; var tokenSet = _antiforgery.GetAndStoreTokens(context); - await context.Response.WriteAsync(string.Format(page, _options.FormFieldName, tokenSet.FormToken)); + await context.Response.WriteAsync(string.Format(page, _options.FormFieldName, tokenSet.RequestToken)); } else if (context.Request.Method == "POST") { diff --git a/src/Microsoft.AspNet.Antiforgery/AntiforgeryOptions.cs b/src/Microsoft.AspNet.Antiforgery/AntiforgeryOptions.cs index db66421a15..2308e36699 100644 --- a/src/Microsoft.AspNet.Antiforgery/AntiforgeryOptions.cs +++ b/src/Microsoft.AspNet.Antiforgery/AntiforgeryOptions.cs @@ -11,16 +11,17 @@ namespace Microsoft.AspNet.Antiforgery public class AntiforgeryOptions { private const string AntiforgeryTokenFieldName = "__RequestVerificationToken"; + private const string AntiforgertyTokenHeaderName = "RequestVerificationToken"; + private string _cookieName; + private string _headerName = AntiforgertyTokenHeaderName; private string _formFieldName = AntiforgeryTokenFieldName; /// - /// Specifies the name of the cookie that is used by the antiforgery - /// system. + /// Specifies the name of the cookie that is used by the antiforgery system. /// /// - /// If an explicit name is not provided, the system will automatically - /// generate a name. + /// If an explicit name is not provided, the system will automatically generate a name. /// public string CookieName { @@ -59,6 +60,16 @@ namespace Microsoft.AspNet.Antiforgery } } + /// + /// Specifies the name of the header value that is used by the antiforgery system. If null then + /// antiforgery validation will only consider form data. + /// + public string HeaderName + { + get { return _headerName; } + set { _headerName = value; } + } + /// /// Specifies whether SSL is required for the antiforgery system /// to operate. If this setting is 'true' and a non-SSL request diff --git a/src/Microsoft.AspNet.Antiforgery/AntiforgeryToken.cs b/src/Microsoft.AspNet.Antiforgery/AntiforgeryToken.cs index 61f9665c26..233c5c51cd 100644 --- a/src/Microsoft.AspNet.Antiforgery/AntiforgeryToken.cs +++ b/src/Microsoft.AspNet.Antiforgery/AntiforgeryToken.cs @@ -23,7 +23,7 @@ namespace Microsoft.AspNet.Antiforgery public BinaryBlob ClaimUid { get; set; } - public bool IsSessionToken { get; set; } + public bool IsCookieToken { get; set; } public BinaryBlob SecurityToken { diff --git a/src/Microsoft.AspNet.Antiforgery/AntiforgeryTokenSet.cs b/src/Microsoft.AspNet.Antiforgery/AntiforgeryTokenSet.cs index 973e6883e9..8fabd782ee 100644 --- a/src/Microsoft.AspNet.Antiforgery/AntiforgeryTokenSet.cs +++ b/src/Microsoft.AspNet.Antiforgery/AntiforgeryTokenSet.cs @@ -6,23 +6,23 @@ using System; namespace Microsoft.AspNet.Antiforgery { /// - /// The antiforgery token pair (cookie and form token) for a request. + /// The antiforgery token pair (cookie and request token) for a request. /// public class AntiforgeryTokenSet { /// - /// Creates the antiforgery token pair (cookie and form token) for a request. + /// Creates the antiforgery token pair (cookie and request token) for a request. /// - /// The token that is supplied in the request form body. + /// The token that is supplied in the request. /// The token that is supplied in the request cookie. - public AntiforgeryTokenSet(string formToken, string cookieToken) + public AntiforgeryTokenSet(string requestToken, string cookieToken) { - if (string.IsNullOrEmpty(formToken)) + if (string.IsNullOrEmpty(requestToken)) { - throw new ArgumentException(Resources.ArgumentCannotBeNullOrEmpty, nameof(formToken)); + throw new ArgumentException(Resources.ArgumentCannotBeNullOrEmpty, nameof(requestToken)); } - FormToken = formToken; + RequestToken = requestToken; // Cookie Token is allowed to be null in the case when the old cookie is valid // and there is no new cookieToken generated. @@ -30,9 +30,9 @@ namespace Microsoft.AspNet.Antiforgery } /// - /// The token that is supplied in the request form body. + /// The token that is supplied in the request. /// - public string FormToken { get; private set; } + public string RequestToken { get; private set; } /// The cookie token is allowed to be null. /// This would be the case when the old cookie token is still valid. diff --git a/src/Microsoft.AspNet.Antiforgery/DefaultAntiforgery.cs b/src/Microsoft.AspNet.Antiforgery/DefaultAntiforgery.cs index cd67239b0d..7aa43f581d 100644 --- a/src/Microsoft.AspNet.Antiforgery/DefaultAntiforgery.cs +++ b/src/Microsoft.AspNet.Antiforgery/DefaultAntiforgery.cs @@ -45,14 +45,14 @@ namespace Microsoft.AspNet.Antiforgery var tokenSet = GetAndStoreTokens(context); - // Though FormToken normally contains only US-ASCII letters, numbers, '-', and '_', must assume the + // Though RequestToken normally contains only US-ASCII letters, numbers, '-', and '_', must assume the // IAntiforgeryTokenSerializer implementation has been overridden. Similarly, users may choose a // FormFieldName containing almost any character. var content = new HtmlContentBuilder() .AppendHtml(""); return content; @@ -122,22 +122,22 @@ namespace Microsoft.AspNet.Antiforgery nameof(antiforgeryTokenSet)); } - if (string.IsNullOrEmpty(antiforgeryTokenSet.FormToken)) + if (string.IsNullOrEmpty(antiforgeryTokenSet.RequestToken)) { throw new ArgumentException( - Resources.Antiforgery_FormToken_MustBeProvided_Generic, + Resources.Antiforgery_RequestToken_MustBeProvided_Generic, nameof(antiforgeryTokenSet)); } - // Extract cookie & form tokens + // Extract cookie & request tokens var deserializedCookieToken = _tokenSerializer.Deserialize(antiforgeryTokenSet.CookieToken); - var deserializedFormToken = _tokenSerializer.Deserialize(antiforgeryTokenSet.FormToken); + var deserializedRequestToken = _tokenSerializer.Deserialize(antiforgeryTokenSet.RequestToken); // Validate _tokenGenerator.ValidateTokens( context, deserializedCookieToken, - deserializedFormToken); + deserializedRequestToken); } /// @@ -225,7 +225,7 @@ namespace Microsoft.AspNet.Antiforgery { cookieToken = newCookieToken; } - var formToken = _tokenGenerator.GenerateFormToken( + var requestToken = _tokenGenerator.GenerateRequestToken( context, cookieToken); @@ -233,7 +233,7 @@ namespace Microsoft.AspNet.Antiforgery { // Note : The new cookie would be null if the old cookie is valid. CookieToken = cookieToken, - FormToken = formToken, + RequestToken = requestToken, IsNewCookieToken = newCookieToken != null }; } @@ -241,13 +241,13 @@ namespace Microsoft.AspNet.Antiforgery private AntiforgeryTokenSet Serialize(AntiforgeryTokenSetInternal tokenSet) { return new AntiforgeryTokenSet( - tokenSet.FormToken != null ? _tokenSerializer.Serialize(tokenSet.FormToken) : null, + tokenSet.RequestToken != null ? _tokenSerializer.Serialize(tokenSet.RequestToken) : null, tokenSet.CookieToken != null ? _tokenSerializer.Serialize(tokenSet.CookieToken) : null); } private class AntiforgeryTokenSetInternal { - public AntiforgeryToken FormToken { get; set; } + public AntiforgeryToken RequestToken { get; set; } public AntiforgeryToken CookieToken { get; set; } diff --git a/src/Microsoft.AspNet.Antiforgery/DefaultAntiforgeryTokenGenerator.cs b/src/Microsoft.AspNet.Antiforgery/DefaultAntiforgeryTokenGenerator.cs index c23661ccc4..32a2581f71 100644 --- a/src/Microsoft.AspNet.Antiforgery/DefaultAntiforgeryTokenGenerator.cs +++ b/src/Microsoft.AspNet.Antiforgery/DefaultAntiforgeryTokenGenerator.cs @@ -27,11 +27,11 @@ namespace Microsoft.AspNet.Antiforgery return new AntiforgeryToken() { // SecurityToken will be populated automatically. - IsSessionToken = true + IsCookieToken = true }; } - public AntiforgeryToken GenerateFormToken( + public AntiforgeryToken GenerateRequestToken( HttpContext httpContext, AntiforgeryToken cookieToken) { @@ -42,10 +42,10 @@ namespace Microsoft.AspNet.Antiforgery Debug.Assert(IsCookieTokenValid(cookieToken)); - var formToken = new AntiforgeryToken() + var requestToken = new AntiforgeryToken() { SecurityToken = cookieToken.SecurityToken, - IsSessionToken = false + IsCookieToken = false }; var isIdentityAuthenticated = false; @@ -55,23 +55,23 @@ namespace Microsoft.AspNet.Antiforgery if (identity != null && identity.IsAuthenticated) { isIdentityAuthenticated = true; - formToken.ClaimUid = GetClaimUidBlob(_claimUidExtractor.ExtractClaimUid(identity)); - if (formToken.ClaimUid == null) + requestToken.ClaimUid = GetClaimUidBlob(_claimUidExtractor.ExtractClaimUid(identity)); + if (requestToken.ClaimUid == null) { - formToken.Username = identity.Name; + requestToken.Username = identity.Name; } } // populate AdditionalData if (_additionalDataProvider != null) { - formToken.AdditionalData = _additionalDataProvider.GetAdditionalData(httpContext); + requestToken.AdditionalData = _additionalDataProvider.GetAdditionalData(httpContext); } if (isIdentityAuthenticated - && string.IsNullOrEmpty(formToken.Username) - && formToken.ClaimUid == null - && string.IsNullOrEmpty(formToken.AdditionalData)) + && string.IsNullOrEmpty(requestToken.Username) + && requestToken.ClaimUid == null + && string.IsNullOrEmpty(requestToken.AdditionalData)) { // Application says user is authenticated, but we have no identifier for the user. throw new InvalidOperationException( @@ -84,46 +84,46 @@ namespace Microsoft.AspNet.Antiforgery nameof(DefaultAntiforgeryAdditionalDataProvider))); } - return formToken; + return requestToken; } public bool IsCookieTokenValid(AntiforgeryToken cookieToken) { - return (cookieToken != null && cookieToken.IsSessionToken); + return (cookieToken != null && cookieToken.IsCookieToken); } public void ValidateTokens( HttpContext httpContext, - AntiforgeryToken sessionToken, - AntiforgeryToken fieldToken) + AntiforgeryToken cookieToken, + AntiforgeryToken requestToken) { if (httpContext == null) { throw new ArgumentNullException(nameof(httpContext)); } - if (sessionToken == null) + if (cookieToken == null) { throw new ArgumentNullException( - nameof(sessionToken), + nameof(cookieToken), Resources.Antiforgery_CookieToken_MustBeProvided_Generic); } - if (fieldToken == null) + if (requestToken == null) { throw new ArgumentNullException( - nameof(fieldToken), - Resources.Antiforgery_FormToken_MustBeProvided_Generic); + nameof(requestToken), + Resources.Antiforgery_RequestToken_MustBeProvided_Generic); } // Do the tokens have the correct format? - if (!sessionToken.IsSessionToken || fieldToken.IsSessionToken) + if (!cookieToken.IsCookieToken || requestToken.IsCookieToken) { throw new InvalidOperationException(Resources.AntiforgeryToken_TokensSwapped); } // Are the security tokens embedded in each incoming token identical? - if (!Equals(sessionToken.SecurityToken, fieldToken.SecurityToken)) + if (!Equals(cookieToken.SecurityToken, requestToken.SecurityToken)) { throw new InvalidOperationException(Resources.AntiforgeryToken_SecurityTokenMismatch); } @@ -148,24 +148,24 @@ namespace Microsoft.AspNet.Antiforgery currentUsername.StartsWith("http://", StringComparison.OrdinalIgnoreCase) || currentUsername.StartsWith("https://", StringComparison.OrdinalIgnoreCase); - if (!string.Equals(fieldToken.Username, + if (!string.Equals(requestToken.Username, currentUsername, (useCaseSensitiveUsernameComparison) ? StringComparison.Ordinal : StringComparison.OrdinalIgnoreCase)) { throw new InvalidOperationException( - Resources.FormatAntiforgeryToken_UsernameMismatch(fieldToken.Username, currentUsername)); + Resources.FormatAntiforgeryToken_UsernameMismatch(requestToken.Username, currentUsername)); } - if (!Equals(fieldToken.ClaimUid, currentClaimUid)) + if (!Equals(requestToken.ClaimUid, currentClaimUid)) { throw new InvalidOperationException(Resources.AntiforgeryToken_ClaimUidMismatch); } // Is the AdditionalData valid? if (_additionalDataProvider != null && - !_additionalDataProvider.ValidateAdditionalData(httpContext, fieldToken.AdditionalData)) + !_additionalDataProvider.ValidateAdditionalData(httpContext, requestToken.AdditionalData)) { throw new InvalidOperationException(Resources.AntiforgeryToken_AdditionalDataCheckFailed); } diff --git a/src/Microsoft.AspNet.Antiforgery/DefaultAntiforgeryTokenSerializer.cs b/src/Microsoft.AspNet.Antiforgery/DefaultAntiforgeryTokenSerializer.cs index c5eb46a413..e1ecd406e8 100644 --- a/src/Microsoft.AspNet.Antiforgery/DefaultAntiforgeryTokenSerializer.cs +++ b/src/Microsoft.AspNet.Antiforgery/DefaultAntiforgeryTokenSerializer.cs @@ -56,8 +56,8 @@ namespace Microsoft.AspNet.Antiforgery /* The serialized format of the anti-XSRF token is as follows: * Version: 1 byte integer * SecurityToken: 16 byte binary blob - * IsSessionToken: 1 byte Boolean - * [if IsSessionToken != true] + * IsCookieToken: 1 byte Boolean + * [if IsCookieToken != true] * +- IsClaimsBased: 1 byte Boolean * | [if IsClaimsBased = true] * | `- ClaimUid: 32 byte binary blob @@ -78,9 +78,9 @@ namespace Microsoft.AspNet.Antiforgery var securityTokenBytes = reader.ReadBytes(AntiforgeryToken.SecurityTokenBitLength / 8); deserializedToken.SecurityToken = new BinaryBlob(AntiforgeryToken.SecurityTokenBitLength, securityTokenBytes); - deserializedToken.IsSessionToken = reader.ReadBoolean(); + deserializedToken.IsCookieToken = reader.ReadBoolean(); - if (!deserializedToken.IsSessionToken) + if (!deserializedToken.IsCookieToken) { var isClaimsBased = reader.ReadBoolean(); if (isClaimsBased) @@ -119,9 +119,9 @@ namespace Microsoft.AspNet.Antiforgery { writer.Write(TokenVersion); writer.Write(token.SecurityToken.GetData()); - writer.Write(token.IsSessionToken); + writer.Write(token.IsCookieToken); - if (!token.IsSessionToken) + if (!token.IsCookieToken) { if (token.ClaimUid != null) { diff --git a/src/Microsoft.AspNet.Antiforgery/DefaultAntiforgeryTokenStore.cs b/src/Microsoft.AspNet.Antiforgery/DefaultAntiforgeryTokenStore.cs index 29a50483ce..8adbd024ea 100644 --- a/src/Microsoft.AspNet.Antiforgery/DefaultAntiforgeryTokenStore.cs +++ b/src/Microsoft.AspNet.Antiforgery/DefaultAntiforgeryTokenStore.cs @@ -7,10 +7,10 @@ using System.Threading.Tasks; using Microsoft.AspNet.Http; using Microsoft.Extensions.DependencyInjection; using Microsoft.Extensions.OptionsModel; +using Microsoft.Extensions.Primitives; namespace Microsoft.AspNet.Antiforgery { - // Saves anti-XSRF tokens split between HttpRequest.Cookies and HttpRequest.Form public class DefaultAntiforgeryTokenStore : IAntiforgeryTokenStore { private readonly AntiforgeryOptions _options; @@ -72,23 +72,43 @@ namespace Microsoft.AspNet.Antiforgery Resources.FormatAntiforgery_CookieToken_MustBeProvided(_options.CookieName)); } - if (!httpContext.Request.HasFormContentType) + StringValues requestToken; + if (httpContext.Request.HasFormContentType) { // Check the content-type before accessing the form collection to make sure // we throw gracefully. - throw new InvalidOperationException( - Resources.FormatAntiforgery_FormToken_MustBeProvided(_options.FormFieldName)); + var form = await httpContext.Request.ReadFormAsync(); + requestToken = form[_options.FormFieldName]; } - var form = await httpContext.Request.ReadFormAsync(); - var formField = form[_options.FormFieldName]; - if (string.IsNullOrEmpty(formField)) + // Fall back to header if the form value was not provided. + if (requestToken.Count == 0 && _options.HeaderName != null) { - throw new InvalidOperationException( - Resources.FormatAntiforgery_FormToken_MustBeProvided(_options.FormFieldName)); + requestToken = httpContext.Request.Headers[_options.HeaderName]; } - return new AntiforgeryTokenSet(formField, requestCookie); + if (requestToken.Count == 0) + { + if (_options.HeaderName == null) + { + var message = Resources.FormatAntiforgery_FormToken_MustBeProvided(_options.FormFieldName); + throw new InvalidOperationException(message); + } + else if (!httpContext.Request.HasFormContentType) + { + var message = Resources.FormatAntiforgery_HeaderToken_MustBeProvided(_options.HeaderName); + throw new InvalidOperationException(message); + } + else + { + var message = Resources.FormatAntiforgery_RequestToken_MustBeProvided( + _options.FormFieldName, + _options.HeaderName); + throw new InvalidOperationException(message); + } + } + + return new AntiforgeryTokenSet(requestToken, requestCookie); } public void SaveCookieToken(HttpContext httpContext, AntiforgeryToken token) diff --git a/src/Microsoft.AspNet.Antiforgery/IAntiforgery.cs b/src/Microsoft.AspNet.Antiforgery/IAntiforgery.cs index 9d11d4dad6..1944b02622 100644 --- a/src/Microsoft.AspNet.Antiforgery/IAntiforgery.cs +++ b/src/Microsoft.AspNet.Antiforgery/IAntiforgery.cs @@ -61,7 +61,7 @@ namespace Microsoft.AspNet.Antiforgery /// /// The associated with the current request. /// - /// The (cookie and form token) for this request. + /// The (cookie and request token) for this request. /// void ValidateTokens(HttpContext context, AntiforgeryTokenSet antiforgeryTokenSet); diff --git a/src/Microsoft.AspNet.Antiforgery/IAntiforgeryTokenGenerator.cs b/src/Microsoft.AspNet.Antiforgery/IAntiforgeryTokenGenerator.cs index 0918d26917..47b701817c 100644 --- a/src/Microsoft.AspNet.Antiforgery/IAntiforgeryTokenGenerator.cs +++ b/src/Microsoft.AspNet.Antiforgery/IAntiforgeryTokenGenerator.cs @@ -13,9 +13,9 @@ namespace Microsoft.AspNet.Antiforgery // Generates a new random cookie token. AntiforgeryToken GenerateCookieToken(); - // Given a cookie token, generates a corresponding form token. + // Given a cookie token, generates a corresponding request token. // The incoming cookie token must be valid. - AntiforgeryToken GenerateFormToken( + AntiforgeryToken GenerateRequestToken( HttpContext httpContext, AntiforgeryToken cookieToken); @@ -23,10 +23,10 @@ namespace Microsoft.AspNet.Antiforgery // If it is not, the caller must call GenerateCookieToken() before calling GenerateFormToken(). bool IsCookieTokenValid(AntiforgeryToken cookieToken); - // Validates a (cookie, form) token pair. + // Validates a (cookie, request) token pair. void ValidateTokens( HttpContext httpContext, AntiforgeryToken cookieToken, - AntiforgeryToken formToken); + AntiforgeryToken requestToken); } } \ No newline at end of file diff --git a/src/Microsoft.AspNet.Antiforgery/IAntiforgeryTokenStore.cs b/src/Microsoft.AspNet.Antiforgery/IAntiforgeryTokenStore.cs index c600d68e53..15ea992cb8 100644 --- a/src/Microsoft.AspNet.Antiforgery/IAntiforgeryTokenStore.cs +++ b/src/Microsoft.AspNet.Antiforgery/IAntiforgeryTokenStore.cs @@ -12,7 +12,7 @@ namespace Microsoft.AspNet.Antiforgery AntiforgeryToken GetCookieToken(HttpContext httpContext); /// - /// Gets the cookie and form tokens from the request. Will throw an exception if either token is + /// Gets the cookie and request tokens from the request. Will throw an exception if either token is /// not present. /// /// The for the current request. diff --git a/src/Microsoft.AspNet.Antiforgery/Properties/Resources.Designer.cs b/src/Microsoft.AspNet.Antiforgery/Properties/Resources.Designer.cs index e4542a56f5..df958a3454 100644 --- a/src/Microsoft.AspNet.Antiforgery/Properties/Resources.Designer.cs +++ b/src/Microsoft.AspNet.Antiforgery/Properties/Resources.Designer.cs @@ -75,7 +75,7 @@ namespace Microsoft.AspNet.Antiforgery } /// - /// The antiforgery cookie token and form field token do not match. + /// The antiforgery cookie token and request token do not match. /// internal static string AntiforgeryToken_SecurityTokenMismatch { @@ -83,7 +83,7 @@ namespace Microsoft.AspNet.Antiforgery } /// - /// The antiforgery cookie token and form field token do not match. + /// The antiforgery cookie token and request token do not match. /// internal static string FormatAntiforgeryToken_SecurityTokenMismatch() { @@ -91,7 +91,7 @@ namespace Microsoft.AspNet.Antiforgery } /// - /// Validation of the provided antiforgery token failed. The cookie token and the form token were swapped. + /// Validation of the provided antiforgery token failed. The cookie token and the request token were swapped. /// internal static string AntiforgeryToken_TokensSwapped { @@ -99,7 +99,7 @@ namespace Microsoft.AspNet.Antiforgery } /// - /// Validation of the provided antiforgery token failed. The cookie token and the form token were swapped. + /// Validation of the provided antiforgery token failed. The cookie token and the request token were swapped. /// internal static string FormatAntiforgeryToken_TokensSwapped() { @@ -155,7 +155,7 @@ namespace Microsoft.AspNet.Antiforgery } /// - /// The cookie token must be provided. + /// The required antiforgery cookie token must be provided. /// internal static string Antiforgery_CookieToken_MustBeProvided_Generic { @@ -163,7 +163,7 @@ namespace Microsoft.AspNet.Antiforgery } /// - /// The cookie token must be provided. + /// The required antiforgery cookie token must be provided. /// internal static string FormatAntiforgery_CookieToken_MustBeProvided_Generic() { @@ -187,19 +187,51 @@ namespace Microsoft.AspNet.Antiforgery } /// - /// The form token must be provided. + /// The required antiforgery header value "{0}" is not present. /// - internal static string Antiforgery_FormToken_MustBeProvided_Generic + internal static string Antiforgery_HeaderToken_MustBeProvided { - get { return GetString("Antiforgery_FormToken_MustBeProvided_Generic"); } + get { return GetString("Antiforgery_HeaderToken_MustBeProvided"); } } /// - /// The form token must be provided. + /// The required antiforgery header value "{0}" is not present. /// - internal static string FormatAntiforgery_FormToken_MustBeProvided_Generic() + internal static string FormatAntiforgery_HeaderToken_MustBeProvided(object p0) { - return GetString("Antiforgery_FormToken_MustBeProvided_Generic"); + return string.Format(CultureInfo.CurrentCulture, GetString("Antiforgery_HeaderToken_MustBeProvided"), p0); + } + + /// + /// The required antiforgery request token was not provided in either form field "{0}" or header value "{1}". + /// + internal static string Antiforgery_RequestToken_MustBeProvided + { + get { return GetString("Antiforgery_RequestToken_MustBeProvided"); } + } + + /// + /// The required antiforgery request token was not provided in either form field "{0}" or header value "{1}". + /// + internal static string FormatAntiforgery_RequestToken_MustBeProvided(object p0, object p1) + { + return string.Format(CultureInfo.CurrentCulture, GetString("Antiforgery_RequestToken_MustBeProvided"), p0, p1); + } + + /// + /// The required antiforgery request token must be provided. + /// + internal static string Antiforgery_RequestToken_MustBeProvided_Generic + { + get { return GetString("Antiforgery_RequestToken_MustBeProvided_Generic"); } + } + + /// + /// The required antiforgery request token must be provided. + /// + internal static string FormatAntiforgery_RequestToken_MustBeProvided_Generic() + { + return GetString("Antiforgery_RequestToken_MustBeProvided_Generic"); } /// diff --git a/src/Microsoft.AspNet.Antiforgery/Resources.resx b/src/Microsoft.AspNet.Antiforgery/Resources.resx index cb8f9c3db8..8e84c4cac5 100644 --- a/src/Microsoft.AspNet.Antiforgery/Resources.resx +++ b/src/Microsoft.AspNet.Antiforgery/Resources.resx @@ -131,10 +131,10 @@ The antiforgery token could not be decrypted. - The antiforgery cookie token and form field token do not match. + The antiforgery cookie token and request token do not match. - Validation of the provided antiforgery token failed. The cookie token and the form token were swapped. + Validation of the provided antiforgery token failed. The cookie token and the request token were swapped. The provided antiforgery token was meant for user "{0}", but the current user is "{1}". @@ -147,13 +147,19 @@ The required antiforgery cookie "{0}" is not present. - The cookie token must be provided. + The required antiforgery cookie token must be provided. The required antiforgery form field "{0}" is not present. - - The form token must be provided. + + The required antiforgery header value "{0}" is not present. + + + The required antiforgery request token was not provided in either form field "{0}" or header value "{1}". + + + The required antiforgery request token must be provided. Value cannot be null or empty. diff --git a/test/Microsoft.AspNet.Antiforgery.Test/AntiforgeryTokenTest.cs b/test/Microsoft.AspNet.Antiforgery.Test/AntiforgeryTokenTest.cs index cddd47e43f..c56055bee7 100644 --- a/test/Microsoft.AspNet.Antiforgery.Test/AntiforgeryTokenTest.cs +++ b/test/Microsoft.AspNet.Antiforgery.Test/AntiforgeryTokenTest.cs @@ -45,21 +45,21 @@ namespace Microsoft.AspNet.Antiforgery } [Fact] - public void IsSessionTokenProperty() + public void IsCookieTokenProperty() { // Arrange var token = new AntiforgeryToken(); // Act & assert - 1 - Assert.False(token.IsSessionToken); + Assert.False(token.IsCookieToken); // Act & assert - 2 - token.IsSessionToken = true; - Assert.True(token.IsSessionToken); + token.IsCookieToken = true; + Assert.True(token.IsCookieToken); // Act & assert - 3 - token.IsSessionToken = false; - Assert.False(token.IsSessionToken); + token.IsCookieToken = false; + Assert.False(token.IsCookieToken); } [Fact] diff --git a/test/Microsoft.AspNet.Antiforgery.Test/DefaultAntiforgeryTest.cs b/test/Microsoft.AspNet.Antiforgery.Test/DefaultAntiforgeryTest.cs index 5f6b2ea59f..1a85a6826a 100644 --- a/test/Microsoft.AspNet.Antiforgery.Test/DefaultAntiforgeryTest.cs +++ b/test/Microsoft.AspNet.Antiforgery.Test/DefaultAntiforgeryTest.cs @@ -289,7 +289,7 @@ namespace Microsoft.AspNet.Antiforgery // Assert Assert.Equal("serialized-new-cookie-token", tokenset.CookieToken); - Assert.Equal("serialized-form-token", tokenset.FormToken); + Assert.Equal("serialized-form-token", tokenset.RequestToken); } [Fact] @@ -316,7 +316,7 @@ namespace Microsoft.AspNet.Antiforgery // Assert Assert.Equal("serialized-new-cookie-token", tokenset.CookieToken); - Assert.Equal("serialized-form-token", tokenset.FormToken); + Assert.Equal("serialized-form-token", tokenset.RequestToken); } [Fact] @@ -334,7 +334,7 @@ namespace Microsoft.AspNet.Antiforgery // Assert Assert.Equal("serialized-old-cookie-token", tokenset.CookieToken); - Assert.Equal("serialized-form-token", tokenset.FormToken); + Assert.Equal("serialized-form-token", tokenset.RequestToken); } [Fact] @@ -356,7 +356,7 @@ namespace Microsoft.AspNet.Antiforgery t => t.SaveCookieToken(It.IsAny(), It.IsAny()), Times.Never); Assert.Equal("serialized-old-cookie-token", tokenSet.CookieToken); - Assert.Equal("serialized-form-token", tokenSet.FormToken); + Assert.Equal("serialized-form-token", tokenSet.RequestToken); } [Fact] @@ -377,7 +377,7 @@ namespace Microsoft.AspNet.Antiforgery t => t.SaveCookieToken(It.IsAny(), It.IsAny()), Times.Once); Assert.Equal("serialized-new-cookie-token", tokenSet.CookieToken); - Assert.Equal("serialized-form-token", tokenSet.FormToken); + Assert.Equal("serialized-form-token", tokenSet.RequestToken); } [Fact] @@ -391,13 +391,13 @@ namespace Microsoft.AspNet.Antiforgery .Returns(context.TestTokenSet.OldCookieToken); context.TokenSerializer .Setup(o => o.Deserialize("form-token")) - .Returns(context.TestTokenSet.FormToken); + .Returns(context.TestTokenSet.RequestToken); context.TokenGenerator .Setup(o => o.ValidateTokens( context.HttpContext, context.TestTokenSet.OldCookieToken, - context.TestTokenSet.FormToken)) + context.TestTokenSet.RequestToken)) .Throws(new InvalidOperationException("my-message")); context.TokenStore = null; var antiforgery = GetAntiforgery(context); @@ -421,13 +421,13 @@ namespace Microsoft.AspNet.Antiforgery .Returns(context.TestTokenSet.OldCookieToken); context.TokenSerializer .Setup(o => o.Deserialize("form-token")) - .Returns(context.TestTokenSet.FormToken); + .Returns(context.TestTokenSet.RequestToken); context.TokenGenerator .Setup(o => o.ValidateTokens( context.HttpContext, context.TestTokenSet.OldCookieToken, - context.TestTokenSet.FormToken)) + context.TestTokenSet.RequestToken)) .Verifiable(); context.TokenStore = null; var antiforgery = GetAntiforgery(context); @@ -455,7 +455,7 @@ namespace Microsoft.AspNet.Antiforgery // Assert var trimmed = exception.Message.Substring(0, exception.Message.IndexOf(Environment.NewLine)); - Assert.Equal("The cookie token must be provided.", trimmed); + Assert.Equal("The required antiforgery cookie token must be provided.", trimmed); } [Fact] @@ -468,7 +468,7 @@ namespace Microsoft.AspNet.Antiforgery .Setup(o => o.ValidateTokens( context.HttpContext, context.TestTokenSet.OldCookieToken, - context.TestTokenSet.FormToken)) + context.TestTokenSet.RequestToken)) .Throws(new InvalidOperationException("my-message")); var antiforgery = GetAntiforgery(context); @@ -489,7 +489,7 @@ namespace Microsoft.AspNet.Antiforgery .Setup(o => o.ValidateTokens( context.HttpContext, context.TestTokenSet.OldCookieToken, - context.TestTokenSet.FormToken)) + context.TestTokenSet.RequestToken)) .Verifiable(); var antiforgery = GetAntiforgery(context); @@ -567,7 +567,7 @@ namespace Microsoft.AspNet.Antiforgery bool saveNewCookie = true) { var oldCookieToken = testTokenSet.OldCookieToken; - var formToken = testTokenSet.FormToken; + var formToken = testTokenSet.RequestToken; var mockTokenStore = new Mock(MockBehavior.Strict); mockTokenStore.Setup(o => o.GetCookieToken(context)) .Returns(oldCookieToken); @@ -591,7 +591,7 @@ namespace Microsoft.AspNet.Antiforgery { var oldCookieToken = testTokenSet.OldCookieToken; var newCookieToken = testTokenSet.NewCookieToken; - var formToken = testTokenSet.FormToken; + var formToken = testTokenSet.RequestToken; var mockSerializer = new Mock(MockBehavior.Strict); mockSerializer.Setup(o => o.Serialize(formToken)) .Returns(testTokenSet.FormTokenString); @@ -613,7 +613,7 @@ namespace Microsoft.AspNet.Antiforgery { // Arrange var httpContext = GetHttpContext(); - var testTokenSet = GetTokenSet(isOldCookieTokenSessionToken: true, isNewCookieSessionToken: true); + var testTokenSet = GetTokenSet(); var mockSerializer = GetTokenSerializer(testTokenSet); @@ -621,10 +621,10 @@ namespace Microsoft.AspNet.Antiforgery var mockGenerator = new Mock(MockBehavior.Strict); mockGenerator - .Setup(o => o.GenerateFormToken( + .Setup(o => o.GenerateRequestToken( httpContext, useOldCookie ? testTokenSet.OldCookieToken : testTokenSet.NewCookieToken)) - .Returns(testTokenSet.FormToken); + .Returns(testTokenSet.RequestToken); mockGenerator .Setup(o => o.GenerateCookieToken()) @@ -649,22 +649,22 @@ namespace Microsoft.AspNet.Antiforgery }; } - private TestTokenSet GetTokenSet(bool isOldCookieTokenSessionToken = true, bool isNewCookieSessionToken = true) + private TestTokenSet GetTokenSet() { return new TestTokenSet() { - FormToken = new AntiforgeryToken() { IsSessionToken = false }, + RequestToken = new AntiforgeryToken() { IsCookieToken = false }, FormTokenString = "serialized-form-token", - OldCookieToken = new AntiforgeryToken() { IsSessionToken = isOldCookieTokenSessionToken }, + OldCookieToken = new AntiforgeryToken() { IsCookieToken = true }, OldCookieTokenString = "serialized-old-cookie-token", - NewCookieToken = new AntiforgeryToken() { IsSessionToken = isNewCookieSessionToken }, + NewCookieToken = new AntiforgeryToken() { IsCookieToken = true }, NewCookieTokenString = "serialized-new-cookie-token", }; } private class TestTokenSet { - public AntiforgeryToken FormToken { get; set; } + public AntiforgeryToken RequestToken { get; set; } public string FormTokenString { get; set; } diff --git a/test/Microsoft.AspNet.Antiforgery.Test/DefaultAntiforgeryTokenGeneratorTest.cs b/test/Microsoft.AspNet.Antiforgery.Test/DefaultAntiforgeryTokenGeneratorTest.cs index 10fd048d19..4813d68411 100644 --- a/test/Microsoft.AspNet.Antiforgery.Test/DefaultAntiforgeryTokenGeneratorTest.cs +++ b/test/Microsoft.AspNet.Antiforgery.Test/DefaultAntiforgeryTokenGeneratorTest.cs @@ -31,7 +31,7 @@ namespace Microsoft.AspNet.Antiforgery public void GenerateFormToken_AnonymousUser() { // Arrange - var cookieToken = new AntiforgeryToken() { IsSessionToken = true }; + var cookieToken = new AntiforgeryToken() { IsCookieToken = true }; var httpContext = new DefaultHttpContext(); httpContext.User = new ClaimsPrincipal(new ClaimsIdentity()); Assert.False(httpContext.User.Identity.IsAuthenticated); @@ -41,12 +41,12 @@ namespace Microsoft.AspNet.Antiforgery additionalDataProvider: null); // Act - var fieldToken = tokenProvider.GenerateFormToken(httpContext, cookieToken); + var fieldToken = tokenProvider.GenerateRequestToken(httpContext, cookieToken); // Assert Assert.NotNull(fieldToken); Assert.Equal(cookieToken.SecurityToken, fieldToken.SecurityToken); - Assert.False(fieldToken.IsSessionToken); + Assert.False(fieldToken.IsCookieToken); Assert.Empty(fieldToken.Username); Assert.Null(fieldToken.ClaimUid); Assert.Empty(fieldToken.AdditionalData); @@ -58,7 +58,7 @@ namespace Microsoft.AspNet.Antiforgery // Arrange var cookieToken = new AntiforgeryToken() { - IsSessionToken = true + IsCookieToken = true }; var httpContext = new DefaultHttpContext(); @@ -73,7 +73,7 @@ namespace Microsoft.AspNet.Antiforgery // Act & assert var exception = Assert.Throws( - () => tokenProvider.GenerateFormToken(httpContext, cookieToken)); + () => tokenProvider.GenerateRequestToken(httpContext, cookieToken)); Assert.Equal( "The provided identity of type " + $"'{typeof(MyAuthenticatedIdentityWithoutUsername).FullName}' " + @@ -90,7 +90,7 @@ namespace Microsoft.AspNet.Antiforgery public void GenerateFormToken_AuthenticatedWithoutUsername_WithAdditionalData() { // Arrange - var cookieToken = new AntiforgeryToken() { IsSessionToken = true }; + var cookieToken = new AntiforgeryToken() { IsCookieToken = true }; var httpContext = new DefaultHttpContext(); httpContext.User = new ClaimsPrincipal(new MyAuthenticatedIdentityWithoutUsername()); @@ -106,12 +106,12 @@ namespace Microsoft.AspNet.Antiforgery additionalDataProvider: mockAdditionalDataProvider.Object); // Act - var fieldToken = tokenProvider.GenerateFormToken(httpContext, cookieToken); + var fieldToken = tokenProvider.GenerateRequestToken(httpContext, cookieToken); // Assert Assert.NotNull(fieldToken); Assert.Equal(cookieToken.SecurityToken, fieldToken.SecurityToken); - Assert.False(fieldToken.IsSessionToken); + Assert.False(fieldToken.IsCookieToken); Assert.Empty(fieldToken.Username); Assert.Null(fieldToken.ClaimUid); Assert.Equal("additional-data", fieldToken.AdditionalData); @@ -121,7 +121,7 @@ namespace Microsoft.AspNet.Antiforgery public void GenerateFormToken_ClaimsBasedIdentity() { // Arrange - var cookieToken = new AntiforgeryToken() { IsSessionToken = true }; + var cookieToken = new AntiforgeryToken() { IsCookieToken = true }; var identity = GetAuthenticatedIdentity("some-identity"); var httpContext = new DefaultHttpContext(); @@ -144,12 +144,12 @@ namespace Microsoft.AspNet.Antiforgery additionalDataProvider: null); // Act - var fieldToken = tokenProvider.GenerateFormToken(httpContext, cookieToken); + var fieldToken = tokenProvider.GenerateRequestToken(httpContext, cookieToken); // Assert Assert.NotNull(fieldToken); Assert.Equal(cookieToken.SecurityToken, fieldToken.SecurityToken); - Assert.False(fieldToken.IsSessionToken); + Assert.False(fieldToken.IsCookieToken); Assert.Equal("", fieldToken.Username); Assert.Equal(expectedClaimUid, fieldToken.ClaimUid); Assert.Equal("", fieldToken.AdditionalData); @@ -159,7 +159,7 @@ namespace Microsoft.AspNet.Antiforgery public void GenerateFormToken_RegularUserWithUsername() { // Arrange - var cookieToken = new AntiforgeryToken() { IsSessionToken = true }; + var cookieToken = new AntiforgeryToken() { IsCookieToken = true }; var httpContext = new DefaultHttpContext(); var mockIdentity = new Mock(); @@ -177,12 +177,12 @@ namespace Microsoft.AspNet.Antiforgery additionalDataProvider: null); // Act - var fieldToken = tokenProvider.GenerateFormToken(httpContext, cookieToken); + var fieldToken = tokenProvider.GenerateRequestToken(httpContext, cookieToken); // Assert Assert.NotNull(fieldToken); Assert.Equal(cookieToken.SecurityToken, fieldToken.SecurityToken); - Assert.False(fieldToken.IsSessionToken); + Assert.False(fieldToken.IsCookieToken); Assert.Equal("my-username", fieldToken.Username); Assert.Null(fieldToken.ClaimUid); Assert.Empty(fieldToken.AdditionalData); @@ -194,7 +194,7 @@ namespace Microsoft.AspNet.Antiforgery // Arrange var cookieToken = new AntiforgeryToken() { - IsSessionToken = false + IsCookieToken = false }; var tokenProvider = new DefaultAntiforgeryTokenGenerator( @@ -230,7 +230,7 @@ namespace Microsoft.AspNet.Antiforgery // Arrange var cookieToken = new AntiforgeryToken() { - IsSessionToken = true + IsCookieToken = true }; var tokenProvider = new DefaultAntiforgeryTokenGenerator( @@ -246,13 +246,13 @@ namespace Microsoft.AspNet.Antiforgery [Fact] - public void ValidateTokens_SessionTokenMissing() + public void ValidateTokens_CookieTokenMissing() { // Arrange var httpContext = new DefaultHttpContext(); httpContext.User = new ClaimsPrincipal(new ClaimsIdentity()); - var fieldtoken = new AntiforgeryToken() { IsSessionToken = false }; + var fieldtoken = new AntiforgeryToken() { IsCookieToken = false }; var tokenProvider = new DefaultAntiforgeryTokenGenerator( claimUidExtractor: null, @@ -263,7 +263,7 @@ namespace Microsoft.AspNet.Antiforgery () => 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); + Assert.Equal(@"The required antiforgery cookie token must be provided.", trimmed); } [Fact] @@ -273,7 +273,7 @@ namespace Microsoft.AspNet.Antiforgery var httpContext = new DefaultHttpContext(); httpContext.User = new ClaimsPrincipal(new ClaimsIdentity()); - var sessionToken = new AntiforgeryToken() { IsSessionToken = true }; + var cookieToken = new AntiforgeryToken() { IsCookieToken = true }; var tokenProvider = new DefaultAntiforgeryTokenGenerator( claimUidExtractor: null, @@ -281,21 +281,21 @@ namespace Microsoft.AspNet.Antiforgery // Act & assert var ex = Assert.Throws( - () => tokenProvider.ValidateTokens(httpContext, sessionToken, null)); + () => tokenProvider.ValidateTokens(httpContext, cookieToken, null)); var trimmed = ex.Message.Substring(0, ex.Message.IndexOf(Environment.NewLine)); - Assert.Equal("The form token must be provided.", trimmed); + Assert.Equal("The required antiforgery request token must be provided.", trimmed); } [Fact] - public void ValidateTokens_FieldAndSessionTokensSwapped() + public void ValidateTokens_FieldAndCookieTokensSwapped() { // Arrange var httpContext = new DefaultHttpContext(); httpContext.User = new ClaimsPrincipal(new ClaimsIdentity()); - var sessionToken = new AntiforgeryToken() { IsSessionToken = true }; - var fieldtoken = new AntiforgeryToken() { IsSessionToken = false }; + var cookieToken = new AntiforgeryToken() { IsCookieToken = true }; + var fieldtoken = new AntiforgeryToken() { IsCookieToken = false }; var tokenProvider = new DefaultAntiforgeryTokenGenerator( claimUidExtractor: null, @@ -307,27 +307,27 @@ namespace Microsoft.AspNet.Antiforgery () => tokenProvider.ValidateTokens(httpContext, fieldtoken, fieldtoken)); Assert.Equal( "Validation of the provided antiforgery token failed. " + - @"The cookie token and the form token were swapped.", + @"The cookie token and the request token were swapped.", ex1.Message); var ex2 = Assert.Throws( - () => tokenProvider.ValidateTokens(httpContext, sessionToken, sessionToken)); + () => tokenProvider.ValidateTokens(httpContext, cookieToken, cookieToken)); Assert.Equal( "Validation of the provided antiforgery token failed. " + - @"The cookie token and the form token were swapped.", + @"The cookie token and the request token were swapped.", ex2.Message); } [Fact] - public void ValidateTokens_FieldAndSessionTokensHaveDifferentSecurityKeys() + public void ValidateTokens_FieldAndCookieTokensHaveDifferentSecurityKeys() { // Arrange var httpContext = new DefaultHttpContext(); httpContext.User = new ClaimsPrincipal(new ClaimsIdentity()); - var sessionToken = new AntiforgeryToken() { IsSessionToken = true }; - var fieldtoken = new AntiforgeryToken() { IsSessionToken = false }; + var cookieToken = new AntiforgeryToken() { IsCookieToken = true }; + var fieldtoken = new AntiforgeryToken() { IsCookieToken = false }; var tokenProvider = new DefaultAntiforgeryTokenGenerator( claimUidExtractor: null, @@ -335,9 +335,9 @@ namespace Microsoft.AspNet.Antiforgery // Act & Assert var exception = Assert.Throws( - () => tokenProvider.ValidateTokens(httpContext, sessionToken, fieldtoken)); + () => tokenProvider.ValidateTokens(httpContext, cookieToken, fieldtoken)); Assert.Equal( - @"The antiforgery cookie token and form field token do not match.", + @"The antiforgery cookie token and request token do not match.", exception.Message); } @@ -352,12 +352,12 @@ namespace Microsoft.AspNet.Antiforgery var identity = GetAuthenticatedIdentity(identityUsername); httpContext.User = new ClaimsPrincipal(identity); - var sessionToken = new AntiforgeryToken() { IsSessionToken = true }; + var cookieToken = new AntiforgeryToken() { IsCookieToken = true }; var fieldtoken = new AntiforgeryToken() { - SecurityToken = sessionToken.SecurityToken, + SecurityToken = cookieToken.SecurityToken, Username = embeddedUsername, - IsSessionToken = false + IsCookieToken = false }; var mockClaimUidExtractor = new Mock(); @@ -370,7 +370,7 @@ namespace Microsoft.AspNet.Antiforgery // Act & Assert var exception = Assert.Throws( - () => tokenProvider.ValidateTokens(httpContext, sessionToken, fieldtoken)); + () => tokenProvider.ValidateTokens(httpContext, cookieToken, fieldtoken)); Assert.Equal( @"The provided antiforgery token was meant for user """ + embeddedUsername + @""", but the current user is """ + identityUsername + @""".", @@ -385,11 +385,11 @@ namespace Microsoft.AspNet.Antiforgery var identity = GetAuthenticatedIdentity("the-user"); httpContext.User = new ClaimsPrincipal(identity); - var sessionToken = new AntiforgeryToken() { IsSessionToken = true }; + var cookieToken = new AntiforgeryToken() { IsCookieToken = true }; var fieldtoken = new AntiforgeryToken() { - SecurityToken = sessionToken.SecurityToken, - IsSessionToken = false, + SecurityToken = cookieToken.SecurityToken, + IsCookieToken = false, ClaimUid = new BinaryBlob(256) }; @@ -404,7 +404,7 @@ namespace Microsoft.AspNet.Antiforgery // Act & assert var exception = Assert.Throws( - () => tokenProvider.ValidateTokens(httpContext, sessionToken, fieldtoken)); + () => tokenProvider.ValidateTokens(httpContext, cookieToken, fieldtoken)); Assert.Equal( @"The provided antiforgery token was meant for a different claims-based user than the current user.", exception.Message); @@ -418,12 +418,12 @@ namespace Microsoft.AspNet.Antiforgery var identity = new ClaimsIdentity(); httpContext.User = new ClaimsPrincipal(identity); - var sessionToken = new AntiforgeryToken() { IsSessionToken = true }; + var cookieToken = new AntiforgeryToken() { IsCookieToken = true }; var fieldtoken = new AntiforgeryToken() { - SecurityToken = sessionToken.SecurityToken, + SecurityToken = cookieToken.SecurityToken, Username = String.Empty, - IsSessionToken = false, + IsCookieToken = false, AdditionalData = "some-additional-data" }; @@ -437,7 +437,7 @@ namespace Microsoft.AspNet.Antiforgery // Act & assert var exception = Assert.Throws( - () => tokenProvider.ValidateTokens(httpContext, sessionToken, fieldtoken)); + () => tokenProvider.ValidateTokens(httpContext, cookieToken, fieldtoken)); Assert.Equal(@"The provided antiforgery token failed a custom data check.", exception.Message); } @@ -449,12 +449,12 @@ namespace Microsoft.AspNet.Antiforgery var identity = new ClaimsIdentity(); httpContext.User = new ClaimsPrincipal(identity); - var sessionToken = new AntiforgeryToken() { IsSessionToken = true }; + var cookieToken = new AntiforgeryToken() { IsCookieToken = true }; var fieldtoken = new AntiforgeryToken() { - SecurityToken = sessionToken.SecurityToken, + SecurityToken = cookieToken.SecurityToken, Username = String.Empty, - IsSessionToken = false, + IsCookieToken = false, AdditionalData = "some-additional-data" }; @@ -467,7 +467,7 @@ namespace Microsoft.AspNet.Antiforgery additionalDataProvider: mockAdditionalDataProvider.Object); // Act - tokenProvider.ValidateTokens(httpContext, sessionToken, fieldtoken); + tokenProvider.ValidateTokens(httpContext, cookieToken, fieldtoken); // Assert // Nothing to assert - if we got this far, success! @@ -481,12 +481,12 @@ namespace Microsoft.AspNet.Antiforgery var identity = GetAuthenticatedIdentity("the-user"); httpContext.User = new ClaimsPrincipal(identity); - var sessionToken = new AntiforgeryToken() { IsSessionToken = true }; + var cookieToken = new AntiforgeryToken() { IsCookieToken = true }; var fieldtoken = new AntiforgeryToken() { - SecurityToken = sessionToken.SecurityToken, + SecurityToken = cookieToken.SecurityToken, Username = "THE-USER", - IsSessionToken = false, + IsCookieToken = false, AdditionalData = "some-additional-data" }; @@ -499,7 +499,7 @@ namespace Microsoft.AspNet.Antiforgery additionalDataProvider: mockAdditionalDataProvider.Object); // Act - tokenProvider.ValidateTokens(httpContext, sessionToken, fieldtoken); + tokenProvider.ValidateTokens(httpContext, cookieToken, fieldtoken); // Assert // Nothing to assert - if we got this far, success! @@ -513,11 +513,11 @@ namespace Microsoft.AspNet.Antiforgery var identity = GetAuthenticatedIdentity("the-user"); httpContext.User = new ClaimsPrincipal(identity); - var sessionToken = new AntiforgeryToken() { IsSessionToken = true }; + var cookieToken = new AntiforgeryToken() { IsCookieToken = true }; var fieldtoken = new AntiforgeryToken() { - SecurityToken = sessionToken.SecurityToken, - IsSessionToken = false, + SecurityToken = cookieToken.SecurityToken, + IsCookieToken = false, ClaimUid = new BinaryBlob(256) }; @@ -530,7 +530,7 @@ namespace Microsoft.AspNet.Antiforgery additionalDataProvider: null); // Act - tokenProvider.ValidateTokens(httpContext, sessionToken, fieldtoken); + tokenProvider.ValidateTokens(httpContext, cookieToken, fieldtoken); // Assert // Nothing to assert - if we got this far, success! diff --git a/test/Microsoft.AspNet.Antiforgery.Test/DefaultAntiforgeryTokenSerializerTest.cs b/test/Microsoft.AspNet.Antiforgery.Test/DefaultAntiforgeryTokenSerializerTest.cs index 0c074a2b13..a6012bcbe7 100644 --- a/test/Microsoft.AspNet.Antiforgery.Test/DefaultAntiforgeryTokenSerializerTest.cs +++ b/test/Microsoft.AspNet.Antiforgery.Test/DefaultAntiforgeryTokenSerializerTest.cs @@ -26,18 +26,18 @@ namespace Microsoft.AspNet.Antiforgery [InlineData( "01" // Version + "705EEDCC7D42F1D6B3B98A593625BB4C" // SecurityToken - + "01" // IsSessionToken + + "01" // IsCookieToken + "00" // (WRONG!) Too much data in stream )] [InlineData( "02" // (WRONG! - must be 0x01) Version + "705EEDCC7D42F1D6B3B98A593625BB4C" // SecurityToken - + "01" // IsSessionToken + + "01" // IsCookieToken )] [InlineData( "01" // Version + "705EEDCC7D42F1D6B3B98A593625BB4C" // SecurityToken - + "00" // IsSessionToken + + "00" // IsCookieToken + "00" // IsClaimsBased + "05" // Username length header + "0000" // (WRONG!) Too little data in stream @@ -60,7 +60,7 @@ namespace Microsoft.AspNet.Antiforgery //"01" // Version //+ "705EEDCC7D42F1D6B3B98A593625BB4C" // SecurityToken - //+ "00" // IsSessionToken + //+ "00" // IsCookieToken //+ "01" // IsClaimsBased //+ "6F1648E97249AA58754036A67E248CF044F07ECFB0ED387556CE029A4F9A40E0" // ClaimUid //+ "05" // AdditionalData length header @@ -68,7 +68,7 @@ namespace Microsoft.AspNet.Antiforgery var token = new AntiforgeryToken() { SecurityToken = _securityToken, - IsSessionToken = false, + IsCookieToken = false, ClaimUid = _claimUid, AdditionalData = "€47" }; @@ -90,7 +90,7 @@ namespace Microsoft.AspNet.Antiforgery //"01" // Version //+ "705EEDCC7D42F1D6B3B98A593625BB4C" // SecurityToken - //+ "00" // IsSessionToken + //+ "00" // IsCookieToken //+ "00" // IsClaimsBased //+ "08" // Username length header //+ "4AC3A972C3B46D65" // Username ("Jérôme") as UTF8 @@ -99,7 +99,7 @@ namespace Microsoft.AspNet.Antiforgery var token = new AntiforgeryToken() { SecurityToken = _securityToken, - IsSessionToken = false, + IsCookieToken = false, Username = "Jérôme", AdditionalData = "€47" }; @@ -114,18 +114,18 @@ namespace Microsoft.AspNet.Antiforgery } [Fact] - public void Serialize_SessionToken_TokenRoundTripSuccessful() + public void Serialize_CookieToken_TokenRoundTripSuccessful() { // Arrange var testSerializer = new DefaultAntiforgeryTokenSerializer(_dataProtector.Object); //"01" // Version //+ "705EEDCC7D42F1D6B3B98A593625BB4C" // SecurityToken - //+ "01"; // IsSessionToken + //+ "01"; // IsCookieToken var token = new AntiforgeryToken() { SecurityToken = _securityToken, - IsSessionToken = true + IsCookieToken = true }; // Act @@ -178,7 +178,7 @@ namespace Microsoft.AspNet.Antiforgery Assert.NotNull(actual); Assert.Equal(expected.AdditionalData, actual.AdditionalData); Assert.Equal(expected.ClaimUid, actual.ClaimUid); - Assert.Equal(expected.IsSessionToken, actual.IsSessionToken); + Assert.Equal(expected.IsCookieToken, actual.IsCookieToken); Assert.Equal(expected.SecurityToken, actual.SecurityToken); Assert.Equal(expected.Username, actual.Username); } diff --git a/test/Microsoft.AspNet.Antiforgery.Test/DefaultAntiforgeryTokenStoreTest.cs b/test/Microsoft.AspNet.Antiforgery.Test/DefaultAntiforgeryTokenStoreTest.cs index 275632ab4f..585c42e171 100644 --- a/test/Microsoft.AspNet.Antiforgery.Test/DefaultAntiforgeryTokenStoreTest.cs +++ b/test/Microsoft.AspNet.Antiforgery.Test/DefaultAntiforgeryTokenStoreTest.cs @@ -187,7 +187,7 @@ namespace Microsoft.AspNet.Antiforgery } [Fact] - public async Task GetRequestTokens_NonFormContentType_Throws() + public async Task GetRequestTokens_NonFormContentType_HeaderDisabled_Throws() { // Arrange var httpContext = new DefaultHttpContext(); @@ -204,6 +204,7 @@ namespace Microsoft.AspNet.Antiforgery { CookieName = "cookie-name", FormFieldName = "form-field-name", + HeaderName = null, }; var tokenStore = new DefaultAntiforgeryTokenStore( @@ -219,7 +220,110 @@ namespace Microsoft.AspNet.Antiforgery } [Fact] - public async Task GetRequestTokens_FormFieldIsEmpty_Throws() + public async Task GetRequestTokens_FormContentType_FallbackHeaderToken() + { + // Arrange + var httpContext = new DefaultHttpContext(); + httpContext.Request.ContentType = "application/json"; + + // Will not be accessed + httpContext.Request.ContentType = "application/x-www-form-urlencoded"; + httpContext.Request.Form = new FormCollection(new Dictionary()); + httpContext.Request.Cookies = new RequestCookieCollection(new Dictionary() + { + { "cookie-name", "cookie-value" }, + }); + httpContext.Request.Headers.Add("header-name", "header-value"); + + var options = new AntiforgeryOptions() + { + CookieName = "cookie-name", + FormFieldName = "form-field-name", + HeaderName = "header-name", + }; + + var tokenStore = new DefaultAntiforgeryTokenStore( + optionsAccessor: new TestOptionsManager(options), + tokenSerializer: new DefaultAntiforgeryTokenSerializer(new EphemeralDataProtectionProvider())); + + // Act + var tokens = await tokenStore.GetRequestTokensAsync(httpContext); + + // Assert + Assert.Equal("cookie-value", tokens.CookieToken); + Assert.Equal("header-value", tokens.RequestToken); + } + + [Fact] + public async Task GetRequestTokens_NonFormContentType_UsesHeaderToken() + { + // Arrange + var httpContext = new DefaultHttpContext(); + httpContext.Request.ContentType = "application/json"; + + // Will not be accessed + httpContext.Request.Form = null; + httpContext.Request.Cookies = new RequestCookieCollection(new Dictionary() + { + { "cookie-name", "cookie-value" }, + }); + + httpContext.Request.Headers.Add("header-name", "header-value"); + + var options = new AntiforgeryOptions() + { + CookieName = "cookie-name", + FormFieldName = "form-field-name", + HeaderName = "header-name", + }; + + var tokenStore = new DefaultAntiforgeryTokenStore( + optionsAccessor: new TestOptionsManager(options), + tokenSerializer: new DefaultAntiforgeryTokenSerializer(new EphemeralDataProtectionProvider())); + + // Act + var tokens = await tokenStore.GetRequestTokensAsync(httpContext); + + // Assert + Assert.Equal("cookie-value", tokens.CookieToken); + Assert.Equal("header-value", tokens.RequestToken); + } + + [Fact] + public async Task GetRequestTokens_NonFormContentType_UsesHeaderToken_ThrowsOnMissingValue() + { + // Arrange + var httpContext = new DefaultHttpContext(); + httpContext.Request.ContentType = "application/json"; + + // Will not be accessed + httpContext.Request.Form = null; + httpContext.Request.Cookies = new RequestCookieCollection(new Dictionary() + { + { "cookie-name", "cookie-value" }, + }); + + var options = new AntiforgeryOptions() + { + CookieName = "cookie-name", + FormFieldName = "form-field-name", + HeaderName = "header-name", + }; + + var tokenStore = new DefaultAntiforgeryTokenStore( + optionsAccessor: new TestOptionsManager(options), + tokenSerializer: new DefaultAntiforgeryTokenSerializer(new EphemeralDataProtectionProvider())); + + // Act + var exception = await Assert.ThrowsAsync( + async () => await tokenStore.GetRequestTokensAsync(httpContext)); + + // Assert + Assert.Equal("The required antiforgery header value \"header-name\" is not present.", exception.Message); + } + + [Fact] + public async Task GetRequestTokens_BothFieldsEmpty_Throws() { // Arrange var httpContext = new DefaultHttpContext(); @@ -234,6 +338,7 @@ namespace Microsoft.AspNet.Antiforgery { CookieName = "cookie-name", FormFieldName = "form-field-name", + HeaderName = "header-name", }; var tokenStore = new DefaultAntiforgeryTokenStore( @@ -245,7 +350,10 @@ namespace Microsoft.AspNet.Antiforgery async () => await tokenStore.GetRequestTokensAsync(httpContext)); // Assert - Assert.Equal("The required antiforgery form field \"form-field-name\" is not present.", exception.Message); + Assert.Equal( + "The required antiforgery request token was not provided in either form field \"form-field-name\" " + + "or header value \"header-name\".", + exception.Message); } [Fact] @@ -262,11 +370,13 @@ namespace Microsoft.AspNet.Antiforgery { { "cookie-name", "cookie-value" }, }); + httpContext.Request.Headers.Add("header-name", "header-value"); // form value has priority. var options = new AntiforgeryOptions() { CookieName = "cookie-name", FormFieldName = "form-field-name", + HeaderName = "header-name", }; var tokenStore = new DefaultAntiforgeryTokenStore( @@ -278,7 +388,7 @@ namespace Microsoft.AspNet.Antiforgery // Assert Assert.Equal("cookie-value", tokens.CookieToken); - Assert.Equal("form-value", tokens.FormToken); + Assert.Equal("form-value", tokens.RequestToken); } [Theory]