Недействительный токен ASP.NET Core Identity в электронном письме с подтверждением

Это очень похожий вопрос на этот вопрос Недействительный токен идентификации aspnet в электронной почте подтверждения но решения недействительны, потому что я использую новый ASP.NET Core 1.0, который включает в себя идентификатор ядра ASP.NET.

Мой сценарий выглядит следующим образом:

  • В конце (ядро ASP.NET) у меня есть функция, которая отправляет пароль reset по ссылке. Чтобы сгенерировать эту ссылку, мне нужно сгенерировать код с помощью 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);
    }
    

    это создаст электронное письмо со ссылкой на:

    http://myapp:8080/passwordreset?code=CfDJ8JBnWaVj6h1PtqlmlJaH57r9TRA5j7Ij1BVyeBUpqX+5Cq1msu9zgkuI32Iz9x/5uE1B9fKFp4tZFFy6lBTseDFTHSJxwtGu+jHX5cajptUBiVqIChiwoTODh7ei4+MOkX7rdNVBMhG4jOZWqqtZ5J30gXr/JmltbYxqOp4JLs8V05BeKDbbVO/Fsq5+jebokKkR5HEJU+mQ5MLvNURsJKRBbI3qIllj1RByXt9mufGRE3wmQf2fgKBkAL6VsNgB8w==

  • Затем мое приложение AngularJs представило бы представление с формой для ввода и подтверждения нового пароля и поместило бы объект JSON с новым паролем и кодом, полученным из параметра запроса в URL.

  • Наконец, мой задний конец получит запрос PUT, возьмет код и проверит его с помощью Identity следующим образом:

    [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();
    }
    

Проблема заключается в том, что Identity отвечает

Недопустимый токен

ошибка. Я обнаружил, что проблема в том, что коды не совпадают, и вышеуказанный код будет получен обратно в объекте JSON в запросе PUT следующим образом:

CfDJ8JBnWaVj6h1PtqlmlJaH57r9TRA5j7Ij1BVyeBUpqX 5Cq1msu9zgkuI32Iz9x/5uE1B9fKFp4tZFFy6lBTseDFTHSJxwtGu jHX5cajptUBiVqIChiwoTODh7ei4 MOkX7rdNVBMhG4jOZWqqtZ5J30gXr/JmltbYxqOp4JLs8V05BeKDbbVO/Fsq5 jebokKkR5HEJU mQ5MLvNURsJKRBbI3qIllj1RByXt9mufGRE3wmQf2fgKBkAL6VsNgB8w==

Обратите внимание, что там, где есть + символы, теперь есть пробелы , и, очевидно, это заставляет Identity думать, что токены разные. По какой-то причине Angular декодирует параметр запроса URL по-другому, который был закодирован.

Как разрешить это?

Ответ 1

Этот ответ fooobar.com/questions/171086/... указал мне в правильном направлении. Но, как я сказал, это было для другой версии, и теперь это немного другое решение.

Ответ все тот же: закодируйте токен в url базы 64, а затем декодируйте его в URL-адресе базы 64. Таким образом, оба Angular и ASP.NET Core получат тот же самый код.

Мне нужно было установить другую зависимость для Microsoft.AspNetCore.WebUtilities;

Теперь код будет примерно таким:

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);
}

и при возврате кода во время запроса 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);
}

Ответ 2

У меня была похожая проблема, и я кодировал свой токен, но он продолжал не проходить проверку, и проблема оказалась в следующем: options.LowercaseQueryStrings = true; Не устанавливайте true в options.LowercaseQueryStrings, это изменяет целостность токена проверки, и вы получите Invalid Token Error.

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