Skip to content
This repository was archived by the owner on Dec 14, 2018. It is now read-only.

Commit fb44694

Browse files
committed
DI related changes to the interface, adding the fallback for extracting the ClaimUid.
1 parent 3895dc2 commit fb44694

17 files changed

+117
-153
lines changed

src/Microsoft.AspNet.Mvc.Core/AntiForgery/AntiForgery.cs

Lines changed: 10 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@
33
using Microsoft.AspNet.Abstractions;
44
using Microsoft.AspNet.Mvc.Rendering;
55
using Microsoft.AspNet.Security.DataProtection;
6+
using System.Threading.Tasks;
67

78
namespace Microsoft.AspNet.Mvc
89
{
@@ -15,12 +16,15 @@ public sealed class AntiForgery
1516
private static readonly string _purpose = "Microsoft.AspNet.Mvc.AntiXsrf.AntiForgeryToken.v1";
1617
private readonly AntiForgeryWorker _worker;
1718

18-
public AntiForgery(IAntiForgeryConfig config,
19-
IClaimUidExtractor claimUidExtractor)
19+
public AntiForgery(IClaimUidExtractor claimUidExtractor,
20+
IDataProtectionProvider dataProtectionProvider,
21+
IAntiForgeryAdditionalDataProvider additionalDataProvider)
2022
{
21-
var serializer = new AntiForgeryTokenSerializer(DataProtectionProvider.CreateNew().CreateProtector(_purpose));
23+
// TODO: This is temporary till we figure out how to flow configs using DI.
24+
var config = new AntiForgeryConfigWrapper();
25+
var serializer = new AntiForgeryTokenSerializer(dataProtectionProvider.CreateProtector(_purpose));
2226
var tokenStore = new AntiForgeryTokenStore(config, serializer);
23-
var tokenProvider = new TokenValidator(config, claimUidExtractor);
27+
var tokenProvider = new TokenProvider(config, claimUidExtractor, additionalDataProvider);
2428
_worker = new AntiForgeryWorker(serializer, config, tokenStore, tokenProvider, tokenProvider);
2529
}
2630

@@ -69,9 +73,9 @@ public AntiForgeryTokenSet GetTokens(HttpContext context, string oldCookieToken)
6973
/// The anti-forgery token may be generated by calling GetHtml().
7074
/// </summary>
7175
/// <param name="context">The http context associated with the current call.</param>
72-
public void Validate(HttpContext context)
76+
public async Task ValidateAsync(HttpContext context)
7377
{
74-
_worker.Validate(context);
78+
await _worker.ValidateAsync(context);
7579
}
7680

7781
/// <summary>

src/Microsoft.AspNet.Mvc.Core/AntiForgery/AntiForgeryConfig.cs

Lines changed: 4 additions & 37 deletions
Original file line numberDiff line numberDiff line change
@@ -1,28 +1,13 @@
1-

2-
using System.ComponentModel;
3-
4-
namespace Microsoft.AspNet.Mvc
1+
namespace Microsoft.AspNet.Mvc
52
{
63
/// <summary>
74
/// Provides programmatic configuration for the anti-forgery token system.
85
/// </summary>
96
public static class AntiForgeryConfig
107
{
118
internal const string AntiForgeryTokenFieldName = "__RequestVerificationToken";
12-
9+
private const string AntiForgeryCookieTokenName = "__RequestVerificationCookieToken";
1310
private static string _cookieName;
14-
private static string _uniqueClaimTypeIdentifier;
15-
16-
/// <summary>
17-
/// Specifies an object that can provide additional data to put into all
18-
/// generated tokens and that can validate additional data in incoming
19-
/// tokens.
20-
/// </summary>
21-
public static IAntiForgeryAdditionalDataProvider AdditionalDataProvider
22-
{
23-
get;
24-
set;
25-
}
2611

2712
/// <summary>
2813
/// Specifies the name of the cookie that is used by the anti-forgery
@@ -71,28 +56,10 @@ public static bool SuppressXFrameOptionsHeader
7156
set;
7257
}
7358

74-
/// <summary>
75-
/// Specifies whether the anti-forgery system should skip checking
76-
/// for conditions that might indicate misuse of the system. Please
77-
/// use caution when setting this switch, as improper use could open
78-
/// security holes in the application.
79-
/// </summary>
80-
/// <remarks>
81-
/// Setting this switch will disable several checks, including:
82-
/// - Identity.IsAuthenticated = true without Identity.Name being set
83-
/// - special-casing claims-based identities
84-
/// </remarks>
85-
[EditorBrowsable(EditorBrowsableState.Never)]
86-
public static bool SuppressIdentityHeuristicChecks
87-
{
88-
get;
89-
set;
90-
}
91-
92-
// TODO: Replace the stub.
59+
// TODO: Replace the stub.
9360
private static string GetAntiForgeryCookieName()
9461
{
95-
return AntiForgeryTokenFieldName;
62+
return AntiForgeryCookieTokenName;
9663
}
9764
}
9865
}

src/Microsoft.AspNet.Mvc.Core/AntiForgery/AntiForgeryConfigWrapper.cs

Lines changed: 1 addition & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -1,16 +1,7 @@
1-

2-
namespace Microsoft.AspNet.Mvc
1+
namespace Microsoft.AspNet.Mvc
32
{
43
public sealed class AntiForgeryConfigWrapper : IAntiForgeryConfig
54
{
6-
public IAntiForgeryAdditionalDataProvider AdditionalDataProvider
7-
{
8-
get
9-
{
10-
return AntiForgeryConfig.AdditionalDataProvider;
11-
}
12-
}
13-
145
public string CookieName
156
{
167
get { return AntiForgeryConfig.CookieName; }
@@ -26,11 +17,6 @@ public bool RequireSSL
2617
get { return AntiForgeryConfig.RequireSsl; }
2718
}
2819

29-
public bool SuppressIdentityHeuristicChecks
30-
{
31-
get { return AntiForgeryConfig.SuppressIdentityHeuristicChecks; }
32-
}
33-
3420
public bool SuppressXFrameOptionsHeader
3521
{
3622
get { return AntiForgeryConfig.SuppressXFrameOptionsHeader; }

src/Microsoft.AspNet.Mvc.Core/AntiForgery/AntiForgeryTokenStore.cs

Lines changed: 6 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11

22
using System;
33
using Microsoft.AspNet.Abstractions;
4+
using System.Threading.Tasks;
45

56
namespace Microsoft.AspNet.Mvc
67
{
@@ -28,10 +29,11 @@ public AntiForgeryToken GetCookieToken(HttpContext httpContext)
2829
return _serializer.Deserialize(cookie);
2930
}
3031

31-
public AntiForgeryToken GetFormToken(HttpContext httpContext)
32+
public async Task<AntiForgeryToken> GetFormTokenAsync(HttpContext httpContext)
3233
{
33-
string value = httpContext.Request.GetFormAsync().Result[_config.FormFieldName];
34-
if (String.IsNullOrEmpty(value))
34+
var form = await httpContext.Request.GetFormAsync();
35+
string value = form[_config.FormFieldName];
36+
if (string.IsNullOrEmpty(value))
3537
{
3638
// did not exist
3739
return null;
@@ -46,8 +48,7 @@ public void SaveCookieToken(HttpContext httpContext, AntiForgeryToken token)
4648
CookieOptions options = new CookieOptions() { HttpOnly = true };
4749

4850
// Note: don't use "newCookie.Secure = _config.RequireSSL;" since the default
49-
// value of newCookie.Secure is automatically populated from the <httpCookies>
50-
// config element.
51+
// value of newCookie.Secure is poulated out of band.
5152
if (_config.RequireSSL)
5253
{
5354
options.Secure = true;

src/Microsoft.AspNet.Mvc.Core/AntiForgery/AntiForgeryWorker.cs

Lines changed: 8 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,7 @@
77
using Microsoft.AspNet.Mvc.Core;
88
using Microsoft.AspNet.Mvc.Rendering;
99
using System.Security.Claims;
10+
using System.Threading.Tasks;
1011

1112
namespace Microsoft.AspNet.Mvc
1213
{
@@ -37,7 +38,7 @@ private void CheckSSLConfig(HttpContext httpContext)
3738

3839
private AntiForgeryToken DeserializeToken(string serializedToken)
3940
{
40-
return (!String.IsNullOrEmpty(serializedToken))
41+
return (!string.IsNullOrEmpty(serializedToken))
4142
? _serializer.Deserialize(serializedToken)
4243
: null;
4344
}
@@ -56,15 +57,17 @@ private AntiForgeryToken DeserializeTokenNoThrow(string serializedToken)
5657
}
5758
}
5859

59-
private static IIdentity ExtractIdentity(HttpContext httpContext)
60+
private static ClaimsIdentity ExtractIdentity(HttpContext httpContext)
6061
{
6162
if (httpContext != null)
6263
{
6364
ClaimsPrincipal user = httpContext.User;
6465

6566
if (user != null)
6667
{
67-
return user.Identity;
68+
// We only support ClaimsIdentity.
69+
// Todo remove this once httpContext.User moves to ClaimsIdentity.
70+
return user.Identity as ClaimsIdentity;
6871
}
6972
}
7073

@@ -159,13 +162,13 @@ private string Serialize(AntiForgeryToken token)
159162
// [ ENTRY POINT ]
160163
// Given an HttpContext, validates that the anti-XSRF tokens contained
161164
// in the cookies & form are OK for this request.
162-
public void Validate(HttpContext httpContext)
165+
public async Task ValidateAsync(HttpContext httpContext)
163166
{
164167
CheckSSLConfig(httpContext);
165168

166169
// Extract cookie & form tokens
167170
AntiForgeryToken cookieToken = _tokenStore.GetCookieToken(httpContext);
168-
AntiForgeryToken formToken = _tokenStore.GetFormToken(httpContext);
171+
AntiForgeryToken formToken = await _tokenStore.GetFormTokenAsync(httpContext);
169172

170173
// Validate
171174
_validator.ValidateTokens(httpContext, ExtractIdentity(httpContext), cookieToken, formToken);

src/Microsoft.AspNet.Mvc.Core/AntiForgery/BinaryBlob.cs

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@
55
using System.Globalization;
66
using System.Text;
77
using Microsoft.AspNet.Security.DataProtection;
8+
using System.Runtime.CompilerServices;
89

910
namespace Microsoft.AspNet.Mvc
1011
{
@@ -94,6 +95,7 @@ private static byte[] GenerateNewToken(int bitLength)
9495
return data;
9596
}
9697

98+
[MethodImplAttribute(MethodImplOptions.NoInlining | MethodImplOptions.NoOptimization)]
9799
private static bool AreByteArraysEqual(byte[] a, byte[] b)
98100
{
99101
if (a == null || b == null || a.Length != b.Length)
Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,18 @@
1+
using Microsoft.AspNet.Abstractions;
2+
3+
namespace Microsoft.AspNet.Mvc
4+
{
5+
public class DefaultAntiForgeryAdditionalDataProvider : IAntiForgeryAdditionalDataProvider
6+
{
7+
public virtual string GetAdditionalData(HttpContext context)
8+
{
9+
return string.Empty;
10+
}
11+
12+
public virtual bool ValidateAdditionalData(HttpContext context, string additionalData)
13+
{
14+
// Default implementation does not understand anything but empty data.
15+
return string.IsNullOrEmpty(additionalData);
16+
}
17+
}
18+
}

src/Microsoft.AspNet.Mvc.Core/AntiForgery/DefaultClaimUidExtractor.cs

Lines changed: 27 additions & 43 deletions
Original file line numberDiff line numberDiff line change
@@ -4,71 +4,54 @@
44
using System.Linq;
55
using System.Security.Claims;
66
using System.Security.Cryptography;
7-
using System.Security.Principal;
87

98
namespace Microsoft.AspNet.Mvc
109
{
1110
// Can extract unique identifers for a claims-based identity
1211
public class DefaultClaimUidExtractor : IClaimUidExtractor
1312
{
14-
private const string NameIdentifierClaimType = "http://schemas.xmlsoap.org/ws/2005/05/identity/claims/nameidentifier";
15-
16-
private readonly IAntiForgeryConfig _config;
17-
18-
public DefaultClaimUidExtractor(IAntiForgeryConfig config)
13+
public string ExtractClaimUid(ClaimsIdentity claimsIdentity)
1914
{
20-
_config = config;
21-
}
22-
23-
public byte[] ExtractClaimUid(IIdentity identity)
24-
{
25-
if (identity == null || !identity.IsAuthenticated || _config.SuppressIdentityHeuristicChecks)
15+
if (claimsIdentity == null || !claimsIdentity.IsAuthenticated)
2616
{
2717
// Skip anonymous users
28-
// Skip when claims-based checks are disabled
29-
return null;
30-
}
31-
32-
var claimsIdentity = identity as ClaimsIdentity;
33-
if (claimsIdentity == null)
34-
{
35-
// not a claims-based identity
3618
return null;
3719
}
3820

39-
string[] uniqueIdentifierParameters = GetUniqueIdentifierParameters(claimsIdentity);
21+
var uniqueIdentifierParameters = GetUniqueIdentifierParameters(claimsIdentity);
4022
byte[] claimUidBytes = ComputeSHA256(uniqueIdentifierParameters);
41-
return claimUidBytes;
23+
return Convert.ToBase64String(claimUidBytes);
4224
}
4325

44-
private static string[] GetUniqueIdentifierParameters(ClaimsIdentity claimsIdentity)
26+
private static IEnumerable<string> GetUniqueIdentifierParameters(ClaimsIdentity claimsIdentity)
4527
{
46-
// TODO: We need to select a single claim based on the Authentication Type of the claim.
47-
var claims = claimsIdentity.Claims;
48-
49-
// TODO: Need to check with vittorio for acs.
50-
// For a correctly configured ACS consumer, this tuple will uniquely
51-
// identify a user of the application. We assume that a well-behaved
52-
// identity provider will never assign the same name identifier to multiple
53-
// users within its security realm, and we assume that ACS has been
54-
// configured so that each identity provider has a unique 'identityProvider'
55-
// claim.
56-
// By default, we look for 'nameIdentifier' claim.
57-
Claim nameIdentifierClaim = claims.SingleOrDefault(claim => String.Equals(NameIdentifierClaimType, claim.Type, StringComparison.Ordinal));
58-
if (nameIdentifierClaim == null || String.IsNullOrEmpty(nameIdentifierClaim.Value))
28+
// TODO: Need to enable support for special casing acs identities.
29+
var nameIdentifierClaim = claimsIdentity.FindFirst(claim =>
30+
String.Equals(ClaimTypes.NameIdentifier,
31+
claim.Type, StringComparison.Ordinal));
32+
if (nameIdentifierClaim != null && !string.IsNullOrEmpty(nameIdentifierClaim.Value))
5933
{
60-
// TODO: The exception message would be decided based on the claim types we choose to expose.
61-
throw new InvalidOperationException("Resources.ClaimUidExtractor_DefaultClaimsNotPresent");
34+
return new string[]
35+
{
36+
ClaimTypes.NameIdentifier,
37+
nameIdentifierClaim.Value
38+
};
6239
}
6340

64-
return new string[]
41+
// We Do not understand this claimsIdentity, fallback on serializing the entire claims Identity.
42+
var claims = claimsIdentity.Claims.ToList();
43+
claims.Sort((a, b) => string.Compare(a.Type, b.Type, StringComparison.Ordinal));
44+
var identifierParameters = new List<string>();
45+
foreach (var claim in claims)
6546
{
66-
NameIdentifierClaimType,
67-
nameIdentifierClaim.Value
68-
};
47+
identifierParameters.Add(claim.Type);
48+
identifierParameters.Add(claim.Value);
49+
}
50+
51+
return identifierParameters;
6952
}
7053

71-
private static byte[] ComputeSHA256(IList<string> parameters)
54+
private static byte[] ComputeSHA256(IEnumerable<string> parameters)
7255
{
7356
using (MemoryStream ms = new MemoryStream())
7457
{
@@ -78,6 +61,7 @@ private static byte[] ComputeSHA256(IList<string> parameters)
7861
{
7962
bw.Write(parameter); // also writes the length as a prefix; unambiguous
8063
}
64+
8165
bw.Flush();
8266

8367
using (SHA256 sha256 = SHA256.Create())

src/Microsoft.AspNet.Mvc.Core/AntiForgery/IAntiForgeryConfig.cs

Lines changed: 0 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -3,9 +3,6 @@
33
// Provides configuration information about the anti-forgery system.
44
public interface IAntiForgeryConfig
55
{
6-
// Provides additional data to go into the tokens.
7-
IAntiForgeryAdditionalDataProvider AdditionalDataProvider { get; }
8-
96
// Name of the cookie to use.
107
string CookieName { get; }
118

@@ -15,9 +12,6 @@ public interface IAntiForgeryConfig
1512
// Whether SSL is mandatory for this request.
1613
bool RequireSSL { get; }
1714

18-
// Skip ClaimsIdentity & related logic.
19-
bool SuppressIdentityHeuristicChecks { get; }
20-
2115
// Skip X-FRAME-OPTIONS header.
2216
bool SuppressXFrameOptionsHeader { get; }
2317
}
Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,10 +1,11 @@
1-
using System.Security.Principal;
1+
using System.Security.Claims;
2+
using System.Security.Principal;
23

34
namespace Microsoft.AspNet.Mvc
45
{
56
// Can extract unique identifers for a claims-based identity
67
public interface IClaimUidExtractor
78
{
8-
byte[] ExtractClaimUid(IIdentity identity);
9+
string ExtractClaimUid(ClaimsIdentity identity);
910
}
1011
}

0 commit comments

Comments
 (0)