|
|
|
@ -29,8 +29,14 @@ namespace Volo.Abp.Identity
|
|
|
|
|
IUserPhoneNumberStore<IdentityUser>,
|
|
|
|
|
IUserTwoFactorStore<IdentityUser>,
|
|
|
|
|
IUserAuthenticationTokenStore<IdentityUser>,
|
|
|
|
|
IUserAuthenticatorKeyStore<IdentityUser>,
|
|
|
|
|
IUserTwoFactorRecoveryCodeStore<IdentityUser>,
|
|
|
|
|
ITransientDependency
|
|
|
|
|
{
|
|
|
|
|
private const string InternalLoginProvider = "[AspNetUserStore]";
|
|
|
|
|
private const string AuthenticatorKeyTokenName = "AuthenticatorKey";
|
|
|
|
|
private const string RecoveryCodeTokenName = "RecoveryCodes";
|
|
|
|
|
|
|
|
|
|
/// <summary>
|
|
|
|
|
/// Gets or sets the <see cref="IdentityErrorDescriber"/> for any error that occurred with the current operation.
|
|
|
|
|
/// </summary>
|
|
|
|
@ -1024,6 +1030,76 @@ namespace Volo.Abp.Identity
|
|
|
|
|
return user.FindToken(loginProvider, name)?.Value;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
public Task SetAuthenticatorKeyAsync(IdentityUser user, string key, CancellationToken cancellationToken)
|
|
|
|
|
{
|
|
|
|
|
return SetTokenAsync(user, InternalLoginProvider, AuthenticatorKeyTokenName, key, cancellationToken);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
public Task<string> GetAuthenticatorKeyAsync(IdentityUser user, CancellationToken cancellationToken)
|
|
|
|
|
{
|
|
|
|
|
return GetTokenAsync(user, InternalLoginProvider, AuthenticatorKeyTokenName, cancellationToken);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/// <summary>
|
|
|
|
|
/// Returns how many recovery code are still valid for a user.
|
|
|
|
|
/// </summary>
|
|
|
|
|
/// <param name="user">The user who owns the recovery code.</param>
|
|
|
|
|
/// <param name="cancellationToken">The <see cref="CancellationToken"/> used to propagate notifications that the operation should be canceled.</param>
|
|
|
|
|
/// <returns>The number of valid recovery codes for the user..</returns>
|
|
|
|
|
public virtual async Task<int> CountCodesAsync(IdentityUser user, CancellationToken cancellationToken)
|
|
|
|
|
{
|
|
|
|
|
cancellationToken.ThrowIfCancellationRequested();
|
|
|
|
|
|
|
|
|
|
Check.NotNull(user, nameof(user));
|
|
|
|
|
|
|
|
|
|
var mergedCodes = await GetTokenAsync(user, InternalLoginProvider, RecoveryCodeTokenName, cancellationToken) ?? "";
|
|
|
|
|
if (mergedCodes.Length > 0)
|
|
|
|
|
{
|
|
|
|
|
return mergedCodes.Split(';').Length;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return 0;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/// <summary>
|
|
|
|
|
/// Updates the recovery codes for the user while invalidating any previous recovery codes.
|
|
|
|
|
/// </summary>
|
|
|
|
|
/// <param name="user">The user to store new recovery codes for.</param>
|
|
|
|
|
/// <param name="recoveryCodes">The new recovery codes for the user.</param>
|
|
|
|
|
/// <param name="cancellationToken">The <see cref="CancellationToken"/> used to propagate notifications that the operation should be canceled.</param>
|
|
|
|
|
/// <returns>The new recovery codes for the user.</returns>
|
|
|
|
|
public virtual Task ReplaceCodesAsync(IdentityUser user, IEnumerable<string> recoveryCodes, CancellationToken cancellationToken)
|
|
|
|
|
{
|
|
|
|
|
var mergedCodes = string.Join(";", recoveryCodes);
|
|
|
|
|
return SetTokenAsync(user, InternalLoginProvider, RecoveryCodeTokenName, mergedCodes, cancellationToken);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/// <summary>
|
|
|
|
|
/// Returns whether a recovery code is valid for a user. Note: recovery codes are only valid
|
|
|
|
|
/// once, and will be invalid after use.
|
|
|
|
|
/// </summary>
|
|
|
|
|
/// <param name="user">The user who owns the recovery code.</param>
|
|
|
|
|
/// <param name="code">The recovery code to use.</param>
|
|
|
|
|
/// <param name="cancellationToken">The <see cref="CancellationToken"/> used to propagate notifications that the operation should be canceled.</param>
|
|
|
|
|
/// <returns>True if the recovery code was found for the user.</returns>
|
|
|
|
|
public virtual async Task<bool> RedeemCodeAsync(IdentityUser user, string code, CancellationToken cancellationToken)
|
|
|
|
|
{
|
|
|
|
|
cancellationToken.ThrowIfCancellationRequested();
|
|
|
|
|
|
|
|
|
|
Check.NotNull(user, nameof(user));
|
|
|
|
|
Check.NotNull(code, nameof(code));
|
|
|
|
|
|
|
|
|
|
var mergedCodes = await GetTokenAsync(user, InternalLoginProvider, RecoveryCodeTokenName, cancellationToken) ?? "";
|
|
|
|
|
var splitCodes = mergedCodes.Split(';');
|
|
|
|
|
if (splitCodes.Contains(code))
|
|
|
|
|
{
|
|
|
|
|
var updatedCodes = new List<string>(splitCodes.Where(s => s != code));
|
|
|
|
|
await ReplaceCodesAsync(user, updatedCodes, cancellationToken);
|
|
|
|
|
return true;
|
|
|
|
|
}
|
|
|
|
|
return false;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
public void Dispose()
|
|
|
|
|
{
|
|
|
|
|
|
|
|
|
|