Now implementing IUserAuthenticationTokenStore to work with TFA.

This commit is contained in:
JudahGabriel
2018-02-23 17:52:18 -06:00
parent 6f61830de0
commit c442be17b2
3 changed files with 127 additions and 2 deletions
+73
View File
@@ -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;
}
}
}
}
+3 -1
View File
@@ -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>
+51 -1
View File
@@ -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