Now implementing IUserAuthenticationTokenStore to work with TFA.
This commit is contained in:
@@ -0,0 +1,73 @@
|
||||
using Raven.Client.Documents;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
|
||||
namespace Raven.Identity
|
||||
{
|
||||
/// <summary>
|
||||
/// A two-factor authentication authorization token.
|
||||
/// </summary>
|
||||
public class IdentityUserAuthToken
|
||||
{
|
||||
/// <summary>
|
||||
/// The ID of the auth token.
|
||||
/// </summary>
|
||||
public string Id { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// The ID of the <see cref="IdentityUser"/> this auth token is for.
|
||||
/// </summary>
|
||||
public string UserId { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// The login provider.
|
||||
/// </summary>
|
||||
public string LoginProvider { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// The name of the token.
|
||||
/// </summary>
|
||||
public string Name { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// The value of the token.
|
||||
/// </summary>
|
||||
public string Value { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets the well-known ID for a IdentityuserAuthToken.
|
||||
/// </summary>
|
||||
/// <param name="db"></param>
|
||||
/// <param name="userId"></param>
|
||||
/// <param name="loginProvider"></param>
|
||||
/// <param name="name"></param>
|
||||
/// <returns></returns>
|
||||
public static string GetWellKnownId(IDocumentStore db, string userId, string loginProvider, string name)
|
||||
{
|
||||
var vals = new[] { userId, loginProvider, name }
|
||||
.Select(v => v ?? "[null]");
|
||||
var collection = db.Conventions.GetCollectionName(typeof(IdentityUserAuthToken));
|
||||
var separator = db.Conventions.IdentityPartsSeparator;
|
||||
var valsHash = CalculateHash(string.Join("-", vals));
|
||||
return collection + separator + valsHash.ToString();
|
||||
}
|
||||
|
||||
private static int CalculateHash(string input)
|
||||
{
|
||||
// We can't use input.GetHashCode in .NET Core, as it can (and does!) return different values each time the app is run.
|
||||
// See https://github.com/dotnet/corefx/issues/19703
|
||||
// Instead, we've implemented the following deterministic string hash algorithm: https://stackoverflow.com/a/5155015/536
|
||||
unchecked
|
||||
{
|
||||
int hash = 23;
|
||||
foreach (char c in input)
|
||||
{
|
||||
hash = hash * 31 + c;
|
||||
}
|
||||
return hash;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -12,14 +12,16 @@
|
||||
<PackageProjectUrl>https://github.com/JudahGabriel/RavenDB.Identity</PackageProjectUrl>
|
||||
<RepositoryType>git</RepositoryType>
|
||||
<RepositoryUrl>https://github.com/JudahGabriel/RavenDB.Identity</RepositoryUrl>
|
||||
<ReleaseNotes>Added support for Two Factor auth.</ReleaseNotes>
|
||||
<PackageLicenseUrl>https://github.com/JudahGabriel/RavenDB.Identity/blob/master/LICENSE.md</PackageLicenseUrl>
|
||||
<PackageIconUrl>https://github.com/JudahGabriel/RavenDB.Identity/blob/master/RavenDB.Identity/nuget-icon.png?raw=true</PackageIconUrl>
|
||||
<copyright>Copyright 2018 BitShuva</copyright>
|
||||
<GenerateAssemblyConfigurationAttribute>false</GenerateAssemblyConfigurationAttribute>
|
||||
<GenerateAssemblyCompanyAttribute>false</GenerateAssemblyCompanyAttribute>
|
||||
<GenerateAssemblyProductAttribute>false</GenerateAssemblyProductAttribute>
|
||||
<Version>4.1.1</Version>
|
||||
<Version>4.2.0</Version>
|
||||
<GeneratePackageOnBuild>False</GeneratePackageOnBuild>
|
||||
<RootNamespace>Raven.Identity</RootNamespace>
|
||||
</PropertyGroup>
|
||||
|
||||
<ItemGroup>
|
||||
|
||||
@@ -25,7 +25,8 @@ namespace Raven.Identity
|
||||
IUserLockoutStore<TUser>,
|
||||
IUserTwoFactorStore<TUser>,
|
||||
IUserPhoneNumberStore<TUser>,
|
||||
IUserAuthenticatorKeyStore<TUser>
|
||||
IUserAuthenticatorKeyStore<TUser>,
|
||||
IUserAuthenticationTokenStore<TUser>
|
||||
where TUser : IdentityUser
|
||||
{
|
||||
private bool _disposed;
|
||||
@@ -652,6 +653,55 @@ namespace Raven.Identity
|
||||
|
||||
#endregion
|
||||
|
||||
#region IUserAuthenticationTokenStore
|
||||
|
||||
/// <inheritdoc />
|
||||
public async Task SetTokenAsync(TUser user, string loginProvider, string name, string value, CancellationToken cancellationToken)
|
||||
{
|
||||
var id = IdentityUserAuthToken.GetWellKnownId(DbSession.Advanced.DocumentStore, user.Id, loginProvider, name);
|
||||
ThrowIfDisposedOrCancelled(cancellationToken);
|
||||
|
||||
var existingOrNull = await DbSession.LoadAsync<IdentityUserAuthToken>(id);
|
||||
if (existingOrNull == null)
|
||||
{
|
||||
existingOrNull = new IdentityUserAuthToken
|
||||
{
|
||||
Id = id,
|
||||
LoginProvider = loginProvider,
|
||||
Name = name,
|
||||
UserId = user.Id,
|
||||
Value = value
|
||||
};
|
||||
await DbSession.StoreAsync(existingOrNull);
|
||||
}
|
||||
|
||||
existingOrNull.Value = value;
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
public Task RemoveTokenAsync(TUser user, string loginProvider, string name, CancellationToken cancellationToken)
|
||||
{
|
||||
var id = IdentityUserAuthToken.GetWellKnownId(DbSession.Advanced.DocumentStore, user.Id, loginProvider, name);
|
||||
DbSession.Delete(id);
|
||||
|
||||
return Task.CompletedTask;
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
public async Task<string> GetTokenAsync(TUser user, string loginProvider, string name, CancellationToken cancellationToken)
|
||||
{
|
||||
var id = IdentityUserAuthToken.GetWellKnownId(DbSession.Advanced.DocumentStore, user.Id, loginProvider, name);
|
||||
var tokenOrNull = await DbSession.LoadAsync<IdentityUserAuthToken>(id);
|
||||
if (tokenOrNull == null)
|
||||
{
|
||||
return null;
|
||||
}
|
||||
|
||||
return tokenOrNull.Value;
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
private IAsyncDocumentSession DbSession
|
||||
{
|
||||
get
|
||||
|
||||
Reference in New Issue
Block a user