如何撤销/失效/取消旧的电子邮件确认令牌(身份)

myn*_*eff 3 asp.net asp.net-mvc asp.net-identity asp.net-identity-2 asp.net-core

我允许知道密码但尚未确认的新创建用户更改其注册电子邮件(只要未在我的数据库中注册)

问题是,如果他们更改了电子邮件,我将生成新的电子邮件确认令牌,但是旧令牌仍可以验证它们(我在注册时发出的令牌),这很可能意味着人们可以首先使用其注册邮件进行更改将其发送到他们无法访问的其他邮件,并通过旧邮件进行验证,这对我来说是一个很大的安全漏洞

有什么办法可以删除/撤销旧令牌?(从技术上讲,我可以创建一个新用户并删除旧用户,旧令牌将不适用于新用户,但是我认为应该为此提供更好的解决方案)

Nko*_*osi 6

我在ApplicationUser课堂上添加了以下属性

public class ApplicationUser : IdentityUser {
    public string EmailConfirmationToken { get; set; }
    public string ResetPasswordToken { get; set; }
}
Run Code Online (Sandbox Code Playgroud)

在确认电子邮件令牌时,这将保留要针对其进行验证的确认令牌。

然后添加以下到我ApplicationUserManager这是一个UserManager<ApplicationUser>派生类。

public override async System.Threading.Tasks.Task<string> GenerateEmailConfirmationTokenAsync(string userId) {
    /* NOTE:
     * The default UserTokenProvider generates tokens based on the users's SecurityStamp, so until that changes
     * (like when the user's password changes), the tokens will always be the same, and remain valid. 
     * So if you want to simply invalidate old tokens, just call manager.UpdateSecurityStampAsync().
     */
    //await base.UpdateSecurityStampAsync(userId);

    var token = await base.GenerateEmailConfirmationTokenAsync(userId);
    if (!string.IsNullOrEmpty(token)) {
        var user = await FindByIdAsync(userId);
        user.EmailConfirmationToken = token;
        user.EmailConfirmed = false;
        await UpdateAsync(user);
    }
    return token;
}

public override async System.Threading.Tasks.Task<string> GeneratePasswordResetTokenAsync(string userId) {
    var token = await base.GeneratePasswordResetTokenAsync(userId);
    if (!string.IsNullOrEmpty(token)) {
        var x = await FindByIdAsync(userId);
        x.ResetPasswordToken = token;
        await UpdateAsync(x);
    }
    return token;
}

public override async System.Threading.Tasks.Task<IdentityResult> ConfirmEmailAsync(string userId, string token) {
    var result = await base.ConfirmEmailAsync(userId, token);
    if (result.Succeeded) {
        var x = await FindByIdAsync(userId);
        x.EmailConfirmationToken = null;
        await UpdateAsync(x);
    }
    return result;
}

public override async System.Threading.Tasks.Task<IdentityResult> ResetPasswordAsync(string userId, string token, string newPassword) {
    var result = await base.ResetPasswordAsync(userId, token, newPassword);
    if (result.Succeeded) {
        var x = await FindByIdAsync(userId);
        x.ResetPasswordToken = null;
        await UpdateAsync(x);
    }
    return result;
}
Run Code Online (Sandbox Code Playgroud)

添加了以下扩展,以便能够根据其存储的令牌查找用户。

public static class ApplicationUserManagerExtension {
    public static Task<string> FindIdByEmailConfirmationTokenAsync(this UserManager<ApplicationUser> manager, string confirmationToken) {
        string result = null;

        ApplicationUser user = manager.Users.SingleOrDefault(u => u.EmailConfirmationToken != null && u.EmailConfirmationToken == confirmationToken);

        if (user != null) {
            result = user.Id;
        }

        return Task.FromResult(result);
    }

    public static Task<string> FindIdByResetPasswordTokenAsync(this UserManager<ApplicationUser> manager, string token) {
        string result = null;

        ApplicationUser user = manager.Users.SingleOrDefault(u => u.ResetPasswordToken != null && u.ResetPasswordToken == token);

        if (user != null) {
            result = user.Id;
        }

        return Task.FromResult(result);
    }
}
Run Code Online (Sandbox Code Playgroud)

  • 您的代码中的注释是我正在寻找的解决方案,更新了安全戳,使旧令牌无效,在发行新令牌之前我只是使用了它,这真是太好了,谢谢! (2认同)