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