Angularjs ASP.NET核心标识确认电子邮件上的无效令牌

Angularjs ASP.NET核心标识确认电子邮件上的无效令牌,angularjs,asp.net-core,asp.net-identity,forgot-password,Angularjs,Asp.net Core,Asp.net Identity,Forgot Password,这是一个与此非常相似的问题 但是这些解决方案无效,因为我正在使用新的ASP.NET Core 1.0,其中包含ASP.NET Core标识 我的设想如下: 在后端(ASP.NET核心),我有一个功能,可以发送一封带有链接的密码重置电子邮件。为了生成链接,我必须使用Identity生成代码。像这样的 public async Task SendPasswordResetEmailAsync(string email) { //_userManager is an instance of U

这是一个与此非常相似的问题 但是这些解决方案无效,因为我正在使用新的ASP.NET Core 1.0,其中包含ASP.NET Core标识

我的设想如下:

  • 在后端(ASP.NET核心),我有一个功能,可以发送一封带有链接的密码重置电子邮件。为了生成链接,我必须使用Identity生成代码。像这样的

    public async Task SendPasswordResetEmailAsync(string email)
    {
        //_userManager is an instance of UserManager<User>
        var userEntity = await _userManager.FindByNameAsync(email);
        var tokenGenerated = await _userManager.GeneratePasswordResetTokenAsync(userEntity);
        var link = Url.Action("MyAction", "MyController", new { email = email, code = tokenGenerated }, protocol: HttpContext.Request.Scheme);
         //this is my service that sends an email to the user containing the generated password reset link
         await _emailService.SendPasswordResetEmailAsync(userEntity , link);
    }
    
  • 问题在于,身份以一种特殊的方式回应

    无效令牌

    错误。我发现问题在于代码不匹配,上面的代码将在PUT请求的JSON对象中接收回来,如下所示:

    CfDJ8JBnWaVj6h1PtqlmlJaH57r9TRA5j7Ij1BVyeBUpqX 5Cq1msu9zgkuI32Iz9x/5ue1b9fkfp4tzfy6btsedthsjx5c5cajptubivqichiwotodh4ei4 mokx7rdvbmhg4jozwqqtz5j30gxr/jmltbyxqop4jls8v05bekbv0bv0bvo/fsqqo5 jebokKkR5HEJU mqlnursjkkkkrbj9mufg8wqfg2fg2fg8bqqlj8bqlj2f8==
    
    请注意,在有
    +
    符号的地方,现在有了空格
    符号,这显然会导致Identity认为标记是不同的。 出于某种原因,Angular正在以不同的编码方式解码URL查询参数

    如何解决这个问题?

    这个答案为我指明了正确的方向。但正如我所说,这是一个不同的版本,现在它是略有不同的解决方案

    答案仍然是一样的:在Base64URL中对令牌进行编码,然后在Base64URL中对其进行解码。这样,Angular和ASP.NET Core都将检索相同的代码

    我需要安装另一个依赖项到
    Microsoft.AspNetCore.WebUtilities

    现在代码应该是这样的:

    [HttpPut]
    [AllowAnonymous]
    [Route("api/password/{email}")]
    public async Task<IActionResult> SendPasswordEmailResetRequestAsync(string email, [FromBody] PasswordReset passwordReset)
    {
        //some irrelevant validatoins here
        await _myIdentityWrapperService.ResetPasswordAsync(email, passwordReset.Password, passwordReset.Code);
        return Ok();
    }
    
    public async Task SendPasswordResetEmailAsync(string email)
    {
        //_userManager is an instance of UserManager<User>
        var userEntity = await _userManager.FindByNameAsync(email);
        var tokenGenerated = await _userManager.GeneratePasswordResetTokenAsync(userEntity);
        byte[] tokenGeneratedBytes = Encoding.UTF8.GetBytes(tokenGenerated);
        var codeEncoded = WebEncoders.Base64UrlEncode(tokenGeneratedBytes);
        var link = Url.Action("MyAction", "MyController", new { email = email, code = codeEncoded }, protocol: HttpContext.Request.Scheme);
         //this is my service that sends an email to the user containing the generated password reset link
         await _emailService.SendPasswordResetEmailAsync(userEntity , link);
    }
    
    公共异步任务SendPasswordResetEmailAsync(字符串电子邮件)
    {
    //_userManager是userManager的一个实例
    var userEntity=await\u userManager.FindByNameAsync(电子邮件);
    var tokenGenerated=wait_userManager.GeneratePasswordResetTokenAsync(userEntity);
    byte[]tokenGeneratedBytes=Encoding.UTF8.GetBytes(tokenGenerated);
    var codeconded=WebEncoders.Base64UrlEncode(tokenGeneratedBytes);
    var link=Url.Action(“MyAction”,“MyController”,新的{email=email,code=codeconded},协议:HttpContext.Request.Scheme);
    //这是我的服务,它向用户发送一封包含生成的密码重置链接的电子邮件
    wait_emailService.SendPasswordResetEmailAsync(userEntity,link);
    }
    
    以及在PUT请求期间接收回代码时

    [HttpPut]
    [AllowAnonymous]
    [Route("api/password/{email}")]
    public async Task<IActionResult> SendPasswordEmailResetRequestAsync(string email, [FromBody] PasswordReset passwordReset)
    {
        //some irrelevant validatoins here
        await _myIdentityWrapperService.ResetPasswordAsync(email, passwordReset.Password, passwordReset.Code);
        return Ok();
    }
    
    //in MyIdentityWrapperService
    public async Task ResetPasswordAsync(string email, string password, string code)
    {
        var userEntity = await _userManager.FindByNameAsync(email);
        var codeDecodedBytes = WebEncoders.Base64UrlDecode(code);
        var codeDecoded = Encoding.UTF8.GetString(codeDecodedBytes);
        await _userManager.ResetPasswordAsync(userEntity, codeDecoded, password);
    }
    
    [HttpPut]
    [异名]
    [路由(“api/密码/{email}”)]
    公共异步任务SendPasswordEmailResetRequestAsync(字符串电子邮件,[FromBody]密码重置PasswordReset)
    {
    //这里有些不相干的东西
    wait _myIdentityWrapperService.ResetPasswordAsync(电子邮件、passwordReset.Password、passwordReset.Code);
    返回Ok();
    }
    //以我的身份为您服务
    公共异步任务ResetPasswordAsync(字符串电子邮件、字符串密码、字符串代码)
    {
    var userEntity=await\u userManager.FindByNameAsync(电子邮件);
    var codedecatedbytes=WebEncoders.Base64UrlDecode(代码);
    var codeDecoded=Encoding.UTF8.GetString(codeDecodedBytes);
    wait_userManager.ResetPasswordAsync(userEntity,codeDecoded,password);
    }
    
    我有一个类似的问题,我正在对我的令牌进行编码,但验证一直失败,问题是:
    options.LowercaseQueryStrings=true
    不要在
    选项上设置
    true
    。LowercaseQueryStrings
    这会改变验证令牌的完整性,您将得到无效令牌错误

    // This allows routes to be in lowercase
    services.AddRouting(options =>
    {
         options.LowercaseUrls = true;
          options.LowercaseQueryStrings = false;
    });
    
    

    在我的Asp.NETCore3.0项目中搭建了ConfirmEmail页面之后,我遇到了同样的问题

    ConfirmEmail.cshtml.cs
    中的
    OnGetAsync
    方法中删除以下行修复了该问题:

    code = Encoding.UTF8.GetString(WebEncoders.Base64UrlDecode(code));
    
    在脚手架登录页面中,将
    code
    添加到
    callbackUrl
    ,然后使用
    HtmlEncoder.Default.Encode(callbackUrl)
    对其进行URL编码。当点击链接时,解码会自动完成,
    code
    就像确认电子邮件一样

    更新:

    我注意到在忘记密码的过程中,
    code
    在放入
    callbackUrl
    之前是Base64编码的,这意味着Base64解码是必要的

    因此,更好的解决方案是在将代码添加到callbackUrl之前,将以下行添加到生成代码的任何位置

    code = WebEncoders.Base64UrlEncode(Encoding.UTF8.GetBytes(code));
    

    这里有一个到的链接,已经修复。

    我尝试了上面的答案,但这对我有所帮助。基本上,您需要对代码进行编码,否则会遇到一些奇怪的bug。总结一下,您需要这样做:

    string code = HttpUtility.UrlEncode(UserManager.GenerateEmailConfirmationToken(userID));
    
    在此之后,如果适用于您,请解码代码:

    string decoded = HttpUtility.UrlDecode(code)
    
    (据本报报道:)

    对于resetPasswordAsync(标识管理器)“令牌无效”问题。。。因为“+”变成了url中的空格。。。 使用Uri.EscapeUriString

    示例:在我的sendResetPasswordByMailAsync中

    var token = "Aa+Bb Cc";
    var encodedToken = Uri.EscapeDataString(token); 
    
    encodedToken=“Aa%20Bb2B%Cc”

    var url=$”http://localhost:4200/account/reset-密码?email={email}&token={encodedToken}”;
    var mailContent=$“请通过重置密码。”;
    

    现在你可以点击你的链接,你将进入一个带有“+”的好网址(按%2B编码)。。。您的令牌不会无效…

    对于ASP Core 2.1也有类似的问题,这让我很头疼,因为
    代码的任何编码/解码(
    令牌
    )对我都不起作用。对于
    userManager.confirmemailsync(用户,代码)


    解决方案: 事实证明,问题在于,用户不是使用
    UserManager
    创建的,而是使用dbcontext创建的,比如
    \u dbcontext.Users.AddAsync
    ,将此创建方法替换为
    \
    
    var url = $"http://localhost:4200/account/reset-password?email={email}&token={encodedToken}";
    var mailContent= $"Please reset your password by <a href='{url}'>clicking here</a>.";
    
    var callbackUrl = Url.Page(
                        "/Account/ConfirmEmail",
                        pageHandler: null,
                        values: new { userId = user.Id, code = code },
                        protocol: Request.Scheme);
    
    await _emailSender.SendEmailAsync(Input.Email, "Confirm your email",
                        $"Please confirm your account by <a href='{HtmlEncoder.Default.Encode(callbackUrl)}'>clicking here</a>.");
    
    var decode = token.Replace(" ", "+");
    
    await _userManager.ResetPasswordAsync(user, decode, Password);