diff --git a/src/Microsoft.AspNet.Mvc.Core/AntiForgery/AntiForgery.cs b/src/Microsoft.AspNet.Mvc.Core/AntiForgery/AntiForgery.cs
new file mode 100644
index 0000000000..ca7ff68dea
--- /dev/null
+++ b/src/Microsoft.AspNet.Mvc.Core/AntiForgery/AntiForgery.cs
@@ -0,0 +1,98 @@
+using System.ComponentModel;
+using System.Threading.Tasks;
+using Microsoft.AspNet.Abstractions;
+using Microsoft.AspNet.Mvc.Rendering;
+using Microsoft.AspNet.Security.DataProtection;
+
+namespace Microsoft.AspNet.Mvc
+{
+ ///
+ /// Provides access to the anti-forgery system, which provides protection against
+ /// Cross-site Request Forgery (XSRF, also called CSRF) attacks.
+ ///
+ public sealed class AntiForgery
+ {
+ private static readonly string _purpose = "Microsoft.AspNet.Mvc.AntiXsrf.AntiForgeryToken.v1";
+ private readonly AntiForgeryWorker _worker;
+
+ public AntiForgery([NotNull] IClaimUidExtractor claimUidExtractor,
+ [NotNull] IDataProtectionProvider dataProtectionProvider,
+ [NotNull] IAntiForgeryAdditionalDataProvider additionalDataProvider)
+ {
+ // TODO: This is temporary till we figure out how to flow configs using DI.
+ var config = new AntiForgeryConfigWrapper();
+ var serializer = new AntiForgeryTokenSerializer(dataProtectionProvider.CreateProtector(_purpose));
+ var tokenStore = new AntiForgeryTokenStore(config, serializer);
+ var tokenProvider = new TokenProvider(config, claimUidExtractor, additionalDataProvider);
+ _worker = new AntiForgeryWorker(serializer, config, tokenStore, tokenProvider, tokenProvider);
+ }
+
+ ///
+ /// Generates an anti-forgery token for this request. This token can
+ /// be validated by calling the Validate() method.
+ ///
+ /// The HTTP context associated with the current call.
+ /// An HTML string corresponding to an <input type="hidden">
+ /// element. This element should be put inside a <form>.
+ ///
+ /// This method has a side effect:
+ /// A response cookie is set if there is no valid cookie associated with the request.
+ ///
+ public HtmlString GetHtml([NotNull] HttpContext context)
+ {
+ TagBuilder builder = _worker.GetFormInputElement(context);
+ return builder.ToHtmlString(TagRenderMode.SelfClosing);
+ }
+
+ ///
+ /// Generates an anti-forgery token pair (cookie and form token) for this request.
+ /// This method is similar to GetHtml(HttpContext context), but this method gives the caller control
+ /// over how to persist the returned values. To validate these tokens, call the
+ /// appropriate overload of Validate.
+ ///
+ /// The HTTP context associated with the current call.
+ /// The anti-forgery token - if any - that already existed
+ /// for this request. May be null. The anti-forgery system will try to reuse this cookie
+ /// value when generating a matching form token.
+ ///
+ /// Unlike the GetHtml(HttpContext context) method, this method has no side effect. The caller
+ /// is responsible for setting the response cookie and injecting the returned
+ /// form token as appropriate.
+ ///
+ public AntiForgeryTokenSet GetTokens([NotNull] HttpContext context, string oldCookieToken)
+ {
+ // Will contain a new cookie value if the old cookie token
+ // was null or invalid. If this value is non-null when the method completes, the caller
+ // must persist this value in the form of a response cookie, and the existing cookie value
+ // should be discarded. If this value is null when the method completes, the existing
+ // cookie value was valid and needn't be modified.
+ return _worker.GetTokens(context, oldCookieToken);
+ }
+
+ ///
+ /// Validates an anti-forgery token that was supplied for this request.
+ /// The anti-forgery token may be generated by calling GetHtml(HttpContext context).
+ ///
+ /// The HTTP context associated with the current call.
+ public async Task ValidateAsync([NotNull] HttpContext context)
+ {
+ await _worker.ValidateAsync(context);
+ }
+
+ ///
+ /// Validates an anti-forgery token pair that was generated by the GetTokens method.
+ ///
+ /// The HTTP context associated with the current call.
+ /// The token that was supplied in the request cookie.
+ /// The token that was supplied in the request form body.
+ public void Validate([NotNull] HttpContext context, string cookieToken, string formToken)
+ {
+ _worker.Validate(context, cookieToken, formToken);
+ }
+
+ public void Validate([NotNull] HttpContext context, AntiForgeryTokenSet antiForgeryTokenSet)
+ {
+ Validate(context, antiForgeryTokenSet.CookieToken, antiForgeryTokenSet.FormToken);
+ }
+ }
+}
\ No newline at end of file
diff --git a/src/Microsoft.AspNet.Mvc.Core/AntiForgery/AntiForgeryConfig.cs b/src/Microsoft.AspNet.Mvc.Core/AntiForgery/AntiForgeryConfig.cs
new file mode 100644
index 0000000000..454a7c4cbe
--- /dev/null
+++ b/src/Microsoft.AspNet.Mvc.Core/AntiForgery/AntiForgeryConfig.cs
@@ -0,0 +1,64 @@
+namespace Microsoft.AspNet.Mvc
+{
+ ///
+ /// Provides programmatic configuration for the anti-forgery token system.
+ ///
+ public static class AntiForgeryConfig
+ {
+ internal const string AntiForgeryTokenFieldName = "__RequestVerificationToken";
+ private static string _cookieName;
+
+ ///
+ /// Specifies the name of the cookie that is used by the anti-forgery
+ /// system.
+ ///
+ ///
+ /// If an explicit name is not provided, the system will automatically
+ /// generate a name.
+ ///
+ public static string CookieName
+ {
+ get
+ {
+ if (_cookieName == null)
+ {
+ _cookieName = GetAntiForgeryCookieName();
+ }
+ return _cookieName;
+ }
+ set
+ {
+ _cookieName = value;
+ }
+ }
+
+ ///
+ /// Specifies whether SSL is required for the anti-forgery system
+ /// to operate. If this setting is 'true' and a non-SSL request
+ /// comes into the system, all anti-forgery APIs will fail.
+ ///
+ public static bool RequireSsl
+ {
+ get;
+ set;
+ }
+
+ ///
+ /// Specifies whether to suppress the generation of X-Frame-Options header
+ /// which is used to prevent ClickJacking. By default, the X-Frame-Options
+ /// header is generated with the value SAMEORIGIN. If this setting is 'true',
+ /// the X-Frame-Options header will not be generated for the response.
+ ///
+ public static bool SuppressXFrameOptionsHeader
+ {
+ get;
+ set;
+ }
+
+ // TODO: Replace the stub.
+ private static string GetAntiForgeryCookieName()
+ {
+ return AntiForgeryTokenFieldName;
+ }
+ }
+}
\ No newline at end of file
diff --git a/src/Microsoft.AspNet.Mvc.Core/AntiForgery/AntiForgeryConfigWrapper.cs b/src/Microsoft.AspNet.Mvc.Core/AntiForgery/AntiForgeryConfigWrapper.cs
new file mode 100644
index 0000000000..57dd2b4f7b
--- /dev/null
+++ b/src/Microsoft.AspNet.Mvc.Core/AntiForgery/AntiForgeryConfigWrapper.cs
@@ -0,0 +1,25 @@
+namespace Microsoft.AspNet.Mvc
+{
+ public sealed class AntiForgeryConfigWrapper : IAntiForgeryConfig
+ {
+ public string CookieName
+ {
+ get { return AntiForgeryConfig.CookieName; }
+ }
+
+ public string FormFieldName
+ {
+ get { return AntiForgeryConfig.AntiForgeryTokenFieldName; }
+ }
+
+ public bool RequireSSL
+ {
+ get { return AntiForgeryConfig.RequireSsl; }
+ }
+
+ public bool SuppressXFrameOptionsHeader
+ {
+ get { return AntiForgeryConfig.SuppressXFrameOptionsHeader; }
+ }
+ }
+}
\ No newline at end of file
diff --git a/src/Microsoft.AspNet.Mvc.Core/AntiForgery/AntiForgeryToken.cs b/src/Microsoft.AspNet.Mvc.Core/AntiForgery/AntiForgeryToken.cs
new file mode 100644
index 0000000000..b1bb1b58a3
--- /dev/null
+++ b/src/Microsoft.AspNet.Mvc.Core/AntiForgery/AntiForgeryToken.cs
@@ -0,0 +1,52 @@
+using System;
+
+namespace Microsoft.AspNet.Mvc
+{
+ internal sealed class AntiForgeryToken
+ {
+ internal const int SecurityTokenBitLength = 128;
+ internal const int ClaimUidBitLength = 256;
+
+ private string _additionalData = string.Empty;
+ private string _username = string.Empty;
+ private BinaryBlob _securityToken;
+
+ public string AdditionalData
+ {
+ get { return _additionalData; }
+ set
+ {
+ _additionalData = value ?? string.Empty;
+ }
+ }
+
+ public BinaryBlob ClaimUid { get; set; }
+
+ public bool IsSessionToken { get; set; }
+
+ public BinaryBlob SecurityToken
+ {
+ get
+ {
+ if (_securityToken == null)
+ {
+ _securityToken = new BinaryBlob(SecurityTokenBitLength);
+ }
+ return _securityToken;
+ }
+ set
+ {
+ _securityToken = value;
+ }
+ }
+
+ public string Username
+ {
+ get { return _username; }
+ set
+ {
+ _username = value ?? string.Empty;
+ }
+ }
+ }
+}
\ No newline at end of file
diff --git a/src/Microsoft.AspNet.Mvc.Core/AntiForgery/AntiForgeryTokenSerializer.cs b/src/Microsoft.AspNet.Mvc.Core/AntiForgery/AntiForgeryTokenSerializer.cs
new file mode 100644
index 0000000000..d64b6e3e7b
--- /dev/null
+++ b/src/Microsoft.AspNet.Mvc.Core/AntiForgery/AntiForgeryTokenSerializer.cs
@@ -0,0 +1,187 @@
+
+using System;
+using System.IO;
+using Microsoft.AspNet.Mvc.Core;
+using Microsoft.AspNet.Security.DataProtection;
+using System.Text;
+
+namespace Microsoft.AspNet.Mvc
+{
+ internal sealed class AntiForgeryTokenSerializer : IAntiForgeryTokenSerializer
+ {
+ private readonly IDataProtector _cryptoSystem;
+ private const byte TokenVersion = 0x01;
+
+ internal AntiForgeryTokenSerializer([NotNull] IDataProtector cryptoSystem)
+ {
+ _cryptoSystem = cryptoSystem;
+ }
+
+ public AntiForgeryToken Deserialize(string serializedToken)
+ {
+ Exception innerException = null;
+ try
+ {
+ using (MemoryStream stream = new MemoryStream(UrlTokenDecode(serializedToken)))
+ {
+ using (BinaryReader reader = new BinaryReader(stream))
+ {
+ AntiForgeryToken token = DeserializeImpl(reader);
+ if (token != null)
+ {
+ return token;
+ }
+ }
+ }
+ }
+ catch(Exception ex)
+ {
+ // swallow all exceptions - homogenize error if something went wrong
+ innerException = ex;
+ }
+
+ // if we reached this point, something went wrong deserializing
+ throw new InvalidOperationException(Resources.AntiForgeryToken_DeserializationFailed, innerException);
+ }
+
+ /* 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]
+ * +- IsClaimsBased: 1 byte Boolean
+ * | [if IsClaimsBased = true]
+ * | `- ClaimUid: 32 byte binary blob
+ * | [if IsClaimsBased = false]
+ * | `- Username: UTF-8 string with 7-bit integer length prefix
+ * `- AdditionalData: UTF-8 string with 7-bit integer length prefix
+ */
+ private static AntiForgeryToken DeserializeImpl(BinaryReader reader)
+ {
+ // we can only consume tokens of the same serialized version that we generate
+ var embeddedVersion = reader.ReadByte();
+ if (embeddedVersion != TokenVersion)
+ {
+ return null;
+ }
+
+ var deserializedToken = new AntiForgeryToken();
+ var securityTokenBytes = reader.ReadBytes(AntiForgeryToken.SecurityTokenBitLength / 8);
+ deserializedToken.SecurityToken = new BinaryBlob(AntiForgeryToken.SecurityTokenBitLength, securityTokenBytes);
+ deserializedToken.IsSessionToken = reader.ReadBoolean();
+
+ if (!deserializedToken.IsSessionToken)
+ {
+ bool isClaimsBased = reader.ReadBoolean();
+ if (isClaimsBased)
+ {
+ var claimUidBytes = reader.ReadBytes(AntiForgeryToken.ClaimUidBitLength / 8);
+ deserializedToken.ClaimUid = new BinaryBlob(AntiForgeryToken.ClaimUidBitLength, claimUidBytes);
+ }
+ else
+ {
+ deserializedToken.Username = reader.ReadString();
+ }
+
+ deserializedToken.AdditionalData = reader.ReadString();
+ }
+
+ // if there's still unconsumed data in the stream, fail
+ if (reader.BaseStream.ReadByte() != -1)
+ {
+ return null;
+ }
+
+ // success
+ return deserializedToken;
+ }
+
+ public string Serialize([NotNull] AntiForgeryToken token)
+ {
+ using (var stream = new MemoryStream())
+ {
+ using (var writer = new BinaryWriter(stream))
+ {
+ writer.Write(TokenVersion);
+ writer.Write(token.SecurityToken.GetData());
+ writer.Write(token.IsSessionToken);
+
+ if (!token.IsSessionToken)
+ {
+ if (token.ClaimUid != null)
+ {
+ writer.Write(true /* isClaimsBased */);
+ writer.Write(token.ClaimUid.GetData());
+ }
+ else
+ {
+ writer.Write(false /* isClaimsBased */);
+ writer.Write(token.Username);
+ }
+
+ writer.Write(token.AdditionalData);
+ }
+
+ writer.Flush();
+ return UrlTokenEncode(_cryptoSystem.Protect(stream.ToArray()));
+ }
+ }
+ }
+
+ private string UrlTokenEncode(byte[] input)
+ {
+ var base64String = Convert.ToBase64String(input);
+ if (string.IsNullOrEmpty(base64String))
+ {
+ return string.Empty;
+ }
+
+ var sb = new StringBuilder();
+ for (int i = 0; i < base64String.Length; i++)
+ {
+ switch (base64String[i])
+ {
+ case '+':
+ sb.Append('-');
+ break;
+ case '/':
+ sb.Append('_');
+ break;
+ case '=':
+ sb.Append('.');
+ break;
+ default:
+ sb.Append(base64String[i]);
+ break;
+ }
+ }
+
+ return sb.ToString();
+ }
+
+ private byte[] UrlTokenDecode(string input)
+ {
+ var sb = new StringBuilder();
+ for (int i = 0; i < input.Length; i++)
+ {
+ switch (input[i])
+ {
+ case '-':
+ sb.Append('+');
+ break;
+ case '_':
+ sb.Append('/');
+ break;
+ case '.':
+ sb.Append('=');
+ break;
+ default:
+ sb.Append(input[i]);
+ break;
+ }
+ }
+
+ return Convert.FromBase64String(sb.ToString());
+ }
+ }
+}
\ No newline at end of file
diff --git a/src/Microsoft.AspNet.Mvc.Core/AntiForgery/AntiForgeryTokenSet.cs b/src/Microsoft.AspNet.Mvc.Core/AntiForgery/AntiForgeryTokenSet.cs
new file mode 100644
index 0000000000..867847e251
--- /dev/null
+++ b/src/Microsoft.AspNet.Mvc.Core/AntiForgery/AntiForgeryTokenSet.cs
@@ -0,0 +1,26 @@
+using System;
+using Microsoft.AspNet.Mvc.Core;
+
+namespace Microsoft.AspNet.Mvc
+{
+ public class AntiForgeryTokenSet
+ {
+ public AntiForgeryTokenSet(string formToken, string cookieToken)
+ {
+ if (string.IsNullOrEmpty(formToken))
+ {
+ throw new ArgumentException(Resources.ArgumentCannotBeNullOrEmpty, formToken);
+ }
+
+ FormToken = formToken;
+ CookieToken = cookieToken;
+ }
+
+ public string FormToken { get; private set; }
+
+ // The cookie token is allowed to be null.
+ // This would be the case when the old cookie token is still valid.
+ // In such cases a call to GetTokens would return a token set with null cookie token.
+ public string CookieToken { get; private set; }
+ }
+}
\ No newline at end of file
diff --git a/src/Microsoft.AspNet.Mvc.Core/AntiForgery/AntiForgeryTokenStore.cs b/src/Microsoft.AspNet.Mvc.Core/AntiForgery/AntiForgeryTokenStore.cs
new file mode 100644
index 0000000000..90c39c95c8
--- /dev/null
+++ b/src/Microsoft.AspNet.Mvc.Core/AntiForgery/AntiForgeryTokenStore.cs
@@ -0,0 +1,61 @@
+
+using System;
+using Microsoft.AspNet.Abstractions;
+using System.Threading.Tasks;
+
+namespace Microsoft.AspNet.Mvc
+{
+ // Saves anti-XSRF tokens split between HttpRequest.Cookies and HttpRequest.Form
+ internal sealed class AntiForgeryTokenStore : ITokenStore
+ {
+ private readonly IAntiForgeryConfig _config;
+ private readonly IAntiForgeryTokenSerializer _serializer;
+
+ internal AntiForgeryTokenStore([NotNull] IAntiForgeryConfig config,
+ [NotNull] IAntiForgeryTokenSerializer serializer)
+ {
+ _config = config;
+ _serializer = serializer;
+ }
+
+ public AntiForgeryToken GetCookieToken(HttpContext httpContext)
+ {
+ var cookie = httpContext.Request.Cookies[_config.CookieName];
+ if (String.IsNullOrEmpty(cookie))
+ {
+ // did not exist
+ return null;
+ }
+
+ return _serializer.Deserialize(cookie);
+ }
+
+ public async Task GetFormTokenAsync(HttpContext httpContext)
+ {
+ var form = await httpContext.Request.GetFormAsync();
+ string value = form[_config.FormFieldName];
+ if (string.IsNullOrEmpty(value))
+ {
+ // did not exist
+ return null;
+ }
+
+ return _serializer.Deserialize(value);
+ }
+
+ public void SaveCookieToken(HttpContext httpContext, AntiForgeryToken token)
+ {
+ string serializedToken = _serializer.Serialize(token);
+ var options = new CookieOptions() { HttpOnly = true };
+
+ // Note: don't use "newCookie.Secure = _config.RequireSSL;" since the default
+ // value of newCookie.Secure is poulated out of band.
+ if (_config.RequireSSL)
+ {
+ options.Secure = true;
+ }
+
+ httpContext.Response.Cookies.Append(_config.CookieName, serializedToken, options);
+ }
+ }
+}
\ No newline at end of file
diff --git a/src/Microsoft.AspNet.Mvc.Core/AntiForgery/AntiForgeryWorker.cs b/src/Microsoft.AspNet.Mvc.Core/AntiForgery/AntiForgeryWorker.cs
new file mode 100644
index 0000000000..437b548815
--- /dev/null
+++ b/src/Microsoft.AspNet.Mvc.Core/AntiForgery/AntiForgeryWorker.cs
@@ -0,0 +1,212 @@
+
+using System;
+using System.Diagnostics.CodeAnalysis;
+using System.Diagnostics.Contracts;
+using System.Security.Claims;
+using System.Threading.Tasks;
+using Microsoft.AspNet.Abstractions;
+using Microsoft.AspNet.Mvc.Core;
+using Microsoft.AspNet.Mvc.Rendering;
+
+namespace Microsoft.AspNet.Mvc
+{
+ internal sealed class AntiForgeryWorker
+ {
+ private readonly IAntiForgeryConfig _config;
+ private readonly IAntiForgeryTokenSerializer _serializer;
+ private readonly ITokenStore _tokenStore;
+ private readonly ITokenValidator _validator;
+ private readonly ITokenGenerator _generator;
+
+ internal AntiForgeryWorker([NotNull] IAntiForgeryTokenSerializer serializer,
+ [NotNull] IAntiForgeryConfig config,
+ [NotNull] ITokenStore tokenStore,
+ [NotNull] ITokenGenerator generator,
+ [NotNull] ITokenValidator validator)
+ {
+ _serializer = serializer;
+ _config = config;
+ _tokenStore = tokenStore;
+ _generator = generator;
+ _validator = validator;
+ }
+
+ private void CheckSSLConfig(HttpContext httpContext)
+ {
+ if (_config.RequireSSL && !httpContext.Request.IsSecure)
+ {
+ throw new InvalidOperationException(Resources.AntiForgeryWorker_RequireSSL);
+ }
+ }
+
+ private AntiForgeryToken DeserializeToken(string serializedToken)
+ {
+ return (!string.IsNullOrEmpty(serializedToken))
+ ? _serializer.Deserialize(serializedToken)
+ : null;
+ }
+
+ [SuppressMessage("Microsoft.Design", "CA1031:DoNotCatchGeneralExceptionTypes", Justification = "Caller will just regenerate token in case of failure.")]
+ private AntiForgeryToken DeserializeTokenNoThrow(string serializedToken)
+ {
+ try
+ {
+ return DeserializeToken(serializedToken);
+ }
+ catch
+ {
+ // ignore failures since we'll just generate a new token
+ return null;
+ }
+ }
+
+ private static ClaimsIdentity ExtractIdentity(HttpContext httpContext)
+ {
+ if (httpContext != null)
+ {
+ ClaimsPrincipal user = httpContext.User;
+
+ if (user != null)
+ {
+ // We only support ClaimsIdentity.
+ // Todo remove this once httpContext.User moves to ClaimsIdentity.
+ return user.Identity as ClaimsIdentity;
+ }
+ }
+
+ return null;
+ }
+
+ [SuppressMessage("Microsoft.Design", "CA1031:DoNotCatchGeneralExceptionTypes", Justification = "Caller will just regenerate token in case of failure.")]
+ private AntiForgeryToken GetCookieTokenNoThrow(HttpContext httpContext)
+ {
+ try
+ {
+ return _tokenStore.GetCookieToken(httpContext);
+ }
+ catch
+ {
+ // ignore failures since we'll just generate a new token
+ return null;
+ }
+ }
+
+ // [ ENTRY POINT ]
+ // Generates an anti-XSRF token pair for the current user. The return
+ // value is the hidden input form element that should be rendered in
+ // the