Added OWIN based token provider. Removed use of IdentityFactoryOptions
This commit is contained in:
@@ -52,7 +52,7 @@ namespace Umbraco.Web.Security
|
||||
passwordConfiguration,
|
||||
ipResolver,
|
||||
new IdentityErrorDescriber(),
|
||||
null,
|
||||
app.GetDataProtectionProvider(),
|
||||
new NullLogger<BackOfficeUserManager2<BackOfficeIdentityUser>>()));
|
||||
|
||||
app.SetBackOfficeUserManagerType<BackOfficeUserManager2, BackOfficeIdentityUser>();
|
||||
@@ -82,7 +82,7 @@ namespace Umbraco.Web.Security
|
||||
ipResolver,
|
||||
customUserStore,
|
||||
new IdentityErrorDescriber(),
|
||||
null,
|
||||
app.GetDataProtectionProvider(),
|
||||
new NullLogger<BackOfficeUserManager2<BackOfficeIdentityUser>>()));
|
||||
|
||||
app.SetBackOfficeUserManagerType<BackOfficeUserManager2, BackOfficeIdentityUser>();
|
||||
@@ -91,34 +91,6 @@ namespace Umbraco.Web.Security
|
||||
app.CreatePerOwinContext<BackOfficeSignInManager2>((options, context) => BackOfficeSignInManager2.Create(context, globalSettings, app.CreateLogger(typeof(BackOfficeSignInManager2).FullName)));
|
||||
}
|
||||
|
||||
// TODO: SB: ConfigureUserManagerForUmbracoBackOffice using IdentityFactoryOptions
|
||||
/*/// <summary>
|
||||
/// Configure a custom BackOfficeUserManager for Umbraco
|
||||
/// </summary>
|
||||
/// <param name="app"></param>
|
||||
/// <param name="runtimeState"></param>
|
||||
/// <param name="globalSettings"></param>
|
||||
/// <param name="userManager"></param>
|
||||
public static void ConfigureUserManagerForUmbracoBackOffice<TManager, TUser>(this IAppBuilder app,
|
||||
IRuntimeState runtimeState,
|
||||
IGlobalSettings globalSettings,
|
||||
Func<IdentityFactoryOptions<TManager>, IOwinContext, TManager> userManager)
|
||||
where TManager : BackOfficeUserManager2<TUser>
|
||||
where TUser : BackOfficeIdentityUser
|
||||
{
|
||||
if (runtimeState == null) throw new ArgumentNullException(nameof(runtimeState));
|
||||
if (userManager == null) throw new ArgumentNullException(nameof(userManager));
|
||||
|
||||
//Configure Umbraco user manager to be created per request
|
||||
app.CreatePerOwinContext<TManager>(userManager);
|
||||
|
||||
app.SetBackOfficeUserManagerType<TManager, TUser>();
|
||||
|
||||
//Create a sign in manager per request
|
||||
app.CreatePerOwinContext<BackOfficeSignInManager2>(
|
||||
(options, context) => BackOfficeSignInManager2.Create(context, globalSettings, app.CreateLogger(typeof(BackOfficeSignInManager2).FullName)));
|
||||
}*/
|
||||
|
||||
/// <summary>
|
||||
/// Ensures that the UmbracoBackOfficeAuthenticationMiddleware is assigned to the pipeline
|
||||
/// </summary>
|
||||
|
||||
@@ -5,6 +5,7 @@ using System.Threading.Tasks;
|
||||
using Microsoft.AspNetCore.Identity;
|
||||
using Microsoft.Extensions.Logging;
|
||||
using Microsoft.Extensions.Options;
|
||||
using Microsoft.Owin.Security.DataProtection;
|
||||
using Umbraco.Core.Configuration;
|
||||
using Umbraco.Core.Mapping;
|
||||
using Umbraco.Core.Security;
|
||||
@@ -27,11 +28,11 @@ namespace Umbraco.Web.Security
|
||||
IEnumerable<IPasswordValidator<BackOfficeIdentityUser>> passwordValidators,
|
||||
ILookupNormalizer keyNormalizer,
|
||||
IdentityErrorDescriber errors,
|
||||
IServiceProvider services,
|
||||
IDataProtectionProvider dataProtectionProvider,
|
||||
ILogger<UserManager<BackOfficeIdentityUser>> logger)
|
||||
: base(passwordConfiguration, ipResolver, store, optionsAccessor, userValidators, passwordValidators, keyNormalizer, errors, services, logger)
|
||||
: base(passwordConfiguration, ipResolver, store, optionsAccessor, userValidators, passwordValidators, keyNormalizer, errors, null, logger)
|
||||
{
|
||||
InitUserManager(this, passwordConfiguration);
|
||||
InitUserManager(this, dataProtectionProvider);
|
||||
}
|
||||
|
||||
#region Static Create methods
|
||||
@@ -48,7 +49,7 @@ namespace Umbraco.Web.Security
|
||||
IPasswordConfiguration passwordConfiguration,
|
||||
IIpResolver ipResolver,
|
||||
IdentityErrorDescriber errors,
|
||||
IServiceProvider services,
|
||||
IDataProtectionProvider dataProtectionProvider,
|
||||
ILogger<UserManager<BackOfficeIdentityUser>> logger)
|
||||
{
|
||||
var store = new BackOfficeUserStore2(userService, entityService, externalLoginService, globalSettings, mapper);
|
||||
@@ -58,7 +59,7 @@ namespace Umbraco.Web.Security
|
||||
ipResolver,
|
||||
store,
|
||||
errors,
|
||||
services,
|
||||
dataProtectionProvider,
|
||||
logger);
|
||||
}
|
||||
|
||||
@@ -70,7 +71,7 @@ namespace Umbraco.Web.Security
|
||||
IIpResolver ipResolver,
|
||||
IUserStore<BackOfficeIdentityUser> customUserStore,
|
||||
IdentityErrorDescriber errors,
|
||||
IServiceProvider services,
|
||||
IDataProtectionProvider dataProtectionProvider,
|
||||
ILogger<UserManager<BackOfficeIdentityUser>> logger)
|
||||
{
|
||||
var options = new IdentityOptions();
|
||||
@@ -103,7 +104,7 @@ namespace Umbraco.Web.Security
|
||||
passwordValidators,
|
||||
new NopLookupNormalizer(),
|
||||
errors,
|
||||
services,
|
||||
dataProtectionProvider,
|
||||
logger);
|
||||
}
|
||||
|
||||
@@ -151,27 +152,24 @@ namespace Umbraco.Web.Security
|
||||
/// <summary>
|
||||
/// Initializes the user manager with the correct options
|
||||
/// </summary>
|
||||
/// <param name="manager"></param>
|
||||
/// <param name="passwordConfig"></param>
|
||||
/// <returns></returns>
|
||||
protected void InitUserManager(
|
||||
BackOfficeUserManager2<T> manager,
|
||||
IPasswordConfiguration passwordConfig)
|
||||
// IDataProtectionProvider dataProtectionProvider
|
||||
IDataProtectionProvider dataProtectionProvider)
|
||||
{
|
||||
//use a custom hasher based on our membership provider
|
||||
PasswordHasher = GetDefaultPasswordHasher(PasswordConfiguration);
|
||||
|
||||
// TODO: SB: manager.Options.Tokens using OWIN data protector
|
||||
/*if (dataProtectionProvider != null)
|
||||
// TODO: SB: manager.Options.Tokens using OWIN data protector - what about the other providers???
|
||||
// https://github.com/dotnet/aspnetcore/blob/0a0e1ea0cdbe29f2fcd2291b900db98597387d77/src/Identity/Core/src/IdentityBuilderExtensions.cs#L28
|
||||
if (dataProtectionProvider != null)
|
||||
{
|
||||
manager.UserTokenProvider = new DataProtectorTokenProvider<T, int>(dataProtectionProvider.Create("ASP.NET Identity"))
|
||||
{
|
||||
TokenLifespan = TimeSpan.FromDays(3)
|
||||
};
|
||||
}*/
|
||||
|
||||
|
||||
manager.RegisterTokenProvider(
|
||||
"Default",
|
||||
new OwinDataProtectorTokenProvider<T>(dataProtectionProvider.Create("ASP.NET Identity"))
|
||||
{
|
||||
TokenLifespan = TimeSpan.FromDays(3)
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
|
||||
@@ -0,0 +1,130 @@
|
||||
using System;
|
||||
using System.Globalization;
|
||||
using System.IO;
|
||||
using System.Text;
|
||||
using System.Threading.Tasks;
|
||||
using Microsoft.AspNetCore.Identity;
|
||||
using Microsoft.Owin.Security.DataProtection;
|
||||
using Umbraco.Web.Models.Identity;
|
||||
|
||||
namespace Umbraco.Web.Security
|
||||
{
|
||||
/// <summary>
|
||||
/// Adapted from Microsoft.AspNet.Identity.Owin.DataProtectorTokenProvider
|
||||
/// </summary>
|
||||
public class OwinDataProtectorTokenProvider<TUser> : IUserTwoFactorTokenProvider<TUser> where TUser : BackOfficeIdentityUser
|
||||
{
|
||||
public TimeSpan TokenLifespan { get; set; }
|
||||
private readonly IDataProtector _protector;
|
||||
|
||||
public OwinDataProtectorTokenProvider(IDataProtector protector)
|
||||
{
|
||||
_protector = protector ?? throw new ArgumentNullException(nameof(protector));
|
||||
TokenLifespan = TimeSpan.FromDays(1);
|
||||
}
|
||||
|
||||
public async Task<string> GenerateAsync(string purpose, UserManager<TUser> manager, TUser user)
|
||||
{
|
||||
if (user == null) throw new ArgumentNullException(nameof(user));
|
||||
|
||||
var ms = new MemoryStream();
|
||||
using (var writer = ms.CreateWriter())
|
||||
{
|
||||
writer.Write(DateTimeOffset.UtcNow);
|
||||
writer.Write(Convert.ToString(user.Id, CultureInfo.InvariantCulture));
|
||||
writer.Write(purpose ?? "");
|
||||
|
||||
string stamp = null;
|
||||
if (manager.SupportsUserSecurityStamp)
|
||||
{
|
||||
stamp = await manager.GetSecurityStampAsync(user);
|
||||
}
|
||||
writer.Write(stamp ?? "");
|
||||
}
|
||||
|
||||
var protectedBytes = _protector.Protect(ms.ToArray());
|
||||
return Convert.ToBase64String(protectedBytes);
|
||||
}
|
||||
|
||||
public async Task<bool> ValidateAsync(string purpose, string token, UserManager<TUser> manager, TUser user)
|
||||
{
|
||||
try
|
||||
{
|
||||
var unprotectedData = _protector.Unprotect(Convert.FromBase64String(token));
|
||||
var ms = new MemoryStream(unprotectedData);
|
||||
using (var reader = ms.CreateReader())
|
||||
{
|
||||
var creationTime = reader.ReadDateTimeOffset();
|
||||
var expirationTime = creationTime + TokenLifespan;
|
||||
if (expirationTime < DateTimeOffset.UtcNow)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
var userId = reader.ReadString();
|
||||
if (!string.Equals(userId, Convert.ToString(user.Id, CultureInfo.InvariantCulture)))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
var purp = reader.ReadString();
|
||||
if (!string.Equals(purp, purpose))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
var stamp = reader.ReadString();
|
||||
if (reader.PeekChar() != -1)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
if (manager.SupportsUserSecurityStamp)
|
||||
{
|
||||
var expectedStamp = await manager.GetSecurityStampAsync(user);
|
||||
return stamp == expectedStamp;
|
||||
}
|
||||
|
||||
return stamp == "";
|
||||
}
|
||||
}
|
||||
// ReSharper disable once EmptyGeneralCatchClause
|
||||
catch
|
||||
{
|
||||
// Do not leak exception
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
public Task<bool> CanGenerateTwoFactorTokenAsync(UserManager<TUser> manager, TUser user)
|
||||
{
|
||||
return Task.FromResult(true);
|
||||
}
|
||||
}
|
||||
|
||||
internal static class StreamExtensions
|
||||
{
|
||||
private static readonly Encoding DefaultEncoding = new UTF8Encoding(false, true);
|
||||
|
||||
public static BinaryReader CreateReader(this Stream stream)
|
||||
{
|
||||
return new BinaryReader(stream, DefaultEncoding, true);
|
||||
}
|
||||
|
||||
public static BinaryWriter CreateWriter(this Stream stream)
|
||||
{
|
||||
return new BinaryWriter(stream, DefaultEncoding, true);
|
||||
}
|
||||
|
||||
public static DateTimeOffset ReadDateTimeOffset(this BinaryReader reader)
|
||||
{
|
||||
return new DateTimeOffset(reader.ReadInt64(), TimeSpan.Zero);
|
||||
}
|
||||
|
||||
public static void Write(this BinaryWriter writer, DateTimeOffset value)
|
||||
{
|
||||
writer.Write(value.UtcTicks);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -210,6 +210,7 @@
|
||||
<Compile Include="Security\MembershipProviderBase.cs" />
|
||||
<Compile Include="Security\MembershipProviderExtensions.cs" />
|
||||
<Compile Include="Security\NopLookupNormalizer.cs" />
|
||||
<Compile Include="Security\OwinDataProtectorTokenProvider.cs" />
|
||||
<Compile Include="Security\PasswordSecurity.cs" />
|
||||
<Compile Include="Security\UmbracoBackOfficeIdentity.cs" />
|
||||
<Compile Include="Security\UmbracoEmailMessage.cs" />
|
||||
|
||||
Reference in New Issue
Block a user