单次使用后如何使两因素身份验证令牌过期

Posted

技术标签:

【中文标题】单次使用后如何使两因素身份验证令牌过期【英文标题】:How to make two factor authentication token expire after single use 【发布时间】:2018-12-13 15:24:25 【问题描述】:

我实现了两因素身份验证,但遵循本教程

https://docs.microsoft.com/en-us/aspnet/identity/overview/features-api/two-factor-authentication-using-sms-and-email-with-aspnet-identity

我想让代码在单次使用后过期。

现在,用户在到期时间(设置为 5 分钟)完成期间收到相同的代码。有没有办法让代码一次性使用?我找不到有关此主题的任何内容。

【问题讨论】:

【参考方案1】:

tutorial that you linked to 中有一条注释说:

2FA 代码是使用Time-based One-time Password Algorithm 生成的,代码有效期为六分钟。如果您输入代码的时间超过 6 分钟,您将收到 Invalid code 错误消息。

所以,使用这种方法,你不能让代码在用户之后过期。

此外,您还可以保留已使用的代码存储库,并在验证代码之前检查该存储库。您可以允许代码在 6 分钟后从该商店过期,这是它们的自然过期时间,但同时使用它们来拒绝第二次身份验证。

或者,您可以选择避免使用 TOTP 方法,并在发送 SMS 或电子邮件之前生成一个随机代码存储在您的用户身上。然后,您可以在用户对其进行身份验证时检查该代码,并在此时删除或使该代码无效。使用 TOTP 意味着您可以扩展此 2FA 以使用基于身份验证器应用程序的流程进行身份验证,这比 SMS 或电子邮件更安全。

【讨论】:

【参考方案2】:

AspNetIdentity 不会自动使使用的第二因素代码失效,代码在六分钟内始终有效,但有一种解决方法。

令牌生成器的输入之一是SecurityStamp,它作为用户帐户的一部分存储。扩展TotpSecurityStampBasedTokenProvider 的令牌提供程序(例如EmailTokenProvider)将在生成和验证第二因素代码时使用安全标记。

因此,您可以通过在成功的两因素身份验证后调用UserManager.UpdateSecurityStampAsync(userId) 更改安全标记来使所有已发布的令牌无效。

有一个副作用可能是不可取的,即当安全标记更改时其他会话将被注销。

ApplicationSignInManager 类中,您可以覆盖TwoFactorSignInAsync 并在那里进行调用: (注意:这取自AspNetIdentity,如果您使用不同的包,请确保取自TwoFactorSignInAsync并相应地修改它。)

public override async Task<SignInStatus> TwoFactorSignInAsync(string provider, string code, bool isPersistent, bool rememberBrowser)

    var userId = await GetVerifiedUserIdAsync().WithCurrentCulture();
    if (userId == null)
    
        return SignInStatus.Failure;
    
    var user = await UserManager.FindByIdAsync(userId).WithCurrentCulture();
    if (user == null)
    
        return SignInStatus.Failure;
    
    if (await UserManager.IsLockedOutAsync(user.Id).WithCurrentCulture())
    
        return SignInStatus.LockedOut;
    
    if (await UserManager.VerifyTwoFactorTokenAsync(user.Id, provider, code).WithCurrentCulture())
    
        // When token is verified correctly, clear the access failed count used for lockout
        await UserManager.ResetAccessFailedCountAsync(user.Id).WithCurrentCulture();

        // Update the security stamp in order to invalidate all issued two factor tokens.
        await UserManager.UpdateSecurityStampAsync(user.Id);

        await SignInAsync(user, isPersistent, rememberBrowser).WithCurrentCulture();
        return SignInStatus.Success;
    
    // If the token is incorrect, record the failure which also may cause the user to be locked out
    await UserManager.AccessFailedAsync(user.Id).WithCurrentCulture();
    return SignInStatus.Failure;

如果您只希望最新发布的代码有效,您也应该在生成任何新代码之前调用UpdateSecurityStampAsync

【讨论】:

虽然这可行,但有一个副作用可能是不可取的,即当安全标记更改时其他会话将被注销。

以上是关于单次使用后如何使两因素身份验证令牌过期的主要内容,如果未能解决你的问题,请参考以下文章

为啥我的 JWT 不记名身份验证在令牌说 5 分钟后将令牌识别为过期?

如何使我的身份验证令牌不会过期?

.Net Core 中的两因素身份验证 - 如何验证令牌

如何使用 Firebase 刷新令牌保持用户身份验证?

使用访问令牌和刷新令牌保持身份验证

尝试进行身份验证时 JWT 令牌过期?