#263 Consume ITlsTokenBindingFeature in CookieAuthMiddleware.
This commit is contained in:
parent
42cba79e01
commit
c14119b612
|
|
@ -8,6 +8,7 @@ using System.Security.Claims;
|
||||||
using System.Threading.Tasks;
|
using System.Threading.Tasks;
|
||||||
using Microsoft.AspNet.Http;
|
using Microsoft.AspNet.Http;
|
||||||
using Microsoft.AspNet.Http.Authentication;
|
using Microsoft.AspNet.Http.Authentication;
|
||||||
|
using Microsoft.AspNet.Http.Features;
|
||||||
using Microsoft.AspNet.Http.Features.Authentication;
|
using Microsoft.AspNet.Http.Features.Authentication;
|
||||||
using Microsoft.Extensions.Logging;
|
using Microsoft.Extensions.Logging;
|
||||||
using Microsoft.Extensions.Primitives;
|
using Microsoft.Extensions.Primitives;
|
||||||
|
|
@ -45,7 +46,7 @@ namespace Microsoft.AspNet.Authentication.Cookies
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
var ticket = Options.TicketDataFormat.Unprotect(cookie);
|
var ticket = Options.TicketDataFormat.Unprotect(cookie, GetTlsTokenBinding());
|
||||||
if (ticket == null)
|
if (ticket == null)
|
||||||
{
|
{
|
||||||
Logger.LogWarning(@"Unprotect ticket failed");
|
Logger.LogWarning(@"Unprotect ticket failed");
|
||||||
|
|
@ -175,7 +176,7 @@ namespace Microsoft.AspNet.Authentication.Cookies
|
||||||
ticket = new AuthenticationTicket(principal, null, Options.AuthenticationScheme);
|
ticket = new AuthenticationTicket(principal, null, Options.AuthenticationScheme);
|
||||||
}
|
}
|
||||||
|
|
||||||
var cookieValue = Options.TicketDataFormat.Protect(ticket);
|
var cookieValue = Options.TicketDataFormat.Protect(ticket, GetTlsTokenBinding());
|
||||||
|
|
||||||
var cookieOptions = BuildCookieOptions();
|
var cookieOptions = BuildCookieOptions();
|
||||||
if (ticket.Properties.IsPersistent && _renewExpiresUtc.HasValue)
|
if (ticket.Properties.IsPersistent && _renewExpiresUtc.HasValue)
|
||||||
|
|
@ -244,7 +245,7 @@ namespace Microsoft.AspNet.Authentication.Cookies
|
||||||
Options.ClaimsIssuer));
|
Options.ClaimsIssuer));
|
||||||
ticket = new AuthenticationTicket(principal, null, Options.AuthenticationScheme);
|
ticket = new AuthenticationTicket(principal, null, Options.AuthenticationScheme);
|
||||||
}
|
}
|
||||||
var cookieValue = Options.TicketDataFormat.Protect(ticket);
|
var cookieValue = Options.TicketDataFormat.Protect(ticket, GetTlsTokenBinding());
|
||||||
|
|
||||||
Options.CookieManager.AppendResponseCookie(
|
Options.CookieManager.AppendResponseCookie(
|
||||||
Context,
|
Context,
|
||||||
|
|
@ -357,5 +358,11 @@ namespace Microsoft.AspNet.Authentication.Cookies
|
||||||
return true;
|
return true;
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private string GetTlsTokenBinding()
|
||||||
|
{
|
||||||
|
var binding = Context.Features.Get<ITlsTokenBindingFeature>()?.GetProvidedTokenBindingId();
|
||||||
|
return binding == null ? null : Convert.ToBase64String(binding);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -6,6 +6,8 @@ namespace Microsoft.AspNet.Authentication
|
||||||
public interface ISecureDataFormat<TData>
|
public interface ISecureDataFormat<TData>
|
||||||
{
|
{
|
||||||
string Protect(TData data);
|
string Protect(TData data);
|
||||||
|
string Protect(TData data, string purpose);
|
||||||
TData Unprotect(string protectedText);
|
TData Unprotect(string protectedText);
|
||||||
|
TData Unprotect(string protectedText, string purpose);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -1,8 +1,6 @@
|
||||||
// Copyright (c) .NET Foundation. All rights reserved.
|
// Copyright (c) .NET Foundation. All rights reserved.
|
||||||
// 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.Diagnostics.CodeAnalysis;
|
|
||||||
using Microsoft.AspNet.DataProtection;
|
using Microsoft.AspNet.DataProtection;
|
||||||
|
|
||||||
namespace Microsoft.AspNet.Authentication
|
namespace Microsoft.AspNet.Authentication
|
||||||
|
|
@ -20,14 +18,29 @@ namespace Microsoft.AspNet.Authentication
|
||||||
|
|
||||||
public string Protect(TData data)
|
public string Protect(TData data)
|
||||||
{
|
{
|
||||||
byte[] userData = _serializer.Serialize(data);
|
return Protect(data, purpose: null);
|
||||||
byte[] protectedData = _protector.Protect(userData);
|
}
|
||||||
string protectedText = Base64UrlTextEncoder.Encode(protectedData);
|
|
||||||
return protectedText;
|
public string Protect(TData data, string purpose)
|
||||||
|
{
|
||||||
|
var userData = _serializer.Serialize(data);
|
||||||
|
|
||||||
|
var protector = _protector;
|
||||||
|
if (!string.IsNullOrEmpty(purpose))
|
||||||
|
{
|
||||||
|
protector = protector.CreateProtector(purpose);
|
||||||
|
}
|
||||||
|
|
||||||
|
var protectedData = protector.Protect(userData);
|
||||||
|
return Base64UrlTextEncoder.Encode(protectedData);
|
||||||
}
|
}
|
||||||
|
|
||||||
[SuppressMessage("Microsoft.Design", "CA1031:DoNotCatchGeneralExceptionTypes", Justification = "Exception will be traced")]
|
|
||||||
public TData Unprotect(string protectedText)
|
public TData Unprotect(string protectedText)
|
||||||
|
{
|
||||||
|
return Unprotect(protectedText, purpose: null);
|
||||||
|
}
|
||||||
|
|
||||||
|
public TData Unprotect(string protectedText, string purpose)
|
||||||
{
|
{
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
|
|
@ -36,20 +49,25 @@ namespace Microsoft.AspNet.Authentication
|
||||||
return default(TData);
|
return default(TData);
|
||||||
}
|
}
|
||||||
|
|
||||||
byte[] protectedData = Base64UrlTextEncoder.Decode(protectedText);
|
var protectedData = Base64UrlTextEncoder.Decode(protectedText);
|
||||||
if (protectedData == null)
|
if (protectedData == null)
|
||||||
{
|
{
|
||||||
return default(TData);
|
return default(TData);
|
||||||
}
|
}
|
||||||
|
|
||||||
byte[] userData = _protector.Unprotect(protectedData);
|
var protector = _protector;
|
||||||
|
if (!string.IsNullOrEmpty(purpose))
|
||||||
|
{
|
||||||
|
protector = protector.CreateProtector(purpose);
|
||||||
|
}
|
||||||
|
|
||||||
|
var userData = protector.Unprotect(protectedData);
|
||||||
if (userData == null)
|
if (userData == null)
|
||||||
{
|
{
|
||||||
return default(TData);
|
return default(TData);
|
||||||
}
|
}
|
||||||
|
|
||||||
TData model = _serializer.Deserialize(userData);
|
return _serializer.Deserialize(userData);
|
||||||
return model;
|
|
||||||
}
|
}
|
||||||
catch
|
catch
|
||||||
{
|
{
|
||||||
|
|
|
||||||
|
|
@ -0,0 +1,80 @@
|
||||||
|
// Copyright (c) .NET Foundation. All rights reserved.
|
||||||
|
// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
|
||||||
|
|
||||||
|
using System;
|
||||||
|
using System.Text;
|
||||||
|
using Microsoft.AspNet.DataProtection;
|
||||||
|
using Microsoft.Extensions.DependencyInjection;
|
||||||
|
using Xunit;
|
||||||
|
|
||||||
|
namespace Microsoft.AspNet.Authentication.DataHandler
|
||||||
|
{
|
||||||
|
public class SecureDataFormatTests
|
||||||
|
{
|
||||||
|
public SecureDataFormatTests()
|
||||||
|
{
|
||||||
|
var serviceCollection = new ServiceCollection();
|
||||||
|
serviceCollection.AddDataProtection();
|
||||||
|
ServiceProvider = serviceCollection.BuildServiceProvider();
|
||||||
|
}
|
||||||
|
|
||||||
|
public IServiceProvider ServiceProvider { get; }
|
||||||
|
|
||||||
|
[Fact]
|
||||||
|
public void ProtectDataRoundTrips()
|
||||||
|
{
|
||||||
|
var provider = ServiceProvider.GetRequiredService<IDataProtectionProvider>();
|
||||||
|
var prototector = provider.CreateProtector("test");
|
||||||
|
var secureDataFormat = new SecureDataFormat<string>(new StringSerializer(), prototector);
|
||||||
|
|
||||||
|
string input = "abcdefghijklmnopqrstuvwxyz0123456789";
|
||||||
|
var protectedData = secureDataFormat.Protect(input);
|
||||||
|
var result = secureDataFormat.Unprotect(protectedData);
|
||||||
|
Assert.Equal(input, result);
|
||||||
|
}
|
||||||
|
|
||||||
|
[Fact]
|
||||||
|
public void ProtectWithPurposeRoundTrips()
|
||||||
|
{
|
||||||
|
var provider = ServiceProvider.GetRequiredService<IDataProtectionProvider>();
|
||||||
|
var prototector = provider.CreateProtector("test");
|
||||||
|
var secureDataFormat = new SecureDataFormat<string>(new StringSerializer(), prototector);
|
||||||
|
|
||||||
|
string input = "abcdefghijklmnopqrstuvwxyz0123456789";
|
||||||
|
string purpose = "purpose1";
|
||||||
|
var protectedData = secureDataFormat.Protect(input, purpose);
|
||||||
|
var result = secureDataFormat.Unprotect(protectedData, purpose);
|
||||||
|
Assert.Equal(input, result);
|
||||||
|
}
|
||||||
|
|
||||||
|
[Fact]
|
||||||
|
public void UnprotectWithDifferentPurposeFails()
|
||||||
|
{
|
||||||
|
var provider = ServiceProvider.GetRequiredService<IDataProtectionProvider>();
|
||||||
|
var prototector = provider.CreateProtector("test");
|
||||||
|
var secureDataFormat = new SecureDataFormat<string>(new StringSerializer(), prototector);
|
||||||
|
|
||||||
|
string input = "abcdefghijklmnopqrstuvwxyz0123456789";
|
||||||
|
string purpose = "purpose1";
|
||||||
|
var protectedData = secureDataFormat.Protect(input, purpose);
|
||||||
|
var result = secureDataFormat.Unprotect(protectedData); // Null other purpose
|
||||||
|
Assert.Null(result);
|
||||||
|
|
||||||
|
result = secureDataFormat.Unprotect(protectedData, "purpose2");
|
||||||
|
Assert.Null(result);
|
||||||
|
}
|
||||||
|
|
||||||
|
private class StringSerializer : IDataSerializer<string>
|
||||||
|
{
|
||||||
|
public byte[] Serialize(string model)
|
||||||
|
{
|
||||||
|
return Encoding.UTF8.GetBytes(model);
|
||||||
|
}
|
||||||
|
|
||||||
|
public string Deserialize(byte[] data)
|
||||||
|
{
|
||||||
|
return Encoding.UTF8.GetString(data);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -31,8 +31,12 @@ namespace Microsoft.AspNet.Authentication.Tests.OpenIdConnect
|
||||||
|
|
||||||
return sb.ToString();
|
return sb.ToString();
|
||||||
}
|
}
|
||||||
|
public string Protect(AuthenticationProperties data, string purpose)
|
||||||
|
{
|
||||||
|
return Protect(data);
|
||||||
|
}
|
||||||
|
|
||||||
AuthenticationProperties ISecureDataFormat<AuthenticationProperties>.Unprotect(string protectedText)
|
public AuthenticationProperties Unprotect(string protectedText)
|
||||||
{
|
{
|
||||||
if (string.IsNullOrEmpty(protectedText))
|
if (string.IsNullOrEmpty(protectedText))
|
||||||
{
|
{
|
||||||
|
|
@ -58,5 +62,10 @@ namespace Microsoft.AspNet.Authentication.Tests.OpenIdConnect
|
||||||
|
|
||||||
return propeties;
|
return propeties;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public AuthenticationProperties Unprotect(string protectedText, string purpose)
|
||||||
|
{
|
||||||
|
return Unprotect(protectedText);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue