public void GetCookieToken_CookieIsMissingInRequest_LooksUpCookieInAntiforgeryContext() { // Arrange var requestCookies = new Mock<IReadableStringCollection>(); requestCookies .Setup(o => o[It.IsAny<string>()]) .Returns(string.Empty); var mockHttpContext = new Mock<HttpContext>(); mockHttpContext .Setup(o => o.Request.Cookies) .Returns(requestCookies.Object); var contextAccessor = new DefaultAntiforgeryContextAccessor(); mockHttpContext.SetupGet(o => o.RequestServices) .Returns(GetServiceProvider(contextAccessor)); // add a cookie explicitly. var cookie = new AntiforgeryToken(); contextAccessor.Value = new AntiforgeryContext() { CookieToken = cookie }; var options = new AntiforgeryOptions() { CookieName = _cookieName }; var tokenStore = new DefaultAntiforgeryTokenStore( optionsAccessor: new TestOptionsManager(options), tokenSerializer: Mock.Of<IAntiforgeryTokenSerializer>()); // Act var token = tokenStore.GetCookieToken(mockHttpContext.Object); // Assert Assert.Equal(cookie, token); }
public void ValidateTokens_FieldTokenMissing() { // Arrange var httpContext = new DefaultHttpContext(); httpContext.User = new ClaimsPrincipal(new ClaimsIdentity()); var sessionToken = new AntiforgeryToken() { IsSessionToken = true }; var options = new AntiforgeryOptions() { FormFieldName = "my-form-field-name" }; var tokenProvider = new DefaultAntiforgeryTokenGenerator( optionsAccessor: new TestOptionsManager(options), claimUidExtractor: null, additionalDataProvider: null); // Act & assert var ex = Assert.Throws <InvalidOperationException>( () => tokenProvider.ValidateTokens(httpContext, sessionToken, null)); Assert.Equal(@"The required antiforgery form field ""my-form-field-name"" is not present.", ex.Message); }
public void SaveCookieToken(HttpContext httpContext, AntiforgeryToken token) { // Add the cookie to the request based context. // This is useful if the cookie needs to be reloaded in the context of the same request. var services = httpContext.RequestServices; var contextAccessor = services.GetRequiredService <IAntiforgeryContextAccessor>(); Debug.Assert(contextAccessor.Value == null, "AntiforgeryContext should be set only once per request."); contextAccessor.Value = new AntiforgeryContext() { CookieToken = token }; var serializedToken = _tokenSerializer.Serialize(token); var options = new CookieOptions() { HttpOnly = true }; // Note: don't use "newCookie.Secure = _options.RequireSSL;" since the default // value of newCookie.Secure is poulated out of band. if (_options.RequireSsl) { options.Secure = true; } httpContext.Response.Cookies.Append(_options.CookieName, serializedToken, options); }
public void ValidateTokens_Success_ClaimsBasedUser() { // Arrange var httpContext = new DefaultHttpContext(); var identity = GetAuthenticatedIdentity("the-user"); httpContext.User = new ClaimsPrincipal(identity); var sessionToken = new AntiforgeryToken() { IsSessionToken = true }; var fieldtoken = new AntiforgeryToken() { SecurityToken = sessionToken.SecurityToken, IsSessionToken = false, ClaimUid = new BinaryBlob(256) }; var mockClaimUidExtractor = new Mock <IClaimUidExtractor>(); mockClaimUidExtractor.Setup(o => o.ExtractClaimUid(identity)) .Returns(Convert.ToBase64String(fieldtoken.ClaimUid.GetData())); var tokenProvider = new DefaultAntiforgeryTokenGenerator( claimUidExtractor: mockClaimUidExtractor.Object, additionalDataProvider: null); // Act tokenProvider.ValidateTokens(httpContext, sessionToken, fieldtoken); // Assert // Nothing to assert - if we got this far, success! }
public void ValidateTokens_FieldAndSessionTokensHaveDifferentSecurityKeys() { // Arrange var httpContext = new DefaultHttpContext(); httpContext.User = new ClaimsPrincipal(new ClaimsIdentity()); var sessionToken = new AntiforgeryToken() { IsSessionToken = true }; var fieldtoken = new AntiforgeryToken() { IsSessionToken = false }; var tokenProvider = new DefaultAntiforgeryTokenGenerator( claimUidExtractor: null, additionalDataProvider: null); // Act & Assert var exception = Assert.Throws <InvalidOperationException>( () => tokenProvider.ValidateTokens(httpContext, sessionToken, fieldtoken)); Assert.Equal( @"The antiforgery cookie token and form field token do not match.", exception.Message); }
public void ValidateTokens_AdditionalDataRejected() { // Arrange var httpContext = new DefaultHttpContext(); var identity = new ClaimsIdentity(); httpContext.User = new ClaimsPrincipal(identity); var sessionToken = new AntiforgeryToken() { IsSessionToken = true }; var fieldtoken = new AntiforgeryToken() { SecurityToken = sessionToken.SecurityToken, Username = String.Empty, IsSessionToken = false, AdditionalData = "some-additional-data" }; var mockAdditionalDataProvider = new Mock <IAntiforgeryAdditionalDataProvider>(); mockAdditionalDataProvider.Setup(o => o.ValidateAdditionalData(httpContext, "some-additional-data")) .Returns(false); var tokenProvider = new DefaultAntiforgeryTokenGenerator( claimUidExtractor: null, additionalDataProvider: mockAdditionalDataProvider.Object); // Act & assert var exception = Assert.Throws <InvalidOperationException>( () => tokenProvider.ValidateTokens(httpContext, sessionToken, fieldtoken)); Assert.Equal(@"The provided antiforgery token failed a custom data check.", exception.Message); }
public void ValidateTokens_Success_AuthenticatedUserWithUsername() { // Arrange var httpContext = new DefaultHttpContext(); var identity = GetAuthenticatedIdentity("the-user"); httpContext.User = new ClaimsPrincipal(identity); var sessionToken = new AntiforgeryToken() { IsSessionToken = true }; var fieldtoken = new AntiforgeryToken() { SecurityToken = sessionToken.SecurityToken, Username = "******", IsSessionToken = false, AdditionalData = "some-additional-data" }; var mockAdditionalDataProvider = new Mock <IAntiforgeryAdditionalDataProvider>(); mockAdditionalDataProvider.Setup(o => o.ValidateAdditionalData(httpContext, "some-additional-data")) .Returns(true); var tokenProvider = new DefaultAntiforgeryTokenGenerator( claimUidExtractor: new Mock <IClaimUidExtractor>().Object, additionalDataProvider: mockAdditionalDataProvider.Object); // Act tokenProvider.ValidateTokens(httpContext, sessionToken, fieldtoken); // Assert // Nothing to assert - if we got this far, success! }
public void GenerateFormToken_AnonymousUser() { // Arrange var cookieToken = new AntiforgeryToken() { IsSessionToken = true }; 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 var fieldToken = tokenProvider.GenerateFormToken(httpContext, cookieToken); // Assert Assert.NotNull(fieldToken); Assert.Equal(cookieToken.SecurityToken, fieldToken.SecurityToken); Assert.False(fieldToken.IsSessionToken); Assert.Empty(fieldToken.Username); Assert.Null(fieldToken.ClaimUid); Assert.Empty(fieldToken.AdditionalData); }
public void Serialize_FieldToken_WithClaimUid_TokenRoundTripSuccessful() { // Arrange var testSerializer = new DefaultAntiforgeryTokenSerializer(_dataProtector.Object); //"01" // Version //+ "705EEDCC7D42F1D6B3B98A593625BB4C" // SecurityToken //+ "00" // IsSessionToken //+ "01" // IsClaimsBased //+ "6F1648E97249AA58754036A67E248CF044F07ECFB0ED387556CE029A4F9A40E0" // ClaimUid //+ "05" // AdditionalData length header //+ "E282AC3437"; // AdditionalData ("€47") as UTF8 var token = new AntiforgeryToken() { SecurityToken = _securityToken, IsSessionToken = false, ClaimUid = _claimUid, AdditionalData = "€47" }; // Act var actualSerializedData = testSerializer.Serialize(token); var deserializedToken = testSerializer.Deserialize(actualSerializedData); // Assert AssertTokensEqual(token, deserializedToken); _dataProtector.Verify(); }
public void GetCookieToken_CookieIsValid_ReturnsToken() { // Arrange var expectedToken = new AntiforgeryToken(); var mockHttpContext = GetMockHttpContext(_cookieName, "valid-value"); var mockSerializer = new Mock <IAntiforgeryTokenSerializer>(); mockSerializer .Setup(o => o.Deserialize("valid-value")) .Returns(expectedToken); var options = new AntiforgeryOptions() { CookieName = _cookieName }; var tokenStore = new DefaultAntiforgeryTokenStore( optionsAccessor: new TestOptionsManager(options), tokenSerializer: mockSerializer.Object); // Act AntiforgeryToken retVal = tokenStore.GetCookieToken(mockHttpContext); // Assert Assert.Same(expectedToken, retVal); }
public void GenerateFormToken_AuthenticatedWithoutUsername_WithAdditionalData() { // Arrange var cookieToken = new AntiforgeryToken() { IsSessionToken = true }; var httpContext = new DefaultHttpContext(); httpContext.User = new ClaimsPrincipal(new MyAuthenticatedIdentityWithoutUsername()); var mockAdditionalDataProvider = new Mock <IAntiforgeryAdditionalDataProvider>(); mockAdditionalDataProvider.Setup(o => o.GetAdditionalData(httpContext)) .Returns("additional-data"); var claimUidExtractor = new Mock <IClaimUidExtractor>().Object; var tokenProvider = new DefaultAntiforgeryTokenGenerator( claimUidExtractor: claimUidExtractor, additionalDataProvider: mockAdditionalDataProvider.Object); // Act var fieldToken = tokenProvider.GenerateFormToken(httpContext, cookieToken); // Assert Assert.NotNull(fieldToken); Assert.Equal(cookieToken.SecurityToken, fieldToken.SecurityToken); Assert.False(fieldToken.IsSessionToken); Assert.Empty(fieldToken.Username); Assert.Null(fieldToken.ClaimUid); Assert.Equal("additional-data", fieldToken.AdditionalData); }
public void GenerateFormToken_RegularUserWithUsername() { // Arrange var cookieToken = new AntiforgeryToken() { IsSessionToken = true }; var httpContext = new DefaultHttpContext(); var mockIdentity = new Mock <ClaimsIdentity>(); mockIdentity.Setup(o => o.IsAuthenticated) .Returns(true); mockIdentity.Setup(o => o.Name) .Returns("my-username"); httpContext.User = new ClaimsPrincipal(mockIdentity.Object); var claimUidExtractor = new Mock <IClaimUidExtractor>().Object; var tokenProvider = new DefaultAntiforgeryTokenGenerator( claimUidExtractor: claimUidExtractor, additionalDataProvider: null); // Act var fieldToken = tokenProvider.GenerateFormToken(httpContext, cookieToken); // Assert Assert.NotNull(fieldToken); Assert.Equal(cookieToken.SecurityToken, fieldToken.SecurityToken); Assert.False(fieldToken.IsSessionToken); Assert.Equal("my-username", fieldToken.Username); Assert.Null(fieldToken.ClaimUid); Assert.Empty(fieldToken.AdditionalData); }
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(WebEncoders.Base64UrlEncode(_cryptoSystem.Protect(stream.ToArray()))); } } }
public void GenerateFormToken_AuthenticatedWithoutUsernameAndNoAdditionalData_NoAdditionalData() { // Arrange var cookieToken = new AntiforgeryToken() { IsSessionToken = true }; var httpContext = new DefaultHttpContext(); httpContext.User = new ClaimsPrincipal(new MyAuthenticatedIdentityWithoutUsername()); var options = new AntiforgeryOptions(); var claimUidExtractor = new Mock <IClaimUidExtractor>().Object; var tokenProvider = new DefaultAntiforgeryTokenGenerator( claimUidExtractor: claimUidExtractor, additionalDataProvider: null); // Act & assert var exception = Assert.Throws <InvalidOperationException>( () => tokenProvider.GenerateFormToken(httpContext, cookieToken)); Assert.Equal( "The provided identity of type " + $"'{typeof(MyAuthenticatedIdentityWithoutUsername).FullName}' " + "is marked IsAuthenticated = true but does not have a value for Name. " + "By default, the antiforgery system requires that all authenticated identities have a unique Name. " + "If it is not possible to provide a unique Name for this identity, " + "consider extending IAntiforgeryAdditionalDataProvider by overriding the " + "DefaultAntiforgeryAdditionalDataProvider " + "or a custom type that can provide some form of unique identifier for the current user.", exception.Message); }
public void GenerateFormToken_AuthenticatedWithoutUsernameAndNoAdditionalData_NoAdditionalData() { // Arrange var cookieToken = new AntiforgeryToken() { IsSessionToken = true }; var httpContext = new DefaultHttpContext(); httpContext.User = new ClaimsPrincipal(new MyAuthenticatedIdentityWithoutUsername()); var options = new AntiforgeryOptions(); var claimUidExtractor = new Mock<IClaimUidExtractor>().Object; var tokenProvider = new DefaultAntiforgeryTokenGenerator( claimUidExtractor: claimUidExtractor, additionalDataProvider: null); // Act & assert var exception = Assert.Throws<InvalidOperationException>( () => tokenProvider.GenerateFormToken(httpContext, cookieToken)); Assert.Equal( "The provided identity of type " + $"'{typeof(MyAuthenticatedIdentityWithoutUsername).FullName}' " + "is marked IsAuthenticated = true but does not have a value for Name. " + "By default, the antiforgery system requires that all authenticated identities have a unique Name. " + "If it is not possible to provide a unique Name for this identity, " + "consider extending IAntiforgeryAdditionalDataProvider by overriding the " + "DefaultAntiforgeryAdditionalDataProvider " + "or a custom type that can provide some form of unique identifier for the current user.", exception.Message); }
public void Serialize_FieldToken_WithUsername_TokenRoundTripSuccessful() { // Arrange var testSerializer = new DefaultAntiforgeryTokenSerializer(_dataProtector.Object); //"01" // Version //+ "705EEDCC7D42F1D6B3B98A593625BB4C" // SecurityToken //+ "00" // IsSessionToken //+ "00" // IsClaimsBased //+ "08" // Username length header //+ "4AC3A972C3B46D65" // Username ("Jérôme") as UTF8 //+ "05" // AdditionalData length header //+ "E282AC3437"; // AdditionalData ("€47") as UTF8 var token = new AntiforgeryToken() { SecurityToken = _securityToken, IsSessionToken = false, Username = "******", AdditionalData = "€47" }; // Act var actualSerializedData = testSerializer.Serialize(token); var deserializedToken = testSerializer.Deserialize(actualSerializedData); // Assert AssertTokensEqual(token, deserializedToken); _dataProtector.Verify(); }
public void Serialize_FieldToken_WithClaimUid_TokenRoundTripSuccessful() { // Arrange var testSerializer = new DefaultAntiforgeryTokenSerializer(_dataProtector.Object); //"01" // Version //+ "705EEDCC7D42F1D6B3B98A593625BB4C" // SecurityToken //+ "00" // IsSessionToken //+ "01" // IsClaimsBased //+ "6F1648E97249AA58754036A67E248CF044F07ECFB0ED387556CE029A4F9A40E0" // ClaimUid //+ "05" // AdditionalData length header //+ "E282AC3437"; // AdditionalData ("€47") as UTF8 var token = new AntiforgeryToken() { SecurityToken = _securityToken, IsSessionToken = false, ClaimUid = _claimUid, AdditionalData = "€47" }; // Act var actualSerializedData = testSerializer.Serialize(token); var deserializedToken = testSerializer.Deserialize(actualSerializedData); // Assert AssertTokensEqual(token, deserializedToken); _dataProtector.Verify(); }
private static void AssertTokensEqual(AntiforgeryToken expected, AntiforgeryToken actual) { Assert.NotNull(expected); Assert.NotNull(actual); Assert.Equal(expected.AdditionalData, actual.AdditionalData); Assert.Equal(expected.ClaimUid, actual.ClaimUid); Assert.Equal(expected.IsSessionToken, actual.IsSessionToken); Assert.Equal(expected.SecurityToken, actual.SecurityToken); Assert.Equal(expected.Username, actual.Username); }
public AntiforgeryToken GenerateFormToken( HttpContext httpContext, AntiforgeryToken cookieToken) { if (httpContext == null) { throw new ArgumentNullException(nameof(httpContext)); } Debug.Assert(IsCookieTokenValid(cookieToken)); var formToken = new AntiforgeryToken() { SecurityToken = cookieToken.SecurityToken, IsSessionToken = false }; var isIdentityAuthenticated = false; var identity = httpContext.User?.Identity as ClaimsIdentity; // populate Username and ClaimUid if (identity != null && identity.IsAuthenticated) { isIdentityAuthenticated = true; formToken.ClaimUid = GetClaimUidBlob(_claimUidExtractor.ExtractClaimUid(identity)); if (formToken.ClaimUid == null) { formToken.Username = identity.Name; } } // populate AdditionalData if (_additionalDataProvider != null) { formToken.AdditionalData = _additionalDataProvider.GetAdditionalData(httpContext); } if (isIdentityAuthenticated && string.IsNullOrEmpty(formToken.Username) && formToken.ClaimUid == null && string.IsNullOrEmpty(formToken.AdditionalData)) { // Application says user is authenticated, but we have no identifier for the user. throw new InvalidOperationException( Resources.FormatAntiforgeryTokenValidator_AuthenticatedUserWithoutUsername( identity.GetType(), nameof(IIdentity.IsAuthenticated), "true", nameof(IIdentity.Name), nameof(IAntiforgeryAdditionalDataProvider), nameof(DefaultAntiforgeryAdditionalDataProvider))); } return formToken; }
public AntiforgeryToken GenerateFormToken( HttpContext httpContext, AntiforgeryToken cookieToken) { if (httpContext == null) { throw new ArgumentNullException(nameof(httpContext)); } Debug.Assert(IsCookieTokenValid(cookieToken)); var formToken = new AntiforgeryToken() { SecurityToken = cookieToken.SecurityToken, IsSessionToken = false }; var isIdentityAuthenticated = false; var identity = httpContext.User?.Identity as ClaimsIdentity; // populate Username and ClaimUid if (identity != null && identity.IsAuthenticated) { isIdentityAuthenticated = true; formToken.ClaimUid = GetClaimUidBlob(_claimUidExtractor.ExtractClaimUid(identity)); if (formToken.ClaimUid == null) { formToken.Username = identity.Name; } } // populate AdditionalData if (_additionalDataProvider != null) { formToken.AdditionalData = _additionalDataProvider.GetAdditionalData(httpContext); } if (isIdentityAuthenticated && string.IsNullOrEmpty(formToken.Username) && formToken.ClaimUid == null && string.IsNullOrEmpty(formToken.AdditionalData)) { // Application says user is authenticated, but we have no identifier for the user. throw new InvalidOperationException( Resources.FormatAntiforgeryTokenValidator_AuthenticatedUserWithoutUsername( identity.GetType(), nameof(IIdentity.IsAuthenticated), "true", nameof(IIdentity.Name), nameof(IAntiforgeryAdditionalDataProvider), nameof(DefaultAntiforgeryAdditionalDataProvider))); } return(formToken); }
public string Serialize(AntiforgeryToken token) { if (token.IsSessionToken) { return TestAntiforgeryTokenGenerator.CookieString; } else { return TestAntiforgeryTokenGenerator.FieldString; } }
// This method returns null if oldCookieToken is valid. private AntiforgeryToken ValidateAndGenerateNewCookieToken(AntiforgeryToken cookieToken) { if (!_tokenGenerator.IsCookieTokenValid(cookieToken)) { // Need to make sure we're always operating with a good cookie token. var newCookieToken = _tokenGenerator.GenerateCookieToken(); Debug.Assert(_tokenGenerator.IsCookieTokenValid(newCookieToken)); return(newCookieToken); } return(null); }
public void SecurityTokenProperty_PropertySetter_DoesNotUseDefaults() { // Arrange var token = new AntiforgeryToken(); // Act var securityToken = new BinaryBlob(64); token.SecurityToken = securityToken; // Assert Assert.Equal(securityToken, token.SecurityToken); }
public void IsCookieTokenValid_NullToken_ReturnsFalse() { // Arrange AntiforgeryToken cookieToken = null; var tokenProvider = new DefaultAntiforgeryTokenGenerator( claimUidExtractor: null, additionalDataProvider: null); // Act var isValid = tokenProvider.IsCookieTokenValid(cookieToken); // Assert Assert.False(isValid); }
public void SecurityTokenProperty_GetsAutopopulated() { // Arrange var token = new AntiforgeryToken(); // Act var securityToken = token.SecurityToken; // Assert Assert.NotNull(securityToken); Assert.Equal(AntiforgeryToken.SecurityTokenBitLength, securityToken.BitLength); // check that we're not making a new one each property call Assert.Equal(securityToken, token.SecurityToken); }
public void AdditionalDataProperty() { // Arrange var token = new AntiforgeryToken(); // Act & assert - 1 Assert.Equal("", token.AdditionalData); // Act & assert - 2 token.AdditionalData = "additional data"; Assert.Equal("additional data", token.AdditionalData); // Act & assert - 3 token.AdditionalData = null; Assert.Equal("", token.AdditionalData); }
public void SecurityTokenProperty_PropertySetter_DoesNotAllowNulls() { // Arrange var token = new AntiforgeryToken(); // Act token.SecurityToken = null; var securityToken = token.SecurityToken; // Assert Assert.NotNull(securityToken); Assert.Equal(AntiforgeryToken.SecurityTokenBitLength, securityToken.BitLength); // check that we're not making a new one each property call Assert.Equal(securityToken, token.SecurityToken); }
public void IsSessionTokenProperty() { // Arrange var token = new AntiforgeryToken(); // Act & assert - 1 Assert.False(token.IsSessionToken); // Act & assert - 2 token.IsSessionToken = true; Assert.True(token.IsSessionToken); // Act & assert - 3 token.IsSessionToken = false; Assert.False(token.IsSessionToken); }
public void UsernameProperty() { // Arrange var token = new AntiforgeryToken(); // Act & assert - 1 Assert.Equal("", token.Username); // Act & assert - 2 token.Username = "******"; Assert.Equal("my username", token.Username); // Act & assert - 3 token.Username = null; Assert.Equal("", token.Username); }
public void ValidateTokens_FieldAndSessionTokensSwapped() { // Arrange var httpContext = new DefaultHttpContext(); httpContext.User = new ClaimsPrincipal(new ClaimsIdentity()); var sessionToken = new AntiforgeryToken() { IsSessionToken = true }; var fieldtoken = new AntiforgeryToken() { IsSessionToken = false }; var options = new AntiforgeryOptions() { CookieName = "my-cookie-name", FormFieldName = "my-form-field-name" }; var tokenProvider = new DefaultAntiforgeryTokenGenerator( optionsAccessor: new TestOptionsManager(options), claimUidExtractor: null, additionalDataProvider: null); // Act & assert var ex1 = Assert.Throws <InvalidOperationException>( () => tokenProvider.ValidateTokens(httpContext, fieldtoken, fieldtoken)); Assert.Equal( "Validation of the provided antiforgery token failed. " + @"The cookie ""my-cookie-name"" and the form field ""my-form-field-name"" were swapped.", ex1.Message); var ex2 = Assert.Throws <InvalidOperationException>( () => tokenProvider.ValidateTokens(httpContext, sessionToken, sessionToken)); Assert.Equal( "Validation of the provided antiforgery token failed. " + @"The cookie ""my-cookie-name"" and the form field ""my-form-field-name"" were swapped.", ex2.Message); }
public void SaveCookieToken(bool requireSsl, bool?expectedCookieSecureFlag) { // Arrange var token = new AntiforgeryToken(); var mockCookies = new Mock <IResponseCookies>(); bool defaultCookieSecureValue = expectedCookieSecureFlag ?? false; // pulled from config; set by ctor var cookies = new MockResponseCookieCollection(); cookies.Count = 0; var mockHttpContext = new Mock <HttpContext>(); mockHttpContext.Setup(o => o.Response.Cookies) .Returns(cookies); var contextAccessor = new DefaultAntiforgeryContextAccessor(); mockHttpContext.SetupGet(o => o.RequestServices) .Returns(GetServiceProvider(contextAccessor)); var mockSerializer = new Mock <IAntiforgeryTokenSerializer>(); mockSerializer.Setup(o => o.Serialize(token)) .Returns("serialized-value"); var options = new AntiforgeryOptions() { CookieName = _cookieName, RequireSsl = requireSsl }; var tokenStore = new DefaultAntiforgeryTokenStore( optionsAccessor: new TestOptionsManager(options), tokenSerializer: mockSerializer.Object); // Act tokenStore.SaveCookieToken(mockHttpContext.Object, token); // Assert Assert.Equal(1, cookies.Count); Assert.NotNull(contextAccessor.Value.CookieToken); Assert.NotNull(cookies); Assert.Equal(_cookieName, cookies.Key); Assert.Equal("serialized-value", cookies.Value); Assert.True(cookies.Options.HttpOnly); Assert.Equal(defaultCookieSecureValue, cookies.Options.Secure); }
public void IsCookieTokenValid_FieldToken_ReturnsFalse() { // Arrange var cookieToken = new AntiforgeryToken() { IsSessionToken = false }; var tokenProvider = new DefaultAntiforgeryTokenGenerator( claimUidExtractor: null, additionalDataProvider: null); // Act bool retVal = tokenProvider.IsCookieTokenValid(cookieToken); // Assert Assert.False(retVal); }
public void IsCookieTokenValid_ValidToken_ReturnsTrue() { // Arrange var cookieToken = new AntiforgeryToken() { IsSessionToken = true }; var tokenProvider = new DefaultAntiforgeryTokenGenerator( claimUidExtractor: null, additionalDataProvider: null); // Act var isValid = tokenProvider.IsCookieTokenValid(cookieToken); // Assert Assert.True(isValid); }
private void SaveCookieTokenAndHeader( [NotNull] HttpContext context, AntiforgeryToken cookieToken) { if (cookieToken != null) { // Persist the new cookie if it is not null. _tokenStore.SaveCookieToken(context, cookieToken); } if (!_options.SuppressXFrameOptionsHeader) { // Adding X-Frame-Options header to prevent ClickJacking. See // http://tools.ietf.org/html/draft-ietf-websec-x-frame-options-10 // for more information. context.Response.Headers.Set("X-Frame-Options", "SAMEORIGIN"); } }
public void ClaimUidProperty() { // Arrange var token = new AntiforgeryToken(); // Act & assert - 1 Assert.Null(token.ClaimUid); // Act & assert - 2 BinaryBlob blob = new BinaryBlob(32); token.ClaimUid = blob; Assert.Equal(blob, token.ClaimUid); // Act & assert - 3 token.ClaimUid = null; Assert.Null(token.ClaimUid); }
public void ValidateTokens(HttpContext httpContext, AntiforgeryToken sessionToken, AntiforgeryToken fieldToken) { if (httpContext == null) { throw new ArgumentNullException(nameof(httpContext)); } if (sessionToken == null) { throw new ArgumentNullException( nameof(sessionToken), "Session token must be provided"); } if (fieldToken == null) { throw new ArgumentNullException( nameof(fieldToken), "Field token must be provided"); } // Do the tokens have the correct format? if (!sessionToken.IsSessionToken || fieldToken.IsSessionToken) { throw new InvalidOperationException("Security tokens swapped"); } var bytes = Encoding.UTF8.GetBytes(CookieString); var cookieTokenValidation = new BinaryBlob(bytes.Length * 8, bytes); if (!cookieTokenValidation.Equals(sessionToken.SecurityToken)) { throw new InvalidOperationException("Invalid cookie token"); } bytes = Encoding.UTF8.GetBytes(FieldString); var headerTokenValidation = new BinaryBlob(bytes.Length * 8, bytes); if (!headerTokenValidation.Equals(fieldToken.SecurityToken)) { throw new InvalidOperationException("Invalid field token"); } }
public void IsCookieTokenValid_FieldToken_ReturnsFalse() { // Arrange var cookieToken = new AntiforgeryToken() { IsSessionToken = false }; var tokenProvider = new DefaultAntiforgeryTokenGenerator( optionsAccessor: new TestOptionsManager(), claimUidExtractor: null, additionalDataProvider: null); // Act bool retVal = tokenProvider.IsCookieTokenValid(cookieToken); // Assert Assert.False(retVal); }
/* 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) { var 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 void SaveCookieToken(HttpContext httpContext, AntiforgeryToken token) { // Add the cookie to the request based context. // This is useful if the cookie needs to be reloaded in the context of the same request. var services = httpContext.RequestServices; var contextAccessor = services.GetRequiredService<IAntiforgeryContextAccessor>(); Debug.Assert(contextAccessor.Value == null, "AntiforgeryContext should be set only once per request."); contextAccessor.Value = new AntiforgeryContext() { CookieToken = token }; var serializedToken = _tokenSerializer.Serialize(token); var options = new CookieOptions() { HttpOnly = true }; // Note: don't use "newCookie.Secure = _options.RequireSSL;" since the default // value of newCookie.Secure is poulated out of band. if (_options.RequireSsl) { options.Secure = true; } httpContext.Response.Cookies.Append(_options.CookieName, serializedToken, options); }
public void GenerateFormToken_AnonymousUser() { // Arrange var cookieToken = new AntiforgeryToken() { IsSessionToken = true }; 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 var fieldToken = tokenProvider.GenerateFormToken(httpContext, cookieToken); // Assert Assert.NotNull(fieldToken); Assert.Equal(cookieToken.SecurityToken, fieldToken.SecurityToken); Assert.False(fieldToken.IsSessionToken); Assert.Empty(fieldToken.Username); Assert.Null(fieldToken.ClaimUid); Assert.Empty(fieldToken.AdditionalData); }
private static void AssertTokensEqual(AntiforgeryToken expected, AntiforgeryToken actual) { Assert.NotNull(expected); Assert.NotNull(actual); Assert.Equal(expected.AdditionalData, actual.AdditionalData); Assert.Equal(expected.ClaimUid, actual.ClaimUid); Assert.Equal(expected.IsSessionToken, actual.IsSessionToken); Assert.Equal(expected.SecurityToken, actual.SecurityToken); Assert.Equal(expected.Username, actual.Username); }
public void ValidateTokens_FieldTokenMissing() { // Arrange var httpContext = new DefaultHttpContext(); httpContext.User = new ClaimsPrincipal(new ClaimsIdentity()); var sessionToken = new AntiforgeryToken() { IsSessionToken = true }; var tokenProvider = new DefaultAntiforgeryTokenGenerator( claimUidExtractor: null, additionalDataProvider: null); // Act & assert var ex = Assert.Throws<ArgumentNullException>( () => tokenProvider.ValidateTokens(httpContext, sessionToken, null)); var trimmed = ex.Message.Substring(0, ex.Message.IndexOf(Environment.NewLine)); Assert.Equal("The form token must be provided.", trimmed); }
public void ValidateTokens_FieldAndSessionTokensSwapped() { // Arrange var httpContext = new DefaultHttpContext(); httpContext.User = new ClaimsPrincipal(new ClaimsIdentity()); var sessionToken = new AntiforgeryToken() { IsSessionToken = true }; var fieldtoken = new AntiforgeryToken() { IsSessionToken = false }; var tokenProvider = new DefaultAntiforgeryTokenGenerator( claimUidExtractor: null, additionalDataProvider: null); // Act & assert var ex1 = Assert.Throws<InvalidOperationException>( () => tokenProvider.ValidateTokens(httpContext, fieldtoken, fieldtoken)); Assert.Equal( "Validation of the provided antiforgery token failed. " + @"The cookie token and the form token were swapped.", ex1.Message); var ex2 = Assert.Throws<InvalidOperationException>( () => tokenProvider.ValidateTokens(httpContext, sessionToken, sessionToken)); Assert.Equal( "Validation of the provided antiforgery token failed. " + @"The cookie token and the form token were swapped.", ex2.Message); }
public void GenerateFormToken_RegularUserWithUsername() { // Arrange var cookieToken = new AntiforgeryToken() { IsSessionToken = true }; var httpContext = new DefaultHttpContext(); var mockIdentity = new Mock<ClaimsIdentity>(); mockIdentity.Setup(o => o.IsAuthenticated) .Returns(true); mockIdentity.Setup(o => o.Name) .Returns("my-username"); httpContext.User = new ClaimsPrincipal(mockIdentity.Object); var claimUidExtractor = new Mock<IClaimUidExtractor>().Object; var tokenProvider = new DefaultAntiforgeryTokenGenerator( claimUidExtractor: claimUidExtractor, additionalDataProvider: null); // Act var fieldToken = tokenProvider.GenerateFormToken(httpContext, cookieToken); // Assert Assert.NotNull(fieldToken); Assert.Equal(cookieToken.SecurityToken, fieldToken.SecurityToken); Assert.False(fieldToken.IsSessionToken); Assert.Equal("my-username", fieldToken.Username); Assert.Null(fieldToken.ClaimUid); Assert.Empty(fieldToken.AdditionalData); }
public void IsCookieTokenValid_ValidToken_ReturnsTrue() { // Arrange var cookieToken = new AntiforgeryToken() { IsSessionToken = true }; var tokenProvider = new DefaultAntiforgeryTokenGenerator( claimUidExtractor: null, additionalDataProvider: null); // Act var isValid = tokenProvider.IsCookieTokenValid(cookieToken); // Assert Assert.True(isValid); }
public void Serialize_SessionToken_TokenRoundTripSuccessful() { // Arrange var testSerializer = new DefaultAntiforgeryTokenSerializer(_dataProtector.Object); //"01" // Version //+ "705EEDCC7D42F1D6B3B98A593625BB4C" // SecurityToken //+ "01"; // IsSessionToken var token = new AntiforgeryToken() { SecurityToken = _securityToken, IsSessionToken = true }; // Act string actualSerializedData = testSerializer.Serialize(token); var deserializedToken = testSerializer.Deserialize(actualSerializedData); // Assert AssertTokensEqual(token, deserializedToken); _dataProtector.Verify(); }
public void GenerateFormToken_ClaimsBasedIdentity() { // Arrange var cookieToken = new AntiforgeryToken() { IsSessionToken = true }; var identity = GetAuthenticatedIdentity("some-identity"); var httpContext = new DefaultHttpContext(); httpContext.User = new ClaimsPrincipal(identity); byte[] data = new byte[256 / 8]; using (var rng = RandomNumberGenerator.Create()) { rng.GetBytes(data); } var base64ClaimUId = Convert.ToBase64String(data); var expectedClaimUid = new BinaryBlob(256, data); var mockClaimUidExtractor = new Mock<IClaimUidExtractor>(); mockClaimUidExtractor.Setup(o => o.ExtractClaimUid(identity)) .Returns(base64ClaimUId); var tokenProvider = new DefaultAntiforgeryTokenGenerator( claimUidExtractor: mockClaimUidExtractor.Object, additionalDataProvider: null); // Act var fieldToken = tokenProvider.GenerateFormToken(httpContext, cookieToken); // Assert Assert.NotNull(fieldToken); Assert.Equal(cookieToken.SecurityToken, fieldToken.SecurityToken); Assert.False(fieldToken.IsSessionToken); Assert.Equal("", fieldToken.Username); Assert.Equal(expectedClaimUid, fieldToken.ClaimUid); Assert.Equal("", fieldToken.AdditionalData); }
public void ValidateTokens_Success_AuthenticatedUserWithUsername() { // Arrange var httpContext = new DefaultHttpContext(); var identity = GetAuthenticatedIdentity("the-user"); httpContext.User = new ClaimsPrincipal(identity); var sessionToken = new AntiforgeryToken() { IsSessionToken = true }; var fieldtoken = new AntiforgeryToken() { SecurityToken = sessionToken.SecurityToken, Username = "******", IsSessionToken = false, AdditionalData = "some-additional-data" }; var mockAdditionalDataProvider = new Mock<IAntiforgeryAdditionalDataProvider>(); mockAdditionalDataProvider.Setup(o => o.ValidateAdditionalData(httpContext, "some-additional-data")) .Returns(true); var tokenProvider = new DefaultAntiforgeryTokenGenerator( claimUidExtractor: new Mock<IClaimUidExtractor>().Object, additionalDataProvider: mockAdditionalDataProvider.Object); // Act tokenProvider.ValidateTokens(httpContext, sessionToken, fieldtoken); // Assert // Nothing to assert - if we got this far, success! }
public void Serialize_FieldToken_WithUsername_TokenRoundTripSuccessful() { // Arrange var testSerializer = new DefaultAntiforgeryTokenSerializer(_dataProtector.Object); //"01" // Version //+ "705EEDCC7D42F1D6B3B98A593625BB4C" // SecurityToken //+ "00" // IsSessionToken //+ "00" // IsClaimsBased //+ "08" // Username length header //+ "4AC3A972C3B46D65" // Username ("Jérôme") as UTF8 //+ "05" // AdditionalData length header //+ "E282AC3437"; // AdditionalData ("€47") as UTF8 var token = new AntiforgeryToken() { SecurityToken = _securityToken, IsSessionToken = false, Username = "******", AdditionalData = "€47" }; // Act var actualSerializedData = testSerializer.Serialize(token); var deserializedToken = testSerializer.Deserialize(actualSerializedData); // Assert AssertTokensEqual(token, deserializedToken); _dataProtector.Verify(); }
public void SaveCookieToken(HttpContext httpContext, AntiforgeryToken token) { this.tokenStore.SaveCookieToken(httpContext, token); }
public void ValidateTokens_FieldAndSessionTokensHaveDifferentSecurityKeys() { // Arrange var httpContext = new DefaultHttpContext(); httpContext.User = new ClaimsPrincipal(new ClaimsIdentity()); var sessionToken = new AntiforgeryToken() { IsSessionToken = true }; var fieldtoken = new AntiforgeryToken() { IsSessionToken = false }; var tokenProvider = new DefaultAntiforgeryTokenGenerator( claimUidExtractor: null, additionalDataProvider: null); // Act & Assert var exception = Assert.Throws<InvalidOperationException>( () => tokenProvider.ValidateTokens(httpContext, sessionToken, fieldtoken)); Assert.Equal( @"The antiforgery cookie token and form field token do not match.", exception.Message); }
public void ValidateTokens_ClaimUidMismatch() { // Arrange var httpContext = new DefaultHttpContext(); var identity = GetAuthenticatedIdentity("the-user"); httpContext.User = new ClaimsPrincipal(identity); var sessionToken = new AntiforgeryToken() { IsSessionToken = true }; var fieldtoken = new AntiforgeryToken() { SecurityToken = sessionToken.SecurityToken, IsSessionToken = false, ClaimUid = new BinaryBlob(256) }; var differentToken = new BinaryBlob(256); var mockClaimUidExtractor = new Mock<IClaimUidExtractor>(); mockClaimUidExtractor.Setup(o => o.ExtractClaimUid(identity)) .Returns(Convert.ToBase64String(differentToken.GetData())); var tokenProvider = new DefaultAntiforgeryTokenGenerator( claimUidExtractor: mockClaimUidExtractor.Object, additionalDataProvider: null); // Act & assert var exception = Assert.Throws<InvalidOperationException>( () => tokenProvider.ValidateTokens(httpContext, sessionToken, fieldtoken)); Assert.Equal( @"The provided antiforgery token was meant for a different claims-based user than the current user.", exception.Message); }
public void ValidateTokens_UsernameMismatch(string identityUsername, string embeddedUsername) { // Arrange var httpContext = new DefaultHttpContext(); var identity = GetAuthenticatedIdentity(identityUsername); httpContext.User = new ClaimsPrincipal(identity); var sessionToken = new AntiforgeryToken() { IsSessionToken = true }; var fieldtoken = new AntiforgeryToken() { SecurityToken = sessionToken.SecurityToken, Username = embeddedUsername, IsSessionToken = false }; var mockClaimUidExtractor = new Mock<IClaimUidExtractor>(); mockClaimUidExtractor.Setup(o => o.ExtractClaimUid(identity)) .Returns((string)null); var tokenProvider = new DefaultAntiforgeryTokenGenerator( claimUidExtractor: mockClaimUidExtractor.Object, additionalDataProvider: null); // Act & Assert var exception = Assert.Throws<InvalidOperationException>( () => tokenProvider.ValidateTokens(httpContext, sessionToken, fieldtoken)); Assert.Equal( @"The provided antiforgery token was meant for user """ + embeddedUsername + @""", but the current user is """ + identityUsername + @""".", exception.Message); }
// This method returns null if oldCookieToken is valid. private AntiforgeryToken ValidateAndGenerateNewCookieToken(AntiforgeryToken cookieToken) { if (!_tokenGenerator.IsCookieTokenValid(cookieToken)) { // Need to make sure we're always operating with a good cookie token. var newCookieToken = _tokenGenerator.GenerateCookieToken(); Debug.Assert(_tokenGenerator.IsCookieTokenValid(newCookieToken)); return newCookieToken; } return null; }
public void ValidateTokens_AdditionalDataRejected() { // Arrange var httpContext = new DefaultHttpContext(); var identity = new ClaimsIdentity(); httpContext.User = new ClaimsPrincipal(identity); var sessionToken = new AntiforgeryToken() { IsSessionToken = true }; var fieldtoken = new AntiforgeryToken() { SecurityToken = sessionToken.SecurityToken, Username = String.Empty, IsSessionToken = false, AdditionalData = "some-additional-data" }; var mockAdditionalDataProvider = new Mock<IAntiforgeryAdditionalDataProvider>(); mockAdditionalDataProvider.Setup(o => o.ValidateAdditionalData(httpContext, "some-additional-data")) .Returns(false); var tokenProvider = new DefaultAntiforgeryTokenGenerator( claimUidExtractor: null, additionalDataProvider: mockAdditionalDataProvider.Object); // Act & assert var exception = Assert.Throws<InvalidOperationException>( () => tokenProvider.ValidateTokens(httpContext, sessionToken, fieldtoken)); Assert.Equal(@"The provided antiforgery token failed a custom data check.", exception.Message); }
private void SaveCookieTokenAndHeader( [NotNull] HttpContext context, AntiforgeryToken cookieToken) { if (cookieToken != null) { // Persist the new cookie if it is not null. _tokenStore.SaveCookieToken(context, cookieToken); } if (!_options.SuppressXFrameOptionsHeader) { // Adding X-Frame-Options header to prevent ClickJacking. See // http://tools.ietf.org/html/draft-ietf-websec-x-frame-options-10 // for more information. context.Response.Headers.Set("X-Frame-Options", "SAMEORIGIN"); } }
public void ValidateTokens_Success_ClaimsBasedUser() { // Arrange var httpContext = new DefaultHttpContext(); var identity = GetAuthenticatedIdentity("the-user"); httpContext.User = new ClaimsPrincipal(identity); var sessionToken = new AntiforgeryToken() { IsSessionToken = true }; var fieldtoken = new AntiforgeryToken() { SecurityToken = sessionToken.SecurityToken, IsSessionToken = false, ClaimUid = new BinaryBlob(256) }; var mockClaimUidExtractor = new Mock<IClaimUidExtractor>(); mockClaimUidExtractor.Setup(o => o.ExtractClaimUid(identity)) .Returns(Convert.ToBase64String(fieldtoken.ClaimUid.GetData())); var tokenProvider = new DefaultAntiforgeryTokenGenerator( claimUidExtractor: mockClaimUidExtractor.Object, additionalDataProvider: null); // Act tokenProvider.ValidateTokens(httpContext, sessionToken, fieldtoken); // Assert // Nothing to assert - if we got this far, success! }
public void GetCookieToken_CookieIsValid_ReturnsToken() { // Arrange var expectedToken = new AntiforgeryToken(); var mockHttpContext = GetMockHttpContext(_cookieName, "valid-value"); var mockSerializer = new Mock<IAntiforgeryTokenSerializer>(); mockSerializer .Setup(o => o.Deserialize("valid-value")) .Returns(expectedToken); var options = new AntiforgeryOptions() { CookieName = _cookieName }; var tokenStore = new DefaultAntiforgeryTokenStore( optionsAccessor: new TestOptionsManager(options), tokenSerializer: mockSerializer.Object); // Act AntiforgeryToken retVal = tokenStore.GetCookieToken(mockHttpContext); // Assert Assert.Same(expectedToken, retVal); }
public void GenerateFormToken_AuthenticatedWithoutUsername_WithAdditionalData() { // Arrange var cookieToken = new AntiforgeryToken() { IsSessionToken = true }; var httpContext = new DefaultHttpContext(); httpContext.User = new ClaimsPrincipal(new MyAuthenticatedIdentityWithoutUsername()); var mockAdditionalDataProvider = new Mock<IAntiforgeryAdditionalDataProvider>(); mockAdditionalDataProvider.Setup(o => o.GetAdditionalData(httpContext)) .Returns("additional-data"); var claimUidExtractor = new Mock<IClaimUidExtractor>().Object; var tokenProvider = new DefaultAntiforgeryTokenGenerator( claimUidExtractor: claimUidExtractor, additionalDataProvider: mockAdditionalDataProvider.Object); // Act var fieldToken = tokenProvider.GenerateFormToken(httpContext, cookieToken); // Assert Assert.NotNull(fieldToken); Assert.Equal(cookieToken.SecurityToken, fieldToken.SecurityToken); Assert.False(fieldToken.IsSessionToken); Assert.Empty(fieldToken.Username); Assert.Null(fieldToken.ClaimUid); Assert.Equal("additional-data", fieldToken.AdditionalData); }
public void SaveCookieToken(bool requireSsl, bool? expectedCookieSecureFlag) { // Arrange var token = new AntiforgeryToken(); var mockCookies = new Mock<IResponseCookies>(); bool defaultCookieSecureValue = expectedCookieSecureFlag ?? false; // pulled from config; set by ctor var cookies = new MockResponseCookieCollection(); cookies.Count = 0; var mockHttpContext = new Mock<HttpContext>(); mockHttpContext.Setup(o => o.Response.Cookies) .Returns(cookies); var contextAccessor = new DefaultAntiforgeryContextAccessor(); mockHttpContext.SetupGet(o => o.RequestServices) .Returns(GetServiceProvider(contextAccessor)); var mockSerializer = new Mock<IAntiforgeryTokenSerializer>(); mockSerializer.Setup(o => o.Serialize(token)) .Returns("serialized-value"); var options = new AntiforgeryOptions() { CookieName = _cookieName, RequireSsl = requireSsl }; var tokenStore = new DefaultAntiforgeryTokenStore( optionsAccessor: new TestOptionsManager(options), tokenSerializer: mockSerializer.Object); // Act tokenStore.SaveCookieToken(mockHttpContext.Object, token); // Assert Assert.Equal(1, cookies.Count); Assert.NotNull(contextAccessor.Value.CookieToken); Assert.NotNull(cookies); Assert.Equal(_cookieName, cookies.Key); Assert.Equal("serialized-value", cookies.Value); Assert.True(cookies.Options.HttpOnly); Assert.Equal(defaultCookieSecureValue, cookies.Options.Secure); }