Adds a IsRequestValidAsync method
Some other misc cleanup - docs for IAntiforgeryTokenGenerator - Add HttpContext parameter where to all IAntiforgeryGenerator methods - rename parameters on DefaultAntiforgery
This commit is contained in:
parent
0ddfa5f0d8
commit
20140c4c15
|
|
@ -36,75 +36,100 @@ namespace Microsoft.AspNetCore.Antiforgery
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <inheritdoc />
|
/// <inheritdoc />
|
||||||
public IHtmlContent GetHtml(HttpContext context)
|
public IHtmlContent GetHtml(HttpContext httpContext)
|
||||||
{
|
{
|
||||||
if (context == null)
|
if (httpContext == null)
|
||||||
{
|
{
|
||||||
throw new ArgumentNullException(nameof(context));
|
throw new ArgumentNullException(nameof(httpContext));
|
||||||
}
|
}
|
||||||
|
|
||||||
CheckSSLConfig(context);
|
CheckSSLConfig(httpContext);
|
||||||
|
|
||||||
var tokenSet = GetAndStoreTokens(context);
|
var tokenSet = GetAndStoreTokens(httpContext);
|
||||||
return new InputContent(_options.FormFieldName, tokenSet.RequestToken);
|
return new InputContent(_options.FormFieldName, tokenSet.RequestToken);
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <inheritdoc />
|
/// <inheritdoc />
|
||||||
public AntiforgeryTokenSet GetAndStoreTokens(HttpContext context)
|
public AntiforgeryTokenSet GetAndStoreTokens(HttpContext httpContext)
|
||||||
{
|
{
|
||||||
if (context == null)
|
if (httpContext == null)
|
||||||
{
|
{
|
||||||
throw new ArgumentNullException(nameof(context));
|
throw new ArgumentNullException(nameof(httpContext));
|
||||||
}
|
}
|
||||||
|
|
||||||
CheckSSLConfig(context);
|
CheckSSLConfig(httpContext);
|
||||||
|
|
||||||
var tokenSet = GetTokensInternal(context);
|
var tokenSet = GetTokensInternal(httpContext);
|
||||||
if (tokenSet.IsNewCookieToken)
|
if (tokenSet.IsNewCookieToken)
|
||||||
{
|
{
|
||||||
SaveCookieTokenAndHeader(context, tokenSet.CookieToken);
|
SaveCookieTokenAndHeader(httpContext, tokenSet.CookieToken);
|
||||||
}
|
}
|
||||||
|
|
||||||
return Serialize(tokenSet);
|
return Serialize(tokenSet);
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <inheritdoc />
|
/// <inheritdoc />
|
||||||
public AntiforgeryTokenSet GetTokens(HttpContext context)
|
public AntiforgeryTokenSet GetTokens(HttpContext httpContext)
|
||||||
{
|
{
|
||||||
if (context == null)
|
if (httpContext == null)
|
||||||
{
|
{
|
||||||
throw new ArgumentNullException(nameof(context));
|
throw new ArgumentNullException(nameof(httpContext));
|
||||||
}
|
}
|
||||||
|
|
||||||
CheckSSLConfig(context);
|
CheckSSLConfig(httpContext);
|
||||||
|
|
||||||
var tokenSet = GetTokensInternal(context);
|
var tokenSet = GetTokensInternal(httpContext);
|
||||||
return Serialize(tokenSet);
|
return Serialize(tokenSet);
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <inheritdoc />
|
/// <inheritdoc />
|
||||||
public async Task ValidateRequestAsync(HttpContext context)
|
public async Task<bool> IsRequestValidAsync(HttpContext httpContext)
|
||||||
{
|
{
|
||||||
if (context == null)
|
if (httpContext == null)
|
||||||
{
|
{
|
||||||
throw new ArgumentNullException(nameof(context));
|
throw new ArgumentNullException(nameof(httpContext));
|
||||||
}
|
}
|
||||||
|
|
||||||
CheckSSLConfig(context);
|
CheckSSLConfig(httpContext);
|
||||||
|
|
||||||
var tokens = await _tokenStore.GetRequestTokensAsync(context);
|
var tokens = await _tokenStore.GetRequestTokensAsync(httpContext);
|
||||||
ValidateTokens(context, tokens);
|
|
||||||
|
// Extract cookie & request tokens
|
||||||
|
var deserializedCookieToken = _tokenSerializer.Deserialize(tokens.CookieToken);
|
||||||
|
var deserializedRequestToken = _tokenSerializer.Deserialize(tokens.RequestToken);
|
||||||
|
|
||||||
|
// Validate
|
||||||
|
string message;
|
||||||
|
return _tokenGenerator.TryValidateTokenSet(
|
||||||
|
httpContext,
|
||||||
|
deserializedCookieToken,
|
||||||
|
deserializedRequestToken,
|
||||||
|
out message);
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <inheritdoc />
|
/// <inheritdoc />
|
||||||
public void ValidateTokens(HttpContext context, AntiforgeryTokenSet antiforgeryTokenSet)
|
public async Task ValidateRequestAsync(HttpContext httpContext)
|
||||||
{
|
{
|
||||||
if (context == null)
|
if (httpContext == null)
|
||||||
{
|
{
|
||||||
throw new ArgumentNullException(nameof(context));
|
throw new ArgumentNullException(nameof(httpContext));
|
||||||
}
|
}
|
||||||
|
|
||||||
CheckSSLConfig(context);
|
CheckSSLConfig(httpContext);
|
||||||
|
|
||||||
|
var tokens = await _tokenStore.GetRequestTokensAsync(httpContext);
|
||||||
|
ValidateTokens(httpContext, tokens);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <inheritdoc />
|
||||||
|
public void ValidateTokens(HttpContext httpContext, AntiforgeryTokenSet antiforgeryTokenSet)
|
||||||
|
{
|
||||||
|
if (httpContext == null)
|
||||||
|
{
|
||||||
|
throw new ArgumentNullException(nameof(httpContext));
|
||||||
|
}
|
||||||
|
|
||||||
|
CheckSSLConfig(httpContext);
|
||||||
|
|
||||||
if (string.IsNullOrEmpty(antiforgeryTokenSet.CookieToken))
|
if (string.IsNullOrEmpty(antiforgeryTokenSet.CookieToken))
|
||||||
{
|
{
|
||||||
|
|
@ -125,25 +150,30 @@ namespace Microsoft.AspNetCore.Antiforgery
|
||||||
var deserializedRequestToken = _tokenSerializer.Deserialize(antiforgeryTokenSet.RequestToken);
|
var deserializedRequestToken = _tokenSerializer.Deserialize(antiforgeryTokenSet.RequestToken);
|
||||||
|
|
||||||
// Validate
|
// Validate
|
||||||
_tokenGenerator.ValidateTokens(
|
string message;
|
||||||
context,
|
if (!_tokenGenerator.TryValidateTokenSet(
|
||||||
|
httpContext,
|
||||||
deserializedCookieToken,
|
deserializedCookieToken,
|
||||||
deserializedRequestToken);
|
deserializedRequestToken,
|
||||||
|
out message))
|
||||||
|
{
|
||||||
|
throw new AntiforgeryValidationException(message);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <inheritdoc />
|
/// <inheritdoc />
|
||||||
public void SetCookieTokenAndHeader(HttpContext context)
|
public void SetCookieTokenAndHeader(HttpContext httpContext)
|
||||||
{
|
{
|
||||||
if (context == null)
|
if (httpContext == null)
|
||||||
{
|
{
|
||||||
throw new ArgumentNullException(nameof(context));
|
throw new ArgumentNullException(nameof(httpContext));
|
||||||
}
|
}
|
||||||
|
|
||||||
CheckSSLConfig(context);
|
CheckSSLConfig(httpContext);
|
||||||
|
|
||||||
var cookieToken = GetCookieTokenDoesNotThrow(context);
|
var cookieToken = GetCookieTokenDoesNotThrow(httpContext);
|
||||||
cookieToken = ValidateAndGenerateNewCookieToken(cookieToken);
|
cookieToken = ValidateAndGenerateNewCookieToken(cookieToken);
|
||||||
SaveCookieTokenAndHeader(context, cookieToken);
|
SaveCookieTokenAndHeader(httpContext, cookieToken);
|
||||||
}
|
}
|
||||||
|
|
||||||
// This method returns null if oldCookieToken is valid.
|
// This method returns null if oldCookieToken is valid.
|
||||||
|
|
@ -208,16 +238,16 @@ namespace Microsoft.AspNetCore.Antiforgery
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private AntiforgeryTokenSetInternal GetTokensInternal(HttpContext context)
|
private AntiforgeryTokenSetInternal GetTokensInternal(HttpContext httpContext)
|
||||||
{
|
{
|
||||||
var cookieToken = GetCookieTokenDoesNotThrow(context);
|
var cookieToken = GetCookieTokenDoesNotThrow(httpContext);
|
||||||
var newCookieToken = ValidateAndGenerateNewCookieToken(cookieToken);
|
var newCookieToken = ValidateAndGenerateNewCookieToken(cookieToken);
|
||||||
if (newCookieToken != null)
|
if (newCookieToken != null)
|
||||||
{
|
{
|
||||||
cookieToken = newCookieToken;
|
cookieToken = newCookieToken;
|
||||||
}
|
}
|
||||||
var requestToken = _tokenGenerator.GenerateRequestToken(
|
var requestToken = _tokenGenerator.GenerateRequestToken(
|
||||||
context,
|
httpContext,
|
||||||
cookieToken);
|
cookieToken);
|
||||||
|
|
||||||
return new AntiforgeryTokenSetInternal()
|
return new AntiforgeryTokenSetInternal()
|
||||||
|
|
|
||||||
|
|
@ -2,7 +2,6 @@
|
||||||
// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
|
// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
|
||||||
|
|
||||||
using System;
|
using System;
|
||||||
using System.Diagnostics;
|
|
||||||
using System.Security.Claims;
|
using System.Security.Claims;
|
||||||
using System.Security.Principal;
|
using System.Security.Principal;
|
||||||
using Microsoft.AspNetCore.Http;
|
using Microsoft.AspNetCore.Http;
|
||||||
|
|
@ -22,6 +21,7 @@ namespace Microsoft.AspNetCore.Antiforgery
|
||||||
_additionalDataProvider = additionalDataProvider;
|
_additionalDataProvider = additionalDataProvider;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// <inheritdoc />
|
||||||
public AntiforgeryToken GenerateCookieToken()
|
public AntiforgeryToken GenerateCookieToken()
|
||||||
{
|
{
|
||||||
return new AntiforgeryToken()
|
return new AntiforgeryToken()
|
||||||
|
|
@ -31,6 +31,7 @@ namespace Microsoft.AspNetCore.Antiforgery
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// <inheritdoc />
|
||||||
public AntiforgeryToken GenerateRequestToken(
|
public AntiforgeryToken GenerateRequestToken(
|
||||||
HttpContext httpContext,
|
HttpContext httpContext,
|
||||||
AntiforgeryToken cookieToken)
|
AntiforgeryToken cookieToken)
|
||||||
|
|
@ -40,7 +41,17 @@ namespace Microsoft.AspNetCore.Antiforgery
|
||||||
throw new ArgumentNullException(nameof(httpContext));
|
throw new ArgumentNullException(nameof(httpContext));
|
||||||
}
|
}
|
||||||
|
|
||||||
Debug.Assert(IsCookieTokenValid(cookieToken));
|
if (cookieToken == null)
|
||||||
|
{
|
||||||
|
throw new ArgumentNullException(nameof(cookieToken));
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!IsCookieTokenValid(cookieToken))
|
||||||
|
{
|
||||||
|
throw new ArgumentException(
|
||||||
|
Resources.Antiforgery_CookieToken_IsInvalid,
|
||||||
|
nameof(cookieToken));
|
||||||
|
}
|
||||||
|
|
||||||
var requestToken = new AntiforgeryToken()
|
var requestToken = new AntiforgeryToken()
|
||||||
{
|
{
|
||||||
|
|
@ -87,15 +98,18 @@ namespace Microsoft.AspNetCore.Antiforgery
|
||||||
return requestToken;
|
return requestToken;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// <inheritdoc />
|
||||||
public bool IsCookieTokenValid(AntiforgeryToken cookieToken)
|
public bool IsCookieTokenValid(AntiforgeryToken cookieToken)
|
||||||
{
|
{
|
||||||
return (cookieToken != null && cookieToken.IsCookieToken);
|
return cookieToken != null && cookieToken.IsCookieToken;
|
||||||
}
|
}
|
||||||
|
|
||||||
public void ValidateTokens(
|
/// <inheritdoc />
|
||||||
|
public bool TryValidateTokenSet(
|
||||||
HttpContext httpContext,
|
HttpContext httpContext,
|
||||||
AntiforgeryToken cookieToken,
|
AntiforgeryToken cookieToken,
|
||||||
AntiforgeryToken requestToken)
|
AntiforgeryToken requestToken,
|
||||||
|
out string message)
|
||||||
{
|
{
|
||||||
if (httpContext == null)
|
if (httpContext == null)
|
||||||
{
|
{
|
||||||
|
|
@ -119,13 +133,15 @@ namespace Microsoft.AspNetCore.Antiforgery
|
||||||
// Do the tokens have the correct format?
|
// Do the tokens have the correct format?
|
||||||
if (!cookieToken.IsCookieToken || requestToken.IsCookieToken)
|
if (!cookieToken.IsCookieToken || requestToken.IsCookieToken)
|
||||||
{
|
{
|
||||||
throw new AntiforgeryValidationException(Resources.AntiforgeryToken_TokensSwapped);
|
message = Resources.AntiforgeryToken_TokensSwapped;
|
||||||
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Are the security tokens embedded in each incoming token identical?
|
// Are the security tokens embedded in each incoming token identical?
|
||||||
if (!object.Equals(cookieToken.SecurityToken, requestToken.SecurityToken))
|
if (!object.Equals(cookieToken.SecurityToken, requestToken.SecurityToken))
|
||||||
{
|
{
|
||||||
throw new AntiforgeryValidationException(Resources.AntiforgeryToken_SecurityTokenMismatch);
|
message = Resources.AntiforgeryToken_SecurityTokenMismatch;
|
||||||
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Is the incoming token meant for the current user?
|
// Is the incoming token meant for the current user?
|
||||||
|
|
@ -153,21 +169,26 @@ namespace Microsoft.AspNetCore.Antiforgery
|
||||||
|
|
||||||
if (!comparer.Equals(requestToken.Username, currentUsername))
|
if (!comparer.Equals(requestToken.Username, currentUsername))
|
||||||
{
|
{
|
||||||
throw new AntiforgeryValidationException(
|
message = Resources.FormatAntiforgeryToken_UsernameMismatch(requestToken.Username, currentUsername);
|
||||||
Resources.FormatAntiforgeryToken_UsernameMismatch(requestToken.Username, currentUsername));
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!object.Equals(requestToken.ClaimUid, currentClaimUid))
|
if (!object.Equals(requestToken.ClaimUid, currentClaimUid))
|
||||||
{
|
{
|
||||||
throw new AntiforgeryValidationException(Resources.AntiforgeryToken_ClaimUidMismatch);
|
message = Resources.AntiforgeryToken_ClaimUidMismatch;
|
||||||
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Is the AdditionalData valid?
|
// Is the AdditionalData valid?
|
||||||
if (_additionalDataProvider != null &&
|
if (_additionalDataProvider != null &&
|
||||||
!_additionalDataProvider.ValidateAdditionalData(httpContext, requestToken.AdditionalData))
|
!_additionalDataProvider.ValidateAdditionalData(httpContext, requestToken.AdditionalData))
|
||||||
{
|
{
|
||||||
throw new AntiforgeryValidationException(Resources.AntiforgeryToken_AdditionalDataCheckFailed);
|
message = Resources.AntiforgeryToken_AdditionalDataCheckFailed;
|
||||||
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
message = null;
|
||||||
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
private static BinaryBlob GetClaimUidBlob(string base64ClaimUid)
|
private static BinaryBlob GetClaimUidBlob(string base64ClaimUid)
|
||||||
|
|
|
||||||
|
|
@ -16,7 +16,7 @@ namespace Microsoft.AspNetCore.Antiforgery
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Generates an <input type="hidden"> element for an antiforgery token.
|
/// Generates an <input type="hidden"> element for an antiforgery token.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
/// <param name="context">The <see cref="HttpContext"/> associated with the current request.</param>
|
/// <param name="httpContext">The <see cref="HttpContext"/> associated with the current request.</param>
|
||||||
/// <returns>
|
/// <returns>
|
||||||
/// A <see cref="IHtmlContent"/> containing an <input type="hidden"> element. This element should be put
|
/// A <see cref="IHtmlContent"/> containing an <input type="hidden"> element. This element should be put
|
||||||
/// inside a <form>.
|
/// inside a <form>.
|
||||||
|
|
@ -25,50 +25,63 @@ namespace Microsoft.AspNetCore.Antiforgery
|
||||||
/// This method has a side effect:
|
/// This method has a side effect:
|
||||||
/// A response cookie is set if there is no valid cookie associated with the request.
|
/// A response cookie is set if there is no valid cookie associated with the request.
|
||||||
/// </remarks>
|
/// </remarks>
|
||||||
IHtmlContent GetHtml(HttpContext context);
|
IHtmlContent GetHtml(HttpContext httpContext);
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Generates an <see cref="AntiforgeryTokenSet"/> for this request and stores the cookie token
|
/// Generates an <see cref="AntiforgeryTokenSet"/> for this request and stores the cookie token
|
||||||
/// in the response.
|
/// in the response.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
/// <param name="context">The <see cref="HttpContext"/> associated with the current request.</param>
|
/// <param name="httpContext">The <see cref="HttpContext"/> associated with the current request.</param>
|
||||||
/// <returns>An <see cref="AntiforgeryTokenSet" /> with tokens for the response.</returns>
|
/// <returns>An <see cref="AntiforgeryTokenSet" /> with tokens for the response.</returns>
|
||||||
/// <remarks>
|
/// <remarks>
|
||||||
/// This method has a side effect:
|
/// This method has a side effect:
|
||||||
/// A response cookie is set if there is no valid cookie associated with the request.
|
/// A response cookie is set if there is no valid cookie associated with the request.
|
||||||
/// </remarks>
|
/// </remarks>
|
||||||
AntiforgeryTokenSet GetAndStoreTokens(HttpContext context);
|
AntiforgeryTokenSet GetAndStoreTokens(HttpContext httpContext);
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Generates an <see cref="AntiforgeryTokenSet"/> for this request.
|
/// Generates an <see cref="AntiforgeryTokenSet"/> for this request.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
/// <param name="context">The <see cref="HttpContext"/> associated with the current request.</param>
|
/// <param name="httpContext">The <see cref="HttpContext"/> associated with the current request.</param>
|
||||||
/// <remarks>
|
/// <remarks>
|
||||||
/// Unlike <see cref="GetAndStoreTokens(HttpContext)"/>, this method has no side effect. The caller
|
/// Unlike <see cref="GetAndStoreTokens(HttpContext)"/>, this method has no side effect. The caller
|
||||||
/// is responsible for setting the response cookie and injecting the returned
|
/// is responsible for setting the response cookie and injecting the returned
|
||||||
/// form token as appropriate.
|
/// form token as appropriate.
|
||||||
/// </remarks>
|
/// </remarks>
|
||||||
AntiforgeryTokenSet GetTokens(HttpContext context);
|
AntiforgeryTokenSet GetTokens(HttpContext httpContext);
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Asynchronously returns a value indicating whether the request contains a valid antiforgery token.
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="httpContext">The <see cref="HttpContext"/> associated with the current request.</param>
|
||||||
|
/// <returns>
|
||||||
|
/// A <see cref="Task{bool}"/> that, when completed, returns <c>true</c> if the request contains a
|
||||||
|
/// valid antiforgery token, otherwise returns <c>false</c>.
|
||||||
|
/// </returns>
|
||||||
|
Task<bool> IsRequestValidAsync(HttpContext httpContext);
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Validates an antiforgery token that was supplied as part of the request.
|
/// Validates an antiforgery token that was supplied as part of the request.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
/// <param name="context">The <see cref="HttpContext"/> associated with the current request.</param>
|
/// <param name="httpContext">The <see cref="HttpContext"/> associated with the current request.</param>
|
||||||
Task ValidateRequestAsync(HttpContext context);
|
/// <exception cref="AntiforgeryValidationException">
|
||||||
|
/// Thrown when the request does not include a valid antiforgery token.
|
||||||
|
/// </exception>
|
||||||
|
Task ValidateRequestAsync(HttpContext httpContext);
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Validates an <see cref="AntiforgeryTokenSet"/> for the current request.
|
/// Validates an <see cref="AntiforgeryTokenSet"/> for the current request.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
/// <param name="context">The <see cref="HttpContext"/> associated with the current request.</param>
|
/// <param name="httpContext">The <see cref="HttpContext"/> associated with the current request.</param>
|
||||||
/// <param name="antiforgeryTokenSet">
|
/// <param name="antiforgeryTokenSet">
|
||||||
/// The <see cref="AntiforgeryTokenSet"/> (cookie and request token) for this request.
|
/// The <see cref="AntiforgeryTokenSet"/> (cookie and request token) for this request.
|
||||||
/// </param>
|
/// </param>
|
||||||
void ValidateTokens(HttpContext context, AntiforgeryTokenSet antiforgeryTokenSet);
|
void ValidateTokens(HttpContext httpContext, AntiforgeryTokenSet antiforgeryTokenSet);
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Generates and stores an antiforgery cookie token if one is not available or not valid.
|
/// Generates and stores an antiforgery cookie token if one is not available or not valid.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
/// <param name="context">The <see cref="HttpContext"/> associated with the current request.</param>
|
/// <param name="httpContext">The <see cref="HttpContext"/> associated with the current request.</param>
|
||||||
void SetCookieTokenAndHeader(HttpContext context);
|
void SetCookieTokenAndHeader(HttpContext httpContext);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -10,23 +10,41 @@ namespace Microsoft.AspNetCore.Antiforgery
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public interface IAntiforgeryTokenGenerator
|
public interface IAntiforgeryTokenGenerator
|
||||||
{
|
{
|
||||||
// Generates a new random cookie token.
|
/// <summary>
|
||||||
|
/// Generates a new random cookie token.
|
||||||
|
/// </summary>
|
||||||
|
/// <returns>An <see cref="AntiforgeryToken"/>.</returns>
|
||||||
AntiforgeryToken GenerateCookieToken();
|
AntiforgeryToken GenerateCookieToken();
|
||||||
|
|
||||||
// Given a cookie token, generates a corresponding request token.
|
/// <summary>
|
||||||
// The incoming cookie token must be valid.
|
/// Generates a request token corresponding to <paramref name="cookieToken"/>.
|
||||||
AntiforgeryToken GenerateRequestToken(
|
/// </summary>
|
||||||
HttpContext httpContext,
|
/// <param name="httpContext">The <see cref="HttpContext"/> associated with the current request.</param>
|
||||||
AntiforgeryToken cookieToken);
|
/// <param name="cookieToken">A valid cookie token.</param>
|
||||||
|
/// <returns>An <see cref="AntiforgeryToken"/>.</returns>
|
||||||
|
AntiforgeryToken GenerateRequestToken(HttpContext httpContext, AntiforgeryToken cookieToken);
|
||||||
|
|
||||||
// Determines whether an existing cookie token is valid (well-formed).
|
/// <summary>
|
||||||
// If it is not, the caller must call GenerateCookieToken() before calling GenerateFormToken().
|
/// Attempts to validate a cookie token.
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="cookieToken">A valid cookie token.</param>
|
||||||
|
/// <returns><c>true</c> if the cookie token is valid, otherwise <c>false</c>.</returns>
|
||||||
bool IsCookieTokenValid(AntiforgeryToken cookieToken);
|
bool IsCookieTokenValid(AntiforgeryToken cookieToken);
|
||||||
|
|
||||||
// Validates a (cookie, request) token pair.
|
/// <summary>
|
||||||
void ValidateTokens(
|
/// Attempts to validate a cookie and request token set for the given <paramref name="httpContext"/>.
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="httpContext">The <see cref="HttpContext"/> associated with the current request.</param>
|
||||||
|
/// <param name="cookieToken">A cookie token.</param>
|
||||||
|
/// <param name="requestToken">A request token.</param>
|
||||||
|
/// <param name="message">
|
||||||
|
/// Will be set to the validation message if the tokens are invalid, otherwise <c>null</c>.
|
||||||
|
/// </param>
|
||||||
|
/// <returns><c>true</c> if the tokens are valid, otherwise <c>false</c>.</returns>
|
||||||
|
bool TryValidateTokenSet(
|
||||||
HttpContext httpContext,
|
HttpContext httpContext,
|
||||||
AntiforgeryToken cookieToken,
|
AntiforgeryToken cookieToken,
|
||||||
AntiforgeryToken requestToken);
|
AntiforgeryToken requestToken,
|
||||||
|
out string message);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -138,6 +138,14 @@ namespace Microsoft.AspNetCore.Antiforgery
|
||||||
return string.Format(CultureInfo.CurrentCulture, GetString("AntiforgeryWorker_RequireSSL"), p0, p1, p2);
|
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_IsInvalid
|
||||||
|
{
|
||||||
|
get { return GetString("Antiforgery_CookieToken_IsInvalid"); }
|
||||||
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// The required antiforgery cookie "{0}" is not present.
|
/// The required antiforgery cookie "{0}" is not present.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
|
|
|
||||||
|
|
@ -143,6 +143,9 @@
|
||||||
<value>The antiforgery system has the configuration value {0}.{1} = {2}, but the current request is not an SSL request.</value>
|
<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>
|
<comment>0 = nameof(AntiforgeryOptions), 1 = nameof(RequireSsl), 2 = bool.TrueString</comment>
|
||||||
</data>
|
</data>
|
||||||
|
<data name="Antiforgery_CookieToken_IsInvalid" xml:space="preserve">
|
||||||
|
<value>The antiforgery cookie token is invalid.</value>
|
||||||
|
</data>
|
||||||
<data name="Antiforgery_CookieToken_MustBeProvided" xml:space="preserve">
|
<data name="Antiforgery_CookieToken_MustBeProvided" xml:space="preserve">
|
||||||
<value>The required antiforgery cookie "{0}" is not present.</value>
|
<value>The required antiforgery cookie "{0}" is not present.</value>
|
||||||
</data>
|
</data>
|
||||||
|
|
|
||||||
|
|
@ -8,6 +8,7 @@ using System.Threading.Tasks;
|
||||||
using Microsoft.AspNetCore.Html;
|
using Microsoft.AspNetCore.Html;
|
||||||
using Microsoft.AspNetCore.Http;
|
using Microsoft.AspNetCore.Http;
|
||||||
using Microsoft.AspNetCore.Http.Internal;
|
using Microsoft.AspNetCore.Http.Internal;
|
||||||
|
using Microsoft.AspNetCore.Testing;
|
||||||
using Microsoft.Extensions.Options;
|
using Microsoft.Extensions.Options;
|
||||||
using Microsoft.Extensions.WebEncoders.Testing;
|
using Microsoft.Extensions.WebEncoders.Testing;
|
||||||
using Moq;
|
using Moq;
|
||||||
|
|
@ -34,9 +35,31 @@ namespace Microsoft.AspNetCore.Antiforgery
|
||||||
var exception = await Assert.ThrowsAsync<InvalidOperationException>(
|
var exception = await Assert.ThrowsAsync<InvalidOperationException>(
|
||||||
async () => await antiforgery.ValidateRequestAsync(httpContext));
|
async () => await antiforgery.ValidateRequestAsync(httpContext));
|
||||||
Assert.Equal(
|
Assert.Equal(
|
||||||
@"The antiforgery system has the configuration value AntiforgeryOptions.RequireSsl = true, " +
|
@"The antiforgery system has the configuration value AntiforgeryOptions.RequireSsl = true, " +
|
||||||
"but the current request is not an SSL request.",
|
"but the current request is not an SSL request.",
|
||||||
exception.Message);
|
exception.Message);
|
||||||
|
}
|
||||||
|
|
||||||
|
[Fact]
|
||||||
|
public async Task ChecksSSL_IsRequestValidAsync_Throws()
|
||||||
|
{
|
||||||
|
// Arrange
|
||||||
|
var httpContext = new DefaultHttpContext();
|
||||||
|
|
||||||
|
var options = new AntiforgeryOptions()
|
||||||
|
{
|
||||||
|
RequireSsl = true
|
||||||
|
};
|
||||||
|
|
||||||
|
var antiforgery = GetAntiforgery(options);
|
||||||
|
|
||||||
|
// Act & Assert
|
||||||
|
var exception = await Assert.ThrowsAsync<InvalidOperationException>(
|
||||||
|
async () => await antiforgery.IsRequestValidAsync(httpContext));
|
||||||
|
Assert.Equal(
|
||||||
|
@"The antiforgery system has the configuration value AntiforgeryOptions.RequireSsl = true, " +
|
||||||
|
"but the current request is not an SSL request.",
|
||||||
|
exception.Message);
|
||||||
}
|
}
|
||||||
|
|
||||||
[Fact]
|
[Fact]
|
||||||
|
|
@ -56,9 +79,9 @@ namespace Microsoft.AspNetCore.Antiforgery
|
||||||
var exception = Assert.Throws<InvalidOperationException>(
|
var exception = Assert.Throws<InvalidOperationException>(
|
||||||
() => antiforgery.ValidateTokens(httpContext, new AntiforgeryTokenSet("hello", "world")));
|
() => antiforgery.ValidateTokens(httpContext, new AntiforgeryTokenSet("hello", "world")));
|
||||||
Assert.Equal(
|
Assert.Equal(
|
||||||
@"The antiforgery system has the configuration value AntiforgeryOptions.RequireSsl = true, " +
|
@"The antiforgery system has the configuration value AntiforgeryOptions.RequireSsl = true, " +
|
||||||
"but the current request is not an SSL request.",
|
"but the current request is not an SSL request.",
|
||||||
exception.Message);
|
exception.Message);
|
||||||
}
|
}
|
||||||
|
|
||||||
[Fact]
|
[Fact]
|
||||||
|
|
@ -162,6 +185,11 @@ namespace Microsoft.AspNetCore.Antiforgery
|
||||||
var antiforgery = GetAntiforgery(context);
|
var antiforgery = GetAntiforgery(context);
|
||||||
var encoder = new HtmlTestEncoder();
|
var encoder = new HtmlTestEncoder();
|
||||||
|
|
||||||
|
// Setup so that the null cookie token returned is treated as invalid.
|
||||||
|
context.TokenGenerator
|
||||||
|
.Setup(o => o.IsCookieTokenValid(null))
|
||||||
|
.Returns(false);
|
||||||
|
|
||||||
// Act
|
// Act
|
||||||
var inputElement = antiforgery.GetHtml(context.HttpContext);
|
var inputElement = antiforgery.GetHtml(context.HttpContext);
|
||||||
|
|
||||||
|
|
@ -303,12 +331,10 @@ namespace Microsoft.AspNetCore.Antiforgery
|
||||||
isOldCookieValid: false);
|
isOldCookieValid: false);
|
||||||
|
|
||||||
// This will cause the cookieToken to be null.
|
// This will cause the cookieToken to be null.
|
||||||
context.TokenSerializer.Setup(o => o.Deserialize("serialized-old-cookie-token"))
|
context.TokenSerializer
|
||||||
.Throws(new Exception("should be swallowed"));
|
.Setup(o => o.Deserialize("serialized-old-cookie-token"))
|
||||||
|
.Throws(new Exception("should be swallowed"));
|
||||||
|
|
||||||
// Setup so that the null cookie token returned is treated as invalid.
|
|
||||||
context.TokenGenerator.Setup(o => o.IsCookieTokenValid(null))
|
|
||||||
.Returns(false);
|
|
||||||
var antiforgery = GetAntiforgery(context);
|
var antiforgery = GetAntiforgery(context);
|
||||||
|
|
||||||
// Act
|
// Act
|
||||||
|
|
@ -381,7 +407,7 @@ namespace Microsoft.AspNetCore.Antiforgery
|
||||||
}
|
}
|
||||||
|
|
||||||
[Fact]
|
[Fact]
|
||||||
public void ValidateTokens_FromInvalidStrings_Throws()
|
public void ValidateTokens_InvalidTokens_Throws()
|
||||||
{
|
{
|
||||||
// Arrange
|
// Arrange
|
||||||
var context = CreateMockContext(new AntiforgeryOptions());
|
var context = CreateMockContext(new AntiforgeryOptions());
|
||||||
|
|
@ -393,17 +419,20 @@ namespace Microsoft.AspNetCore.Antiforgery
|
||||||
.Setup(o => o.Deserialize("form-token"))
|
.Setup(o => o.Deserialize("form-token"))
|
||||||
.Returns(context.TestTokenSet.RequestToken);
|
.Returns(context.TestTokenSet.RequestToken);
|
||||||
|
|
||||||
context.TokenGenerator
|
// You can't really do Moq with out-parameters :(
|
||||||
.Setup(o => o.ValidateTokens(
|
var tokenGenerator = new TestTokenGenerator()
|
||||||
context.HttpContext,
|
{
|
||||||
context.TestTokenSet.OldCookieToken,
|
Message = "my-message",
|
||||||
context.TestTokenSet.RequestToken))
|
};
|
||||||
.Throws(new InvalidOperationException("my-message"));
|
|
||||||
context.TokenStore = null;
|
|
||||||
var antiforgery = GetAntiforgery(context);
|
|
||||||
|
|
||||||
// Act & assert
|
var antiforgery = new DefaultAntiforgery(
|
||||||
var exception = Assert.Throws<InvalidOperationException>(
|
new TestOptionsManager(),
|
||||||
|
tokenGenerator,
|
||||||
|
context.TokenSerializer.Object,
|
||||||
|
tokenStore: null);
|
||||||
|
|
||||||
|
// Act & Assert
|
||||||
|
var exception = Assert.Throws<AntiforgeryValidationException>(
|
||||||
() => antiforgery.ValidateTokens(
|
() => antiforgery.ValidateTokens(
|
||||||
context.HttpContext,
|
context.HttpContext,
|
||||||
new AntiforgeryTokenSet("form-token", "cookie-token")));
|
new AntiforgeryTokenSet("form-token", "cookie-token")));
|
||||||
|
|
@ -423,11 +452,14 @@ namespace Microsoft.AspNetCore.Antiforgery
|
||||||
.Setup(o => o.Deserialize("form-token"))
|
.Setup(o => o.Deserialize("form-token"))
|
||||||
.Returns(context.TestTokenSet.RequestToken);
|
.Returns(context.TestTokenSet.RequestToken);
|
||||||
|
|
||||||
|
string message;
|
||||||
context.TokenGenerator
|
context.TokenGenerator
|
||||||
.Setup(o => o.ValidateTokens(
|
.Setup(o => o.TryValidateTokenSet(
|
||||||
context.HttpContext,
|
context.HttpContext,
|
||||||
context.TestTokenSet.OldCookieToken,
|
context.TestTokenSet.OldCookieToken,
|
||||||
context.TestTokenSet.RequestToken))
|
context.TestTokenSet.RequestToken,
|
||||||
|
out message))
|
||||||
|
.Returns(true)
|
||||||
.Verifiable();
|
.Verifiable();
|
||||||
context.TokenStore = null;
|
context.TokenStore = null;
|
||||||
var antiforgery = GetAntiforgery(context);
|
var antiforgery = GetAntiforgery(context);
|
||||||
|
|
@ -450,12 +482,60 @@ namespace Microsoft.AspNetCore.Antiforgery
|
||||||
|
|
||||||
|
|
||||||
// Act
|
// Act
|
||||||
var exception = Assert.Throws<ArgumentException>(
|
ExceptionAssert.ThrowsArgument(
|
||||||
() => antiforgery.ValidateTokens(context.HttpContext, tokenSet));
|
() => antiforgery.ValidateTokens(context.HttpContext, tokenSet),
|
||||||
|
"antiforgeryTokenSet",
|
||||||
|
"The required antiforgery cookie token must be provided.");
|
||||||
|
}
|
||||||
|
|
||||||
|
[Fact]
|
||||||
|
public async Task IsRequestValueAsync_FromStore_Failure()
|
||||||
|
{
|
||||||
|
// Arrange
|
||||||
|
var context = CreateMockContext(new AntiforgeryOptions());
|
||||||
|
|
||||||
|
string message;
|
||||||
|
context.TokenGenerator
|
||||||
|
.Setup(o => o.TryValidateTokenSet(
|
||||||
|
context.HttpContext,
|
||||||
|
context.TestTokenSet.OldCookieToken,
|
||||||
|
context.TestTokenSet.RequestToken,
|
||||||
|
out message))
|
||||||
|
.Returns(false);
|
||||||
|
|
||||||
|
var antiforgery = GetAntiforgery(context);
|
||||||
|
|
||||||
|
// Act
|
||||||
|
var result = await antiforgery.IsRequestValidAsync(context.HttpContext);
|
||||||
|
|
||||||
// Assert
|
// Assert
|
||||||
var trimmed = exception.Message.Substring(0, exception.Message.IndexOf(Environment.NewLine));
|
Assert.False(result);
|
||||||
Assert.Equal("The required antiforgery cookie token must be provided.", trimmed);
|
}
|
||||||
|
|
||||||
|
[Fact]
|
||||||
|
public async Task IsRequestValidAsync_FromStore_Success()
|
||||||
|
{
|
||||||
|
// Arrange
|
||||||
|
var context = CreateMockContext(new AntiforgeryOptions());
|
||||||
|
|
||||||
|
string message;
|
||||||
|
context.TokenGenerator
|
||||||
|
.Setup(o => o.TryValidateTokenSet(
|
||||||
|
context.HttpContext,
|
||||||
|
context.TestTokenSet.OldCookieToken,
|
||||||
|
context.TestTokenSet.RequestToken,
|
||||||
|
out message))
|
||||||
|
.Returns(true)
|
||||||
|
.Verifiable();
|
||||||
|
|
||||||
|
var antiforgery = GetAntiforgery(context);
|
||||||
|
|
||||||
|
// Act
|
||||||
|
var result = await antiforgery.IsRequestValidAsync(context.HttpContext);
|
||||||
|
|
||||||
|
// Assert
|
||||||
|
Assert.True(result);
|
||||||
|
context.TokenGenerator.Verify();
|
||||||
}
|
}
|
||||||
|
|
||||||
[Fact]
|
[Fact]
|
||||||
|
|
@ -464,17 +544,20 @@ namespace Microsoft.AspNetCore.Antiforgery
|
||||||
// Arrange
|
// Arrange
|
||||||
var context = CreateMockContext(new AntiforgeryOptions());
|
var context = CreateMockContext(new AntiforgeryOptions());
|
||||||
|
|
||||||
context.TokenGenerator
|
// You can't really do Moq with out-parameters :(
|
||||||
.Setup(o => o.ValidateTokens(
|
var tokenGenerator = new TestTokenGenerator()
|
||||||
context.HttpContext,
|
{
|
||||||
context.TestTokenSet.OldCookieToken,
|
Message = "my-message",
|
||||||
context.TestTokenSet.RequestToken))
|
};
|
||||||
.Throws(new InvalidOperationException("my-message"));
|
|
||||||
|
|
||||||
var antiforgery = GetAntiforgery(context);
|
var antiforgery = new DefaultAntiforgery(
|
||||||
|
new TestOptionsManager(),
|
||||||
|
tokenGenerator,
|
||||||
|
context.TokenSerializer.Object,
|
||||||
|
context.TokenStore.Object);
|
||||||
|
|
||||||
// Act & assert
|
// Act & assert
|
||||||
var exception = await Assert.ThrowsAsync<InvalidOperationException>(
|
var exception = await Assert.ThrowsAsync<AntiforgeryValidationException>(
|
||||||
async () => await antiforgery.ValidateRequestAsync(context.HttpContext));
|
async () => await antiforgery.ValidateRequestAsync(context.HttpContext));
|
||||||
Assert.Equal("my-message", exception.Message);
|
Assert.Equal("my-message", exception.Message);
|
||||||
}
|
}
|
||||||
|
|
@ -485,11 +568,14 @@ namespace Microsoft.AspNetCore.Antiforgery
|
||||||
// Arrange
|
// Arrange
|
||||||
var context = CreateMockContext(new AntiforgeryOptions());
|
var context = CreateMockContext(new AntiforgeryOptions());
|
||||||
|
|
||||||
|
string message;
|
||||||
context.TokenGenerator
|
context.TokenGenerator
|
||||||
.Setup(o => o.ValidateTokens(
|
.Setup(o => o.TryValidateTokenSet(
|
||||||
context.HttpContext,
|
context.HttpContext,
|
||||||
context.TestTokenSet.OldCookieToken,
|
context.TestTokenSet.OldCookieToken,
|
||||||
context.TestTokenSet.RequestToken))
|
context.TestTokenSet.RequestToken,
|
||||||
|
out message))
|
||||||
|
.Returns(true)
|
||||||
.Verifiable();
|
.Verifiable();
|
||||||
|
|
||||||
var antiforgery = GetAntiforgery(context);
|
var antiforgery = GetAntiforgery(context);
|
||||||
|
|
@ -696,5 +782,35 @@ namespace Microsoft.AspNetCore.Antiforgery
|
||||||
{
|
{
|
||||||
public AntiforgeryOptions Value { get; set; } = new AntiforgeryOptions();
|
public AntiforgeryOptions Value { get; set; } = new AntiforgeryOptions();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private class TestTokenGenerator : IAntiforgeryTokenGenerator
|
||||||
|
{
|
||||||
|
public string Message { get; set; }
|
||||||
|
|
||||||
|
public AntiforgeryToken GenerateCookieToken()
|
||||||
|
{
|
||||||
|
throw new NotImplementedException();
|
||||||
|
}
|
||||||
|
|
||||||
|
public AntiforgeryToken GenerateRequestToken(HttpContext httpContext, AntiforgeryToken cookieToken)
|
||||||
|
{
|
||||||
|
throw new NotImplementedException();
|
||||||
|
}
|
||||||
|
|
||||||
|
public bool IsCookieTokenValid(AntiforgeryToken cookieToken)
|
||||||
|
{
|
||||||
|
throw new NotImplementedException();
|
||||||
|
}
|
||||||
|
|
||||||
|
public bool TryValidateTokenSet(
|
||||||
|
HttpContext httpContext,
|
||||||
|
AntiforgeryToken cookieToken,
|
||||||
|
AntiforgeryToken requestToken,
|
||||||
|
out string message)
|
||||||
|
{
|
||||||
|
message = Message;
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -5,6 +5,7 @@ using System;
|
||||||
using System.Security.Claims;
|
using System.Security.Claims;
|
||||||
using System.Security.Cryptography;
|
using System.Security.Cryptography;
|
||||||
using Microsoft.AspNetCore.Http.Internal;
|
using Microsoft.AspNetCore.Http.Internal;
|
||||||
|
using Microsoft.AspNetCore.Testing;
|
||||||
using Moq;
|
using Moq;
|
||||||
using Xunit;
|
using Xunit;
|
||||||
|
|
||||||
|
|
@ -28,7 +29,27 @@ namespace Microsoft.AspNetCore.Antiforgery
|
||||||
}
|
}
|
||||||
|
|
||||||
[Fact]
|
[Fact]
|
||||||
public void GenerateFormToken_AnonymousUser()
|
public void GenerateRequestToken_InvalidCookieToken()
|
||||||
|
{
|
||||||
|
// Arrange
|
||||||
|
var cookieToken = new AntiforgeryToken() { IsCookieToken = false };
|
||||||
|
var httpContext = new DefaultHttpContext();
|
||||||
|
httpContext.User = new ClaimsPrincipal(new ClaimsIdentity());
|
||||||
|
Assert.False(httpContext.User.Identity.IsAuthenticated);
|
||||||
|
|
||||||
|
var tokenProvider = new DefaultAntiforgeryTokenGenerator(
|
||||||
|
claimUidExtractor: null,
|
||||||
|
additionalDataProvider: null);
|
||||||
|
|
||||||
|
// Act & Assert
|
||||||
|
ExceptionAssert.ThrowsArgument(
|
||||||
|
() => tokenProvider.GenerateRequestToken(httpContext, cookieToken),
|
||||||
|
"cookieToken",
|
||||||
|
"The antiforgery cookie token is invalid.");
|
||||||
|
}
|
||||||
|
|
||||||
|
[Fact]
|
||||||
|
public void GenerateRequestToken_AnonymousUser()
|
||||||
{
|
{
|
||||||
// Arrange
|
// Arrange
|
||||||
var cookieToken = new AntiforgeryToken() { IsCookieToken = true };
|
var cookieToken = new AntiforgeryToken() { IsCookieToken = true };
|
||||||
|
|
@ -53,7 +74,7 @@ namespace Microsoft.AspNetCore.Antiforgery
|
||||||
}
|
}
|
||||||
|
|
||||||
[Fact]
|
[Fact]
|
||||||
public void GenerateFormToken_AuthenticatedWithoutUsernameAndNoAdditionalData_NoAdditionalData()
|
public void GenerateRequestToken_AuthenticatedWithoutUsernameAndNoAdditionalData_NoAdditionalData()
|
||||||
{
|
{
|
||||||
// Arrange
|
// Arrange
|
||||||
var cookieToken = new AntiforgeryToken()
|
var cookieToken = new AntiforgeryToken()
|
||||||
|
|
@ -87,7 +108,7 @@ namespace Microsoft.AspNetCore.Antiforgery
|
||||||
}
|
}
|
||||||
|
|
||||||
[Fact]
|
[Fact]
|
||||||
public void GenerateFormToken_AuthenticatedWithoutUsername_WithAdditionalData()
|
public void GenerateRequestToken_AuthenticatedWithoutUsername_WithAdditionalData()
|
||||||
{
|
{
|
||||||
// Arrange
|
// Arrange
|
||||||
var cookieToken = new AntiforgeryToken() { IsCookieToken = true };
|
var cookieToken = new AntiforgeryToken() { IsCookieToken = true };
|
||||||
|
|
@ -118,7 +139,7 @@ namespace Microsoft.AspNetCore.Antiforgery
|
||||||
}
|
}
|
||||||
|
|
||||||
[Fact]
|
[Fact]
|
||||||
public void GenerateFormToken_ClaimsBasedIdentity()
|
public void GenerateRequestToken_ClaimsBasedIdentity()
|
||||||
{
|
{
|
||||||
// Arrange
|
// Arrange
|
||||||
var cookieToken = new AntiforgeryToken() { IsCookieToken = true };
|
var cookieToken = new AntiforgeryToken() { IsCookieToken = true };
|
||||||
|
|
@ -156,7 +177,7 @@ namespace Microsoft.AspNetCore.Antiforgery
|
||||||
}
|
}
|
||||||
|
|
||||||
[Fact]
|
[Fact]
|
||||||
public void GenerateFormToken_RegularUserWithUsername()
|
public void GenerateRequestToken_RegularUserWithUsername()
|
||||||
{
|
{
|
||||||
// Arrange
|
// Arrange
|
||||||
var cookieToken = new AntiforgeryToken() { IsCookieToken = true };
|
var cookieToken = new AntiforgeryToken() { IsCookieToken = true };
|
||||||
|
|
@ -202,10 +223,10 @@ namespace Microsoft.AspNetCore.Antiforgery
|
||||||
additionalDataProvider: null);
|
additionalDataProvider: null);
|
||||||
|
|
||||||
// Act
|
// Act
|
||||||
bool retVal = tokenProvider.IsCookieTokenValid(cookieToken);
|
var isValid = tokenProvider.IsCookieTokenValid(cookieToken);
|
||||||
|
|
||||||
// Assert
|
// Assert
|
||||||
Assert.False(retVal);
|
Assert.False(isValid);
|
||||||
}
|
}
|
||||||
|
|
||||||
[Fact]
|
[Fact]
|
||||||
|
|
@ -246,7 +267,7 @@ namespace Microsoft.AspNetCore.Antiforgery
|
||||||
|
|
||||||
|
|
||||||
[Fact]
|
[Fact]
|
||||||
public void ValidateTokens_CookieTokenMissing()
|
public void TryValidateTokenSet_CookieTokenMissing()
|
||||||
{
|
{
|
||||||
// Arrange
|
// Arrange
|
||||||
var httpContext = new DefaultHttpContext();
|
var httpContext = new DefaultHttpContext();
|
||||||
|
|
@ -254,98 +275,126 @@ namespace Microsoft.AspNetCore.Antiforgery
|
||||||
|
|
||||||
var fieldtoken = new AntiforgeryToken() { IsCookieToken = false };
|
var fieldtoken = new AntiforgeryToken() { IsCookieToken = false };
|
||||||
|
|
||||||
var tokenProvider = new DefaultAntiforgeryTokenGenerator(
|
|
||||||
claimUidExtractor: null,
|
|
||||||
additionalDataProvider: null);
|
|
||||||
|
|
||||||
// Act & assert
|
|
||||||
var ex = Assert.Throws<ArgumentNullException>(
|
|
||||||
() => tokenProvider.ValidateTokens(httpContext, null, fieldtoken));
|
|
||||||
|
|
||||||
var trimmed = ex.Message.Substring(0, ex.Message.IndexOf(Environment.NewLine));
|
|
||||||
Assert.Equal(@"The required antiforgery cookie token must be provided.", trimmed);
|
|
||||||
}
|
|
||||||
|
|
||||||
[Fact]
|
|
||||||
public void ValidateTokens_FieldTokenMissing()
|
|
||||||
{
|
|
||||||
// Arrange
|
|
||||||
var httpContext = new DefaultHttpContext();
|
|
||||||
httpContext.User = new ClaimsPrincipal(new ClaimsIdentity());
|
|
||||||
|
|
||||||
var cookieToken = new AntiforgeryToken() { IsCookieToken = true };
|
|
||||||
|
|
||||||
var tokenProvider = new DefaultAntiforgeryTokenGenerator(
|
|
||||||
claimUidExtractor: null,
|
|
||||||
additionalDataProvider: null);
|
|
||||||
|
|
||||||
// Act & assert
|
|
||||||
var ex = Assert.Throws<ArgumentNullException>(
|
|
||||||
() => tokenProvider.ValidateTokens(httpContext, cookieToken, null));
|
|
||||||
|
|
||||||
var trimmed = ex.Message.Substring(0, ex.Message.IndexOf(Environment.NewLine));
|
|
||||||
Assert.Equal("The required antiforgery request token must be provided.", trimmed);
|
|
||||||
}
|
|
||||||
|
|
||||||
[Fact]
|
|
||||||
public void ValidateTokens_FieldAndCookieTokensSwapped()
|
|
||||||
{
|
|
||||||
// Arrange
|
|
||||||
var httpContext = new DefaultHttpContext();
|
|
||||||
httpContext.User = new ClaimsPrincipal(new ClaimsIdentity());
|
|
||||||
|
|
||||||
var cookieToken = new AntiforgeryToken() { IsCookieToken = true };
|
|
||||||
var fieldtoken = new AntiforgeryToken() { IsCookieToken = false };
|
|
||||||
|
|
||||||
var tokenProvider = new DefaultAntiforgeryTokenGenerator(
|
|
||||||
claimUidExtractor: null,
|
|
||||||
additionalDataProvider: null);
|
|
||||||
|
|
||||||
// Act & assert
|
|
||||||
var ex1 =
|
|
||||||
Assert.Throws<AntiforgeryValidationException>(
|
|
||||||
() => tokenProvider.ValidateTokens(httpContext, fieldtoken, fieldtoken));
|
|
||||||
Assert.Equal(
|
|
||||||
"Validation of the provided antiforgery token failed. " +
|
|
||||||
@"The cookie token and the request token were swapped.",
|
|
||||||
ex1.Message);
|
|
||||||
|
|
||||||
var ex2 =
|
|
||||||
Assert.Throws<AntiforgeryValidationException>(
|
|
||||||
() => tokenProvider.ValidateTokens(httpContext, cookieToken, cookieToken));
|
|
||||||
Assert.Equal(
|
|
||||||
"Validation of the provided antiforgery token failed. " +
|
|
||||||
@"The cookie token and the request token were swapped.",
|
|
||||||
ex2.Message);
|
|
||||||
}
|
|
||||||
|
|
||||||
[Fact]
|
|
||||||
public void ValidateTokens_FieldAndCookieTokensHaveDifferentSecurityKeys()
|
|
||||||
{
|
|
||||||
// Arrange
|
|
||||||
var httpContext = new DefaultHttpContext();
|
|
||||||
httpContext.User = new ClaimsPrincipal(new ClaimsIdentity());
|
|
||||||
|
|
||||||
var cookieToken = new AntiforgeryToken() { IsCookieToken = true };
|
|
||||||
var fieldtoken = new AntiforgeryToken() { IsCookieToken = false };
|
|
||||||
|
|
||||||
var tokenProvider = new DefaultAntiforgeryTokenGenerator(
|
var tokenProvider = new DefaultAntiforgeryTokenGenerator(
|
||||||
claimUidExtractor: null,
|
claimUidExtractor: null,
|
||||||
additionalDataProvider: null);
|
additionalDataProvider: null);
|
||||||
|
|
||||||
// Act & Assert
|
// Act & Assert
|
||||||
var exception = Assert.Throws<AntiforgeryValidationException>(
|
string message;
|
||||||
() => tokenProvider.ValidateTokens(httpContext, cookieToken, fieldtoken));
|
var ex = Assert.Throws<ArgumentNullException>(
|
||||||
Assert.Equal(
|
() => tokenProvider.TryValidateTokenSet(httpContext, null, fieldtoken, out message));
|
||||||
@"The antiforgery cookie token and request token do not match.",
|
|
||||||
exception.Message);
|
var trimmed = ex.Message.Substring(0, ex.Message.IndexOf(Environment.NewLine));
|
||||||
|
Assert.Equal(@"The required antiforgery cookie token must be provided.", trimmed);
|
||||||
|
}
|
||||||
|
|
||||||
|
[Fact]
|
||||||
|
public void TryValidateTokenSet_FieldTokenMissing()
|
||||||
|
{
|
||||||
|
// Arrange
|
||||||
|
var httpContext = new DefaultHttpContext();
|
||||||
|
httpContext.User = new ClaimsPrincipal(new ClaimsIdentity());
|
||||||
|
|
||||||
|
var cookieToken = new AntiforgeryToken() { IsCookieToken = true };
|
||||||
|
|
||||||
|
var tokenProvider = new DefaultAntiforgeryTokenGenerator(
|
||||||
|
claimUidExtractor: null,
|
||||||
|
additionalDataProvider: null);
|
||||||
|
|
||||||
|
|
||||||
|
// Act & Assert
|
||||||
|
string message;
|
||||||
|
var ex = Assert.Throws<ArgumentNullException>(
|
||||||
|
() => tokenProvider.TryValidateTokenSet(httpContext, cookieToken, null, out message));
|
||||||
|
|
||||||
|
var trimmed = ex.Message.Substring(0, ex.Message.IndexOf(Environment.NewLine));
|
||||||
|
Assert.Equal("The required antiforgery request token must be provided.", trimmed);
|
||||||
|
}
|
||||||
|
|
||||||
|
[Fact]
|
||||||
|
public void TryValidateTokenSet_FieldAndCookieTokensSwapped_FieldTokenDuplicated()
|
||||||
|
{
|
||||||
|
// Arrange
|
||||||
|
var httpContext = new DefaultHttpContext();
|
||||||
|
httpContext.User = new ClaimsPrincipal(new ClaimsIdentity());
|
||||||
|
|
||||||
|
var cookieToken = new AntiforgeryToken() { IsCookieToken = true };
|
||||||
|
var fieldtoken = new AntiforgeryToken() { IsCookieToken = false };
|
||||||
|
|
||||||
|
var tokenProvider = new DefaultAntiforgeryTokenGenerator(
|
||||||
|
claimUidExtractor: null,
|
||||||
|
additionalDataProvider: null);
|
||||||
|
|
||||||
|
string expectedMessage =
|
||||||
|
"Validation of the provided antiforgery token failed. " +
|
||||||
|
"The cookie token and the request token were swapped.";
|
||||||
|
|
||||||
|
// Act
|
||||||
|
string message;
|
||||||
|
var result = tokenProvider.TryValidateTokenSet(httpContext, fieldtoken, fieldtoken, out message);
|
||||||
|
|
||||||
|
// Assert
|
||||||
|
Assert.False(result);
|
||||||
|
Assert.Equal(expectedMessage, message);
|
||||||
|
}
|
||||||
|
|
||||||
|
[Fact]
|
||||||
|
public void TryValidateTokenSet_FieldAndCookieTokensSwapped_CookieDuplicated()
|
||||||
|
{
|
||||||
|
// Arrange
|
||||||
|
var httpContext = new DefaultHttpContext();
|
||||||
|
httpContext.User = new ClaimsPrincipal(new ClaimsIdentity());
|
||||||
|
|
||||||
|
var cookieToken = new AntiforgeryToken() { IsCookieToken = true };
|
||||||
|
var fieldtoken = new AntiforgeryToken() { IsCookieToken = false };
|
||||||
|
|
||||||
|
var tokenProvider = new DefaultAntiforgeryTokenGenerator(
|
||||||
|
claimUidExtractor: null,
|
||||||
|
additionalDataProvider: null);
|
||||||
|
|
||||||
|
string expectedMessage =
|
||||||
|
"Validation of the provided antiforgery token failed. " +
|
||||||
|
"The cookie token and the request token were swapped.";
|
||||||
|
|
||||||
|
// Act
|
||||||
|
string message;
|
||||||
|
var result = tokenProvider.TryValidateTokenSet(httpContext, cookieToken, cookieToken, out message);
|
||||||
|
|
||||||
|
// Assert
|
||||||
|
Assert.False(result);
|
||||||
|
Assert.Equal(expectedMessage, message);
|
||||||
|
}
|
||||||
|
|
||||||
|
[Fact]
|
||||||
|
public void TryValidateTokenSet_FieldAndCookieTokensHaveDifferentSecurityKeys()
|
||||||
|
{
|
||||||
|
// Arrange
|
||||||
|
var httpContext = new DefaultHttpContext();
|
||||||
|
httpContext.User = new ClaimsPrincipal(new ClaimsIdentity());
|
||||||
|
|
||||||
|
var cookieToken = new AntiforgeryToken() { IsCookieToken = true };
|
||||||
|
var fieldtoken = new AntiforgeryToken() { IsCookieToken = false };
|
||||||
|
|
||||||
|
var tokenProvider = new DefaultAntiforgeryTokenGenerator(
|
||||||
|
claimUidExtractor: null,
|
||||||
|
additionalDataProvider: null);
|
||||||
|
|
||||||
|
string expectedMessage = "The antiforgery cookie token and request token do not match.";
|
||||||
|
|
||||||
|
// Act
|
||||||
|
string message;
|
||||||
|
var result = tokenProvider.TryValidateTokenSet(httpContext, cookieToken, fieldtoken, out message);
|
||||||
|
|
||||||
|
// Assert
|
||||||
|
Assert.False(result);
|
||||||
|
Assert.Equal(expectedMessage, message);
|
||||||
}
|
}
|
||||||
|
|
||||||
[Theory]
|
[Theory]
|
||||||
[InlineData("the-user", "the-other-user")]
|
[InlineData("the-user", "the-other-user")]
|
||||||
[InlineData("http://example.com/uri-casing", "http://example.com/URI-casing")]
|
[InlineData("http://example.com/uri-casing", "http://example.com/URI-casing")]
|
||||||
[InlineData("https://example.com/secure-uri-casing", "https://example.com/secure-URI-casing")]
|
[InlineData("https://example.com/secure-uri-casing", "https://example.com/secure-URI-casing")]
|
||||||
public void ValidateTokens_UsernameMismatch(string identityUsername, string embeddedUsername)
|
public void TryValidateTokenSet_UsernameMismatch(string identityUsername, string embeddedUsername)
|
||||||
{
|
{
|
||||||
// Arrange
|
// Arrange
|
||||||
var httpContext = new DefaultHttpContext();
|
var httpContext = new DefaultHttpContext();
|
||||||
|
|
@ -368,17 +417,21 @@ namespace Microsoft.AspNetCore.Antiforgery
|
||||||
claimUidExtractor: mockClaimUidExtractor.Object,
|
claimUidExtractor: mockClaimUidExtractor.Object,
|
||||||
additionalDataProvider: null);
|
additionalDataProvider: null);
|
||||||
|
|
||||||
// Act & Assert
|
string expectedMessage =
|
||||||
var exception = Assert.Throws<AntiforgeryValidationException>(
|
$"The provided antiforgery token was meant for user \"{embeddedUsername}\", " +
|
||||||
() => tokenProvider.ValidateTokens(httpContext, cookieToken, fieldtoken));
|
$"but the current user is \"{identityUsername}\".";
|
||||||
Assert.Equal(
|
|
||||||
@"The provided antiforgery token was meant for user """ + embeddedUsername +
|
// Act
|
||||||
@""", but the current user is """ + identityUsername + @""".",
|
string message;
|
||||||
exception.Message);
|
var result = tokenProvider.TryValidateTokenSet(httpContext, cookieToken, fieldtoken, out message);
|
||||||
|
|
||||||
|
// Assert
|
||||||
|
Assert.False(result);
|
||||||
|
Assert.Equal(expectedMessage, message);
|
||||||
}
|
}
|
||||||
|
|
||||||
[Fact]
|
[Fact]
|
||||||
public void ValidateTokens_ClaimUidMismatch()
|
public void TryValidateTokenSet_ClaimUidMismatch()
|
||||||
{
|
{
|
||||||
// Arrange
|
// Arrange
|
||||||
var httpContext = new DefaultHttpContext();
|
var httpContext = new DefaultHttpContext();
|
||||||
|
|
@ -402,16 +455,21 @@ namespace Microsoft.AspNetCore.Antiforgery
|
||||||
claimUidExtractor: mockClaimUidExtractor.Object,
|
claimUidExtractor: mockClaimUidExtractor.Object,
|
||||||
additionalDataProvider: null);
|
additionalDataProvider: null);
|
||||||
|
|
||||||
// Act & assert
|
string expectedMessage =
|
||||||
var exception = Assert.Throws<AntiforgeryValidationException>(
|
"The provided antiforgery token was meant for a different " +
|
||||||
() => tokenProvider.ValidateTokens(httpContext, cookieToken, fieldtoken));
|
"claims-based user than the current user.";
|
||||||
Assert.Equal(
|
|
||||||
@"The provided antiforgery token was meant for a different claims-based user than the current user.",
|
// Act
|
||||||
exception.Message);
|
string message;
|
||||||
|
var result = tokenProvider.TryValidateTokenSet(httpContext, cookieToken, fieldtoken, out message);
|
||||||
|
|
||||||
|
// Assert
|
||||||
|
Assert.False(result);
|
||||||
|
Assert.Equal(expectedMessage, message);
|
||||||
}
|
}
|
||||||
|
|
||||||
[Fact]
|
[Fact]
|
||||||
public void ValidateTokens_AdditionalDataRejected()
|
public void TryValidateTokenSet_AdditionalDataRejected()
|
||||||
{
|
{
|
||||||
// Arrange
|
// Arrange
|
||||||
var httpContext = new DefaultHttpContext();
|
var httpContext = new DefaultHttpContext();
|
||||||
|
|
@ -428,21 +486,27 @@ namespace Microsoft.AspNetCore.Antiforgery
|
||||||
};
|
};
|
||||||
|
|
||||||
var mockAdditionalDataProvider = new Mock<IAntiforgeryAdditionalDataProvider>();
|
var mockAdditionalDataProvider = new Mock<IAntiforgeryAdditionalDataProvider>();
|
||||||
mockAdditionalDataProvider.Setup(o => o.ValidateAdditionalData(httpContext, "some-additional-data"))
|
mockAdditionalDataProvider
|
||||||
.Returns(false);
|
.Setup(o => o.ValidateAdditionalData(httpContext, "some-additional-data"))
|
||||||
|
.Returns(false);
|
||||||
|
|
||||||
var tokenProvider = new DefaultAntiforgeryTokenGenerator(
|
var tokenProvider = new DefaultAntiforgeryTokenGenerator(
|
||||||
claimUidExtractor: null,
|
claimUidExtractor: null,
|
||||||
additionalDataProvider: mockAdditionalDataProvider.Object);
|
additionalDataProvider: mockAdditionalDataProvider.Object);
|
||||||
|
|
||||||
// Act & assert
|
string expectedMessage = "The provided antiforgery token failed a custom data check.";
|
||||||
var exception = Assert.Throws<AntiforgeryValidationException>(
|
|
||||||
() => tokenProvider.ValidateTokens(httpContext, cookieToken, fieldtoken));
|
// Act
|
||||||
Assert.Equal(@"The provided antiforgery token failed a custom data check.", exception.Message);
|
string message;
|
||||||
|
var result = tokenProvider.TryValidateTokenSet(httpContext, cookieToken, fieldtoken, out message);
|
||||||
|
|
||||||
|
// Assert
|
||||||
|
Assert.False(result);
|
||||||
|
Assert.Equal(expectedMessage, message);
|
||||||
}
|
}
|
||||||
|
|
||||||
[Fact]
|
[Fact]
|
||||||
public void ValidateTokens_Success_AnonymousUser()
|
public void TryValidateTokenSet_Success_AnonymousUser()
|
||||||
{
|
{
|
||||||
// Arrange
|
// Arrange
|
||||||
var httpContext = new DefaultHttpContext();
|
var httpContext = new DefaultHttpContext();
|
||||||
|
|
@ -467,14 +531,16 @@ namespace Microsoft.AspNetCore.Antiforgery
|
||||||
additionalDataProvider: mockAdditionalDataProvider.Object);
|
additionalDataProvider: mockAdditionalDataProvider.Object);
|
||||||
|
|
||||||
// Act
|
// Act
|
||||||
tokenProvider.ValidateTokens(httpContext, cookieToken, fieldtoken);
|
string message;
|
||||||
|
var result = tokenProvider.TryValidateTokenSet(httpContext, cookieToken, fieldtoken, out message);
|
||||||
|
|
||||||
// Assert
|
// Assert
|
||||||
// Nothing to assert - if we got this far, success!
|
Assert.True(result);
|
||||||
|
Assert.Null(message);
|
||||||
}
|
}
|
||||||
|
|
||||||
[Fact]
|
[Fact]
|
||||||
public void ValidateTokens_Success_AuthenticatedUserWithUsername()
|
public void TryValidateTokenSet_Success_AuthenticatedUserWithUsername()
|
||||||
{
|
{
|
||||||
// Arrange
|
// Arrange
|
||||||
var httpContext = new DefaultHttpContext();
|
var httpContext = new DefaultHttpContext();
|
||||||
|
|
@ -499,14 +565,16 @@ namespace Microsoft.AspNetCore.Antiforgery
|
||||||
additionalDataProvider: mockAdditionalDataProvider.Object);
|
additionalDataProvider: mockAdditionalDataProvider.Object);
|
||||||
|
|
||||||
// Act
|
// Act
|
||||||
tokenProvider.ValidateTokens(httpContext, cookieToken, fieldtoken);
|
string message;
|
||||||
|
var result = tokenProvider.TryValidateTokenSet(httpContext, cookieToken, fieldtoken, out message);
|
||||||
|
|
||||||
// Assert
|
// Assert
|
||||||
// Nothing to assert - if we got this far, success!
|
Assert.True(result);
|
||||||
|
Assert.Null(message);
|
||||||
}
|
}
|
||||||
|
|
||||||
[Fact]
|
[Fact]
|
||||||
public void ValidateTokens_Success_ClaimsBasedUser()
|
public void TryValidateTokenSet_Success_ClaimsBasedUser()
|
||||||
{
|
{
|
||||||
// Arrange
|
// Arrange
|
||||||
var httpContext = new DefaultHttpContext();
|
var httpContext = new DefaultHttpContext();
|
||||||
|
|
@ -530,10 +598,12 @@ namespace Microsoft.AspNetCore.Antiforgery
|
||||||
additionalDataProvider: null);
|
additionalDataProvider: null);
|
||||||
|
|
||||||
// Act
|
// Act
|
||||||
tokenProvider.ValidateTokens(httpContext, cookieToken, fieldtoken);
|
string message;
|
||||||
|
var result = tokenProvider.TryValidateTokenSet(httpContext, cookieToken, fieldtoken, out message);
|
||||||
|
|
||||||
// Assert
|
// Assert
|
||||||
// Nothing to assert - if we got this far, success!
|
Assert.True(result);
|
||||||
|
Assert.Null(message);
|
||||||
}
|
}
|
||||||
|
|
||||||
private static ClaimsIdentity GetAuthenticatedIdentity(string identityUsername)
|
private static ClaimsIdentity GetAuthenticatedIdentity(string identityUsername)
|
||||||
|
|
|
||||||
|
|
@ -6,6 +6,7 @@
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"Microsoft.AspNetCore.Antiforgery": "1.0.0-*",
|
"Microsoft.AspNetCore.Antiforgery": "1.0.0-*",
|
||||||
"Microsoft.AspNetCore.Http": "1.0.0-*",
|
"Microsoft.AspNetCore.Http": "1.0.0-*",
|
||||||
|
"Microsoft.AspNetCore.Testing": "1.0.0-*",
|
||||||
"Microsoft.Extensions.DependencyInjection": "1.0.0-*",
|
"Microsoft.Extensions.DependencyInjection": "1.0.0-*",
|
||||||
"Microsoft.Extensions.WebEncoders": "1.0.0-*",
|
"Microsoft.Extensions.WebEncoders": "1.0.0-*",
|
||||||
"Microsoft.NETCore.Platforms": "1.0.1-*",
|
"Microsoft.NETCore.Platforms": "1.0.1-*",
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue