TFA tweaks for templates (#1299)
This commit is contained in:
parent
a72acc93dc
commit
b355f59a01
|
|
@ -7,7 +7,6 @@ using System.Linq;
|
||||||
using System.Linq.Expressions;
|
using System.Linq.Expressions;
|
||||||
using System.Security.Claims;
|
using System.Security.Claims;
|
||||||
using System.Threading.Tasks;
|
using System.Threading.Tasks;
|
||||||
using Microsoft.AspNetCore.Builder;
|
|
||||||
using Microsoft.AspNetCore.Http;
|
using Microsoft.AspNetCore.Http;
|
||||||
using Microsoft.Extensions.DependencyInjection;
|
using Microsoft.Extensions.DependencyInjection;
|
||||||
using Microsoft.Extensions.Logging;
|
using Microsoft.Extensions.Logging;
|
||||||
|
|
@ -1890,6 +1889,7 @@ namespace Microsoft.AspNetCore.Identity.Test
|
||||||
{
|
{
|
||||||
IdentityResultAssert.IsSuccess(await manager.RedeemTwoFactorRecoveryCodeAsync(user, code));
|
IdentityResultAssert.IsSuccess(await manager.RedeemTwoFactorRecoveryCodeAsync(user, code));
|
||||||
IdentityResultAssert.IsFailure(await manager.RedeemTwoFactorRecoveryCodeAsync(user, code));
|
IdentityResultAssert.IsFailure(await manager.RedeemTwoFactorRecoveryCodeAsync(user, code));
|
||||||
|
Assert.Equal(--numCodes, await manager.CountRecoveryCodesAsync(user));
|
||||||
}
|
}
|
||||||
// One last time to be sure
|
// One last time to be sure
|
||||||
foreach (var code in newCodes)
|
foreach (var code in newCodes)
|
||||||
|
|
|
||||||
|
|
@ -31,5 +31,13 @@ namespace Microsoft.AspNetCore.Identity
|
||||||
/// <param name="cancellationToken">The <see cref="CancellationToken"/> used to propagate notifications that the operation should be canceled.</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>
|
/// <returns>True if the recovery code was found for the user.</returns>
|
||||||
Task<bool> RedeemCodeAsync(TUser user, string code, CancellationToken cancellationToken);
|
Task<bool> RedeemCodeAsync(TUser user, string code, CancellationToken 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>
|
||||||
|
Task<int> CountCodesAsync(TUser user, CancellationToken cancellationToken);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -2167,7 +2167,7 @@ namespace Microsoft.AspNetCore.Identity
|
||||||
/// </summary>
|
/// </summary>
|
||||||
/// <param name="user">The user to generate recovery codes for.</param>
|
/// <param name="user">The user to generate recovery codes for.</param>
|
||||||
/// <param name="number">The number of codes to generate.</param>
|
/// <param name="number">The number of codes to generate.</param>
|
||||||
/// <returns>The new recovery codes for the user.</returns>
|
/// <returns>The new recovery codes for the user. Note: there may be less than number returned, as duplicates will be removed.</returns>
|
||||||
public virtual async Task<IEnumerable<string>> GenerateNewTwoFactorRecoveryCodesAsync(TUser user, int number)
|
public virtual async Task<IEnumerable<string>> GenerateNewTwoFactorRecoveryCodesAsync(TUser user, int number)
|
||||||
{
|
{
|
||||||
ThrowIfDisposed();
|
ThrowIfDisposed();
|
||||||
|
|
@ -2183,7 +2183,7 @@ namespace Microsoft.AspNetCore.Identity
|
||||||
newCodes.Add(CreateTwoFactorRecoveryCode());
|
newCodes.Add(CreateTwoFactorRecoveryCode());
|
||||||
}
|
}
|
||||||
|
|
||||||
await store.ReplaceCodesAsync(user, newCodes, CancellationToken);
|
await store.ReplaceCodesAsync(user, newCodes.Distinct(), CancellationToken);
|
||||||
var update = await UpdateAsync(user);
|
var update = await UpdateAsync(user);
|
||||||
if (update.Succeeded)
|
if (update.Succeeded)
|
||||||
{
|
{
|
||||||
|
|
@ -2197,9 +2197,7 @@ namespace Microsoft.AspNetCore.Identity
|
||||||
/// </summary>
|
/// </summary>
|
||||||
/// <returns></returns>
|
/// <returns></returns>
|
||||||
protected virtual string CreateTwoFactorRecoveryCode()
|
protected virtual string CreateTwoFactorRecoveryCode()
|
||||||
{
|
=> Guid.NewGuid().ToString().Substring(0, 8);
|
||||||
return Guid.NewGuid().ToString();
|
|
||||||
}
|
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Returns whether a recovery code is valid for a user. Note: recovery codes are only valid
|
/// Returns whether a recovery code is valid for a user. Note: recovery codes are only valid
|
||||||
|
|
@ -2225,6 +2223,23 @@ namespace Microsoft.AspNetCore.Identity
|
||||||
return IdentityResult.Failed(ErrorDescriber.RecoveryCodeRedemptionFailed());
|
return IdentityResult.Failed(ErrorDescriber.RecoveryCodeRedemptionFailed());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Returns how many recovery code are still valid for a user.
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="user">The user.</param>
|
||||||
|
/// <returns>How many recovery code are still valid for a user.</returns>
|
||||||
|
public virtual Task<int> CountRecoveryCodesAsync(TUser user)
|
||||||
|
{
|
||||||
|
ThrowIfDisposed();
|
||||||
|
var store = GetRecoveryCodeStore();
|
||||||
|
if (user == null)
|
||||||
|
{
|
||||||
|
throw new ArgumentNullException(nameof(user));
|
||||||
|
}
|
||||||
|
|
||||||
|
return store.CountCodesAsync(user, CancellationToken);
|
||||||
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Releases the unmanaged resources used by the role manager and optionally releases the managed resources.
|
/// Releases the unmanaged resources used by the role manager and optionally releases the managed resources.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
|
|
|
||||||
|
|
@ -992,9 +992,7 @@ namespace Microsoft.AspNetCore.Identity
|
||||||
/// <param name="cancellationToken">The <see cref="CancellationToken"/> used to propagate notifications that the operation should be canceled.</param>
|
/// <param name="cancellationToken">The <see cref="CancellationToken"/> used to propagate notifications that the operation should be canceled.</param>
|
||||||
/// <returns>The <see cref="Task"/> that represents the asynchronous operation.</returns>
|
/// <returns>The <see cref="Task"/> that represents the asynchronous operation.</returns>
|
||||||
public virtual Task SetAuthenticatorKeyAsync(TUser user, string key, CancellationToken cancellationToken)
|
public virtual Task SetAuthenticatorKeyAsync(TUser user, string key, CancellationToken cancellationToken)
|
||||||
{
|
=> SetTokenAsync(user, InternalLoginProvider, AuthenticatorKeyTokenName, key, cancellationToken);
|
||||||
return SetTokenAsync(user, InternalLoginProvider, AuthenticatorKeyTokenName, key, cancellationToken);
|
|
||||||
}
|
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Get the authenticator key for the specified <paramref name="user" />.
|
/// Get the authenticator key for the specified <paramref name="user" />.
|
||||||
|
|
@ -1003,8 +1001,29 @@ namespace Microsoft.AspNetCore.Identity
|
||||||
/// <param name="cancellationToken">The <see cref="CancellationToken"/> used to propagate notifications that the operation should be canceled.</param>
|
/// <param name="cancellationToken">The <see cref="CancellationToken"/> used to propagate notifications that the operation should be canceled.</param>
|
||||||
/// <returns>The <see cref="Task"/> that represents the asynchronous operation, containing the security stamp for the specified <paramref name="user"/>.</returns>
|
/// <returns>The <see cref="Task"/> that represents the asynchronous operation, containing the security stamp for the specified <paramref name="user"/>.</returns>
|
||||||
public virtual Task<string> GetAuthenticatorKeyAsync(TUser user, CancellationToken cancellationToken)
|
public virtual Task<string> GetAuthenticatorKeyAsync(TUser user, CancellationToken cancellationToken)
|
||||||
|
=> 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(TUser user, CancellationToken cancellationToken)
|
||||||
{
|
{
|
||||||
return GetTokenAsync(user, InternalLoginProvider, AuthenticatorKeyTokenName, cancellationToken);
|
cancellationToken.ThrowIfCancellationRequested();
|
||||||
|
ThrowIfDisposed();
|
||||||
|
|
||||||
|
if (user == null)
|
||||||
|
{
|
||||||
|
throw new ArgumentNullException(nameof(user));
|
||||||
|
}
|
||||||
|
var mergedCodes = await GetTokenAsync(user, InternalLoginProvider, RecoveryCodeTokenName, cancellationToken) ?? "";
|
||||||
|
if (mergedCodes.Length > 0)
|
||||||
|
{
|
||||||
|
return mergedCodes.Split(';').Length;
|
||||||
|
}
|
||||||
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
|
|
|
||||||
|
|
@ -431,5 +431,15 @@ namespace Microsoft.AspNetCore.Identity.InMemory
|
||||||
}
|
}
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public async Task<int> CountCodesAsync(TUser user, CancellationToken cancellationToken)
|
||||||
|
{
|
||||||
|
var mergedCodes = await GetTokenAsync(user, AuthenticatorStoreLoginProvider, RecoveryCodeTokenName, cancellationToken) ?? "";
|
||||||
|
if (mergedCodes.Length > 0)
|
||||||
|
{
|
||||||
|
return mergedCodes.Split(';').Length;
|
||||||
|
}
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue