From c442be17b27cac90235b6701707c19eef3111749 Mon Sep 17 00:00:00 2001 From: JudahGabriel Date: Fri, 23 Feb 2018 17:52:18 -0600 Subject: [PATCH] Now implementing IUserAuthenticationTokenStore to work with TFA. --- RavenDB.Identity/IdentityUserAuthToken.cs | 73 +++++++++++++++++++++++ RavenDB.Identity/RavenDB.Identity.csproj | 4 +- RavenDB.Identity/UserStore.cs | 52 +++++++++++++++- 3 files changed, 127 insertions(+), 2 deletions(-) create mode 100644 RavenDB.Identity/IdentityUserAuthToken.cs diff --git a/RavenDB.Identity/IdentityUserAuthToken.cs b/RavenDB.Identity/IdentityUserAuthToken.cs new file mode 100644 index 0000000..b8dd8a2 --- /dev/null +++ b/RavenDB.Identity/IdentityUserAuthToken.cs @@ -0,0 +1,73 @@ +using Raven.Client.Documents; +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; + +namespace Raven.Identity +{ + /// + /// A two-factor authentication authorization token. + /// + public class IdentityUserAuthToken + { + /// + /// The ID of the auth token. + /// + public string Id { get; set; } + + /// + /// The ID of the this auth token is for. + /// + public string UserId { get; set; } + + /// + /// The login provider. + /// + public string LoginProvider { get; set; } + + /// + /// The name of the token. + /// + public string Name { get; set; } + + /// + /// The value of the token. + /// + public string Value { get; set; } + + /// + /// Gets the well-known ID for a IdentityuserAuthToken. + /// + /// + /// + /// + /// + /// + 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; + } + } + } +} diff --git a/RavenDB.Identity/RavenDB.Identity.csproj b/RavenDB.Identity/RavenDB.Identity.csproj index 68e0602..600d2ca 100644 --- a/RavenDB.Identity/RavenDB.Identity.csproj +++ b/RavenDB.Identity/RavenDB.Identity.csproj @@ -12,14 +12,16 @@ https://github.com/JudahGabriel/RavenDB.Identity git https://github.com/JudahGabriel/RavenDB.Identity + Added support for Two Factor auth. https://github.com/JudahGabriel/RavenDB.Identity/blob/master/LICENSE.md https://github.com/JudahGabriel/RavenDB.Identity/blob/master/RavenDB.Identity/nuget-icon.png?raw=true Copyright 2018 BitShuva false false false - 4.1.1 + 4.2.0 False + Raven.Identity diff --git a/RavenDB.Identity/UserStore.cs b/RavenDB.Identity/UserStore.cs index 3919e89..8e43733 100644 --- a/RavenDB.Identity/UserStore.cs +++ b/RavenDB.Identity/UserStore.cs @@ -25,7 +25,8 @@ namespace Raven.Identity IUserLockoutStore, IUserTwoFactorStore, IUserPhoneNumberStore, - IUserAuthenticatorKeyStore + IUserAuthenticatorKeyStore, + IUserAuthenticationTokenStore where TUser : IdentityUser { private bool _disposed; @@ -652,6 +653,55 @@ namespace Raven.Identity #endregion + #region IUserAuthenticationTokenStore + + /// + 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(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; + } + + /// + 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; + } + + /// + public async Task 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(id); + if (tokenOrNull == null) + { + return null; + } + + return tokenOrNull.Value; + } + + #endregion + private IAsyncDocumentSession DbSession { get