ASP.NET ID 2에서 "잘못된 토큰" 오류가 발생함
저는 Asp를 사용하고 있습니다.Net-Identity-2와 저는 아래 방법으로 이메일 인증 코드를 확인하려고 합니다.그러나 "Invalid Token" 오류 메시지가 표시됩니다.
내 응용 프로그램의 사용자 관리자는 다음과 같습니다.
public class AppUserManager : UserManager<AppUser> { public AppUserManager(IUserStore<AppUser> store) : base(store) { } public static AppUserManager Create(IdentityFactoryOptions<AppUserManager> options, IOwinContext context) { AppIdentityDbContext db = context.Get<AppIdentityDbContext>(); AppUserManager manager = new AppUserManager(new UserStore<AppUser>(db)); manager.PasswordValidator = new PasswordValidator { RequiredLength = 6, RequireNonLetterOrDigit = false, RequireDigit = false, RequireLowercase = true, RequireUppercase = true }; manager.UserValidator = new UserValidator<AppUser>(manager) { AllowOnlyAlphanumericUserNames = true, RequireUniqueEmail = true }; var dataProtectionProvider = options.DataProtectionProvider; //token life span is 3 hours if (dataProtectionProvider != null) { manager.UserTokenProvider = new DataProtectorTokenProvider<AppUser> (dataProtectionProvider.Create("ConfirmationToken")) { TokenLifespan = TimeSpan.FromHours(3) }; } manager.EmailService = new EmailService(); return manager; } //Create } //class } //namespace
토큰을 생성하기 위한 My Action은 다음과 같습니다(여기서 토큰을 확인해도 "Invalid token" 메시지가 표시됨).
[AllowAnonymous] [HttpPost] [ValidateAntiForgeryToken] public ActionResult ForgotPassword(string email) { if (ModelState.IsValid) { AppUser user = UserManager.FindByEmail(email); if (user == null || !(UserManager.IsEmailConfirmed(user.Id))) { // Returning without warning anything wrong... return View("../Home/Index"); } //if string code = UserManager.GeneratePasswordResetToken(user.Id); string callbackUrl = Url.Action("ResetPassword", "Admin", new { Id = user.Id, code = HttpUtility.UrlEncode(code) }, protocol: Request.Url.Scheme); UserManager.SendEmail(user.Id, "Reset password Link", "Use the following link to reset your password: <a href=\"" + callbackUrl + "\">link</a>"); //This 2 lines I use tho debugger propose. The result is: "Invalid token" (???) IdentityResult result; result = UserManager.ConfirmEmail(user.Id, code); } // If we got this far, something failed, redisplay form return View(); } //ForgotPassword
토큰을 확인하기 위한 내 작업은 다음과 같습니다(여기서는 결과를 확인할 때 항상 "Invalid Token"이 표시됨).
[AllowAnonymous] public async Task<ActionResult> ResetPassword(string id, string code) { if (id == null || code == null) { return View("Error", new string[] { "Invalid params to reset password." }); } IdentityResult result; try { result = await UserManager.ConfirmEmailAsync(id, code); } catch (InvalidOperationException ioe) { // ConfirmEmailAsync throws when the id is not found. return View("Error", new string[] { "Error to reset password:<br/><br/><li>" + ioe.Message + "</li>" }); } if (result.Succeeded) { AppUser objUser = await UserManager.FindByIdAsync(id); ResetPasswordModel model = new ResetPasswordModel(); model.Id = objUser.Id; model.Name = objUser.UserName; model.Email = objUser.Email; return View(model); } // If we got this far, something failed. string strErrorMsg = ""; foreach(string strError in result.Errors) { strErrorMsg += "<li>" + strError + "</li>"; } //foreach return View("Error", new string[] { strErrorMsg }); } //ForgotPasswordConfirmation
뭐가 빠졌는지 뭐가 잘못됐는지 모르겠어요
저는 이 문제에 부딪혀 해결했습니다.몇 가지 가능한 이유가 있습니다.
URL 인코딩 문제("임의로" 문제가 발생하는 경우)
이 문제가 임의로 발생하면 URL 인코딩 문제가 발생할 수 있습니다.알 수 없는 이유로 인해 토큰이 url-safe용으로 설계되지 않았습니다. 즉, url을 통해 전달될 때(예: 전자 메일을 통해 전송되는 경우) 잘못된 문자가 포함될 수 있습니다.
이경에는우,,HttpUtility.UrlEncode(token)
그리고.HttpUtility.UrlDecode(token)
사용해야 합니다.
아웅 페레이라는 그의 논평에서 다음과 같이 말했습니다.UrlDecode
필수 항목이 아닌 경우도 있습니다.둘 다 먹어보세요.감사해요.
일치하지 않는 방법(전자 메일 토큰과 암호 토큰)
예:
var code = await userManager.GenerateEmailConfirmationTokenAsync(user.Id);
그리고.
var result = await userManager.ResetPasswordAsync(user.Id, code, newPassword);
전자 메일 토큰 제공자가 생성한 토큰을 reset-password-token-provider에서 확인할 수 없습니다.
하지만 우리는 왜 이런 일이 일어나는지 근본적인 원인을 보게 될 것입니다.
토큰 공급자의 다른 인스턴스
다음을 사용하는 경우에도:
var token = await _userManager.GeneratePasswordResetTokenAsync(user.Id);
와 함께
var result = await _userManager.ResetPasswordAsync(user.Id, HttpUtility.UrlDecode(token), newPassword);
오류가 여전히 발생할 수 있습니다.
이전 코드는 다음과 같은 이유를 보여줍니다.
public class AccountController : Controller
{
private readonly UserManager _userManager = UserManager.CreateUserManager();
[AllowAnonymous]
[HttpPost]
public async Task<ActionResult> ForgotPassword(FormCollection collection)
{
var token = await _userManager.GeneratePasswordResetTokenAsync(user.Id);
var callbackUrl = Url.Action("ResetPassword", "Account", new { area = "", UserId = user.Id, token = HttpUtility.UrlEncode(token) }, Request.Url.Scheme);
Mail.Send(...);
}
그리고:
public class UserManager : UserManager<IdentityUser>
{
private static readonly UserStore<IdentityUser> UserStore = new UserStore<IdentityUser>();
private static readonly UserManager Instance = new UserManager();
private UserManager()
: base(UserStore)
{
}
public static UserManager CreateUserManager()
{
var dataProtectionProvider = new DpapiDataProtectionProvider();
Instance.UserTokenProvider = new DataProtectorTokenProvider<IdentityUser>(dataProtectionProvider.Create());
return Instance;
}
이 코드에서는 항상 주의하십시오.UserManager
생됨또성)new
-ed), 규신dataProtectionProvider
또한 생성됩니다.따라서 사용자가 이메일을 수신하고 링크를 클릭하면 다음과 같습니다.
public class AccountController : Controller
{
private readonly UserManager _userManager = UserManager.CreateUserManager();
[HttpPost]
[AllowAnonymous]
[ValidateAntiForgeryToken]
public async Task<ActionResult> ResetPassword(string userId, string token, FormCollection collection)
{
var result = await _userManager.ResetPasswordAsync(user.Id, HttpUtility.UrlDecode(token), newPassword);
if (result != IdentityResult.Success)
return Content(result.Errors.Aggregate("", (current, error) => current + error + "\r\n"));
return RedirectToAction("Login");
}
그AccountController
더 이상 오래된 것이 아니며, 그것도 마찬가지입니다._userManager
토큰 제공업체입니다.따라서 새 토큰 공급자는 메모리에 해당 토큰이 없기 때문에 실패합니다.
따라서 토큰 공급자에 대해 단일 인스턴스를 사용해야 합니다.여기 제 새 코드가 있는데 잘 작동합니다.
public class UserManager : UserManager<IdentityUser>
{
private static readonly UserStore<IdentityUser> UserStore = new UserStore<IdentityUser>();
private static readonly UserManager Instance = new UserManager();
private UserManager()
: base(UserStore)
{
}
public static UserManager CreateUserManager()
{
//...
Instance.UserTokenProvider = TokenProvider.Provider;
return Instance;
}
그리고:
public static class TokenProvider
{
[UsedImplicitly] private static DataProtectorTokenProvider<IdentityUser> _tokenProvider;
public static DataProtectorTokenProvider<IdentityUser> Provider
{
get
{
if (_tokenProvider != null)
return _tokenProvider;
var dataProtectionProvider = new DpapiDataProtectionProvider();
_tokenProvider = new DataProtectorTokenProvider<IdentityUser>(dataProtectionProvider.Create());
return _tokenProvider;
}
}
}
고상한 해결책이라고 할 수는 없었지만, 그것이 뿌리를 내리고 제 문제를 해결해 주었습니다.
여기서 암호 재설정을 위한 토큰을 생성하고 있기 때문입니다.
string code = UserManager.GeneratePasswordResetToken(user.Id);
하지만 실제로 이메일에 대한 토큰의 유효성을 확인하려고 합니다.
result = await UserManager.ConfirmEmailAsync(id, code);
이것들은 두 개의 다른 토큰입니다.
질문에서 전자 메일을 확인하려고 하지만 암호 재설정을 위한 코드라고 말합니다.당신은 어떤 것을 하고 있습니까?
이메일 확인이 필요한 경우 다음을 통해 토큰을 생성
var emailConfirmationCode = await UserManager.GenerateEmailConfirmationTokenAsync(user.Id);
를 통해 확인합니다.
var confirmResult = await UserManager.ConfirmEmailAsync(userId, code);
암호 재설정이 필요한 경우 다음과 같은 토큰을 생성합니다.
var code = await UserManager.GeneratePasswordResetTokenAsync(user.Id);
다음과 같이 확인합니다.
var resetResult = await userManager.ResetPasswordAsync(user.Id, code, newPassword);
다음과 같은 코드에서도 "Invalid Token" 오류가 발생했습니다.
var emailCode = UserManager.GenerateEmailConfirmationToken(id);
var result = UserManager.ConfirmEmail(id, emailCode);
제 경우 문제는 사용자를 수동으로 생성하고 메소드를 사용하지 않고 데이터베이스에 추가하는 것이었습니다.사용자가 데이터베이스에 있지만 보안 스탬프가 없습니다.
흥미로운 것은GenerateEmailConfirmationToken
보안 스탬프 부족에 대해 불평하지 않고 토큰을 반환했지만 해당 토큰은 유효성을 검사할 수 없습니다.
그 외에도 코드 자체가 인코딩되지 않으면 실패하는 것을 보았습니다.
최근에 다음과 같은 방식으로 인코딩을 시작했습니다.
string code = manager.GeneratePasswordResetToken(user.Id);
code = HttpUtility.UrlEncode(code);
그리고 다시 읽을 준비가 되면,
string code = IdentityHelper.GetCodeFromRequest(Request);
code = HttpUtility.UrlDecode(code);
솔직히 말해서, 저는 애초에 그것이 제대로 인코딩되지 않았다는 것에 놀랐습니다.
저 같은 경우에는 저희 앵귤러.JS 앱이 모든 더하기 기호(+)를 빈 공간(" ")으로 변환했으므로 토큰이 다시 전달될 때 실제로 유효하지 않습니다.
이 문제를 해결하기 위해 계정 컨트롤러의 암호 재설정 방법에서 암호를 업데이트하기 전에 간단히 대체 항목을 추가했습니다.
code = code.Replace(" ", "+");
IdentityResult result = await AppUserManager.ResetPasswordAsync(user.Id, code, newPassword);
저는 이것이 웹 API와 Angular에서 Identity로 일하는 다른 사람들에게 도움이 되기를 바랍니다.제이에스
tl;dr: MachineKey 보호 대신 AES 암호화를 사용하려면 aspnet core 2.2에 사용자 지정 토큰 공급자를 등록하십시오. gist: https://gist.github.com/cyptus/dd9b2f90c190aaed4e807177c45c3c8b
저는 같은 문제에 부딪혔습니다.aspnet core 2.2
Ascheny는 토큰 제공자의 인스턴스가 동일해야 한다고 지적했습니다.이것은 나에게 효과가 없습니다 왜냐하면
- 받았습니다
different API-projects
하기 위해 합니다. - 는 API 실행수있니에서 될 수 .
different instances
시스템 동일하지 수 . , 시스템 키는 동일하지 않습니다. - 는 API일 수 있습니다.
restart
은 " ▁it"가 아니기 때문에 가 됩니다.same instance
이상
나는 사용할 수 있습니다.services.AddDataProtection().PersistKeysToFileSystem(new DirectoryInfo("path"))
토큰을 파일 시스템에 저장하고 재시작 및 여러 인스턴스 공유 문제를 방지하기 위해 각 프로젝트가 하나의 파일을 생성하므로 여러 프로젝트와 관련된 문제를 해결할 수 없습니다.
데이터 을 MachineKey를 입니다.AES then HMAC
시스템, 인스턴스 및 프로젝트 간에 공유할 수 있는 내 설정의 키로 토큰을 대칭 암호화합니다.Encrypt(암호화)에서 암호화 로직을 가져와 C#의 문자열을 해독했습니다(Gist: https://gist.github.com/jbtule/4336842#file-aesthenhmac-cs) 에서 사용자 지정 TokenProvider:
public class AesDataProtectorTokenProvider<TUser> : DataProtectorTokenProvider<TUser> where TUser : class
{
public AesDataProtectorTokenProvider(IOptions<DataProtectionTokenProviderOptions> options, ISettingSupplier settingSupplier)
: base(new AesProtectionProvider(settingSupplier.Supply()), options)
{
var settingsLifetime = settingSupplier.Supply().Encryption.PasswordResetLifetime;
if (settingsLifetime.TotalSeconds > 1)
{
Options.TokenLifespan = settingsLifetime;
}
}
}
public class AesProtectionProvider : IDataProtectionProvider
{
private readonly SystemSettings _settings;
public AesProtectionProvider(SystemSettings settings)
{
_settings = settings;
if(string.IsNullOrEmpty(_settings.Encryption.AESPasswordResetKey))
throw new ArgumentNullException("AESPasswordResetKey must be set");
}
public IDataProtector CreateProtector(string purpose)
{
return new AesDataProtector(purpose, _settings.Encryption.AESPasswordResetKey);
}
}
public class AesDataProtector : IDataProtector
{
private readonly string _purpose;
private readonly SymmetricSecurityKey _key;
private readonly Encoding _encoding = Encoding.UTF8;
public AesDataProtector(string purpose, string key)
{
_purpose = purpose;
_key = new SymmetricSecurityKey(Encoding.UTF8.GetBytes(key));
}
public byte[] Protect(byte[] userData)
{
return AESThenHMAC.SimpleEncryptWithPassword(userData, _encoding.GetString(_key.Key));
}
public byte[] Unprotect(byte[] protectedData)
{
return AESThenHMAC.SimpleDecryptWithPassword(protectedData, _encoding.GetString(_key.Key));
}
public IDataProtector CreateProtector(string purpose)
{
throw new NotSupportedException();
}
}
그리고 내 프로젝트에서 내가 내 설정을 제공하기 위해 사용하는 SettingsSupplier.
public interface ISettingSupplier
{
SystemSettings Supply();
}
public class SettingSupplier : ISettingSupplier
{
private IConfiguration Configuration { get; }
public SettingSupplier(IConfiguration configuration)
{
Configuration = configuration;
}
public SystemSettings Supply()
{
var settings = new SystemSettings();
Configuration.Bind("SystemSettings", settings);
return settings;
}
}
public class SystemSettings
{
public EncryptionSettings Encryption { get; set; } = new EncryptionSettings();
}
public class EncryptionSettings
{
public string AESPasswordResetKey { get; set; }
public TimeSpan PasswordResetLifetime { get; set; } = new TimeSpan(3, 0, 0, 0);
}
마지막으로 시작에서 제공자를 등록합니다.
services
.AddIdentity<AppUser, AppRole>()
.AddEntityFrameworkStores<AppDbContext>()
.AddDefaultTokenProviders()
.AddTokenProvider<AesDataProtectorTokenProvider<AppUser>>(TokenOptions.DefaultProvider);
services.AddScoped(typeof(ISettingSupplier), typeof(SettingSupplier));
//AESThenHMAC.cs: See https://gist.github.com/jbtule/4336842#file-aesthenhmac-cs
string code = _userManager.GeneratePasswordResetToken(user.Id);
code = HttpUtility.UrlEncode(code);
//휴식 이메일 보내기
코드를 디코딩하지 않음
var result = await _userManager.ResetPasswordAsync(user.Id, model.Code, model.Password);
제가 한 일은 다음과 같습니다: URL을 위해 인코딩한 후 토큰을 디코딩합니다(간단히).
먼저 사용자를 인코딩하여 전자 메일 생성 확인을 확인해야 했습니다.생성된 토큰입니다.(표준 위의 조언)
var token = await userManager.GenerateEmailConfirmationTokenAsync(user);
var encodedToken = HttpUtility.UrlEncode(token);
컨트롤러의 "확인" 작업에서 토큰을 확인하기 전에 디코딩해야 했습니다.
var decodedCode = HttpUtility.UrlDecode(mViewModel.Token);
var result = await userManager.ConfirmEmailAsync(user,decodedCode);
여기서 저는 같은 문제를 겪고 있지만 오랜 시간이 지난 후에 제 경우 사용자 지정 계정 클래스가 ID 속성을 다시 선언하고 재정의했기 때문에 잘못된 토큰 오류가 발생했다는 것을 발견했습니다.
그런 식으로:
public class Account : IdentityUser
{
[ScaffoldColumn(false)]
public override string Id { get; set; }
//Other properties ....
}
이 문제를 해결하기 위해 해당 속성을 제거하고 데이터베이스 스키마를 다시 생성했습니다.
이것을 제거하면 문제가 해결됩니다.
asp.net core로 이 문제를 해결한 후에 저는 Startup:에서 이 옵션을 활성화했다는 것을 깨달았습니다.
services.Configure<RouteOptions>(options =>
{
options.LowercaseQueryStrings = true;
});
이것은 당연히 쿼리 문자열에 있던 토큰을 무효화했습니다.
WebApi에서 도움이 되는 솔루션은 다음과 같습니다.
등록.
var result = await _userManager.CreateAsync(user, model.Password);
if (result.Succeeded) {
EmailService emailService = new EmailService();
var url = _configuration["ServiceName"];
var token = await _userManager.GenerateEmailConfirmationTokenAsync(user);
var encodedToken = HttpUtility.UrlEncode(token);
// .Net Core 2.1, Url.Action return null
// Url.Action("confirm", "account", new { userId = user.Id, code = token }, protocol: HttpContext.Request.Scheme);
var callbackUrl = _configuration["ServiceAddress"] + $"/account/confirm?userId={user.Id}&code={encodedToken}";
var message = emailService.GetRegisterMailTemplate(callbackUrl, url);
await emailService.SendEmailAsync( model.Email, $"please confirm your registration {url}", message );
}
확인.
[Route("account/confirm")]
[AllowAnonymous]
[HttpGet]
public async Task<IActionResult> ConfirmEmail(string userId, string code) {
if (userId == null || code == null) {
return Content(JsonConvert.SerializeObject( new { result = "false", message = "data is incorrect" }), "application/json");
}
var user = await _userManager.FindByIdAsync(userId);
if (user == null) {
return Content(JsonConvert.SerializeObject(new { result = "false", message = "user not found" }), "application/json");
}
//var decodedCode = HttpUtility.UrlDecode(code);
//var result = await _userManager.ConfirmEmailAsync(user, decodedCode);
var result = await _userManager.ConfirmEmailAsync(user, code);
if (result.Succeeded)
return Content(JsonConvert.SerializeObject(new { result = "true", message = "ок", token = code }), "application/json");
else
return Content(JsonConvert.SerializeObject(new { result = "false", message = "confirm error" }), "application/json");
}
@cheny가 올린 #3 솔루션에 영감을 받아, 같은 것을 사용할 수 있다는 것을 깨달았습니다.UserManager
인스턴스(instance)가 생성된 코드를 허용합니다.그러나 실제 시나리오에서는 사용자가 이메일 링크를 클릭한 후 두 번째 API 호출에서 유효성 검사 코드가 발생합니다.은 그은새사례의 합니다.UserManager
이 생성되고 첫 번째 호출의 첫 번째 인스턴스에서 생성된 코드를 확인할 수 없습니다.그것이 작동하도록 만드는 유일한 방법은 확실히 하는 것입니다.SecurityStamp
데이터베이스 사용자 테이블의 열입니다.사중인클등록을 하는 클래스UserManager
싱글톤은 애플리케이션 시작 시 예외를 던집니다. 왜냐하면UserManager
으로 클스가자에등다니됩록에 됩니다.Scoped
생성 시 다음을 사용해야 합니다.
GeneratePasswordResetTokenAsync(user.Id)
사용을 확인합니다.
ResetPasswordAsync(user.Id, model.Code, model.Password)
일치하는 방법을 사용하고 있지만 여전히 작동하지 않는 경우 확인하십시오.user.Id
두 방법 모두 동일합니다. (가끔 레지스트리 등에 동일한 전자 메일 사용을 허용하기 때문에 논리가 올바르지 않을 수 있습니다.)
어쩌면 이것은 오래된 스레드일지도 모르지만, 만약을 위해, 저는 이 오류의 무작위적인 발생으로 머리를 긁적이고 있습니다.저는 각 제안에 대한 모든 스레드를 확인하고 확인해왔지만, "잘못된 토큰"으로 반환된 코드 중 일부는 무작위로 보였습니다.사용자 데이터베이스에 대한 몇 가지 쿼리를 통해 사용자 이름의 공백 또는 기타 영숫자가 아닌 문자와 직접 관련된 "잘못된 토큰" 오류를 발견했습니다.그 당시에는 솔루션을 찾기가 쉬웠습니다.사용자 이름에 이러한 문자를 허용하도록 사용자 관리자를 구성하기만 하면 됩니다.이 작업은 사용자 관리자가 이벤트를 생성한 직후에 수행할 수 있으며, 새 UserValidator 설정을 추가하여 다음과 같이 해당 속성을 거짓으로 만들 수 있습니다.
public static UserManager<User> Create(IdentityFactoryOptions<UserManager<User>> options, IOwinContext context)
{
var userManager = new UserManager<User>(new UserStore());
// this is the key
userManager.UserValidator = new UserValidator<User>(userManager) { AllowOnlyAlphanumericUserNames = false };
// other settings here
userManager.UserLockoutEnabledByDefault = true;
userManager.MaxFailedAccessAttemptsBeforeLockout = 5;
userManager.DefaultAccountLockoutTimeSpan = TimeSpan.FromDays(1);
var dataProtectionProvider = options.DataProtectionProvider;
if (dataProtectionProvider != null)
{
userManager.UserTokenProvider = new DataProtectorTokenProvider<User>(dataProtectionProvider.Create("ASP.NET Identity"))
{
TokenLifespan = TimeSpan.FromDays(5)
};
}
return userManager;
}
이것이 저와 같은 "늦은 도착"에 도움이 되기를 바랍니다!
생성한 토큰이 빠르게 만료되지 않도록 하십시오. 테스트를 위해 토큰을 10초로 변경하면 항상 오류가 반환됩니다.
if (dataProtectionProvider != null) {
manager.UserTokenProvider =
new DataProtectorTokenProvider<AppUser>
(dataProtectionProvider.Create("ConfirmationToken")) {
TokenLifespan = TimeSpan.FromHours(3)
//TokenLifespan = TimeSpan.FromSeconds(10);
};
}
사용자들이 모두 정상적으로 작동하는 상황에 직면했습니다.Symantec의 전자 메일 보호 시스템으로 분리했습니다. Symantec은 전자 메일의 링크를 사용자의 사이트로 이동하여 유효성을 검사한 후 사용자를 원래 링크로 리디렉션합니다.
문제는 그들이 디코드를 도입하고 있다는 것입니다...생성된 링크에서 URL 인코딩을 수행하여 사이트에 대한 쿼리 매개 변수로 링크를 삽입한 다음 사용자가 safe.symantec을 클릭하면 해당 링크가 표시됩니다.com은 인코딩해야 하는 첫 번째 부분뿐만 아니라 쿼리 문자열의 내용도 디코딩하고 브라우저가 리디렉션되는 URL도 디코딩하여 특수 문자가 뒤에 있는 코드에서 쿼리 문자열 처리를 엉망으로 만드는 상태로 돌아갑니다.
저 같은 경우에는 Http Utility만 하면 됩니다.전자 메일을 보내기 전에 UrlEncode.Http 유틸리티가 없습니다.재설정 중인 UrlDecode입니다.
Chenny의 3과 관련이 있습니다. 토큰 공급자의 다른 인스턴스입니다.
저 같은 경우에는 그냥 지나쳤어요.IDataProtectionProvider.Create
호출될 때마다 새로운 guid를 사용하여 후속 웹 API 호출에서 기존 코드를 인식할 수 없습니다(각 요청은 자체 사용자 관리자를 만듭니다).
끈을 정적으로 만들어서 해결했습니다.
private static string m_tokenProviderId = "MyApp_" + Guid.NewGuid().ToString();
...
manager.UserTokenProvider =
new DataProtectorTokenProvider<User>(
dataProtectionProvider.Create(new string[1] { m_tokenProviderId } ))
{
TokenLifespan = TimeSpan.FromMinutes(accessTokenLifespan)
};
누군가가 이것에 부딪힐 경우, 토큰이 URL 친화적이지 않다는 것이 밝혀졌고, 그래서 저는 그것을 Http Utility로 포장해야 했습니다.UrlEncode()는 다음과 같습니다.
var callback = Url.Content($"{this.Request.Scheme}://{this.Request.Host}{this.Request.PathBase}/reset-password?token={HttpUtility.UrlEncode(token)}&email={user.Email}");
설명된 힌트의 대부분은 "Invalid Token" 문제를 해결했습니다.여기 블레이저 프로젝트에 대한 제 해결책이 있습니다.은 핵은심에 .StringExtensions
학생들
사용자가 전자 메일을 등록할 때 전자 메일 생성:
user = new IdentityUser { UserName = email, Email = email };
var createUser = await _userManager.CreateAsync(user, password);
if (createUser.Succeeded)
{
var code = await _userManager.GenerateEmailConfirmationTokenAsync(user);
var baseUri = NavMgr.BaseUri;
var setNewPasswordUri = baseUri + "confirm-password";
var urlWithParams = StringExtensions.GenerateUrl(token, emailTo, url);
await SendAsync( urlWithParams ); // use your own Email solution send the email
}
이메일 확인(사용자가 메일의 링크를 클릭)
@page "/confirm-email"
<h3>Confirm email</h3>
@Error
[Inject]
UserManager<IdentityUser> UserMgr { get; set; }
[Inject]
NavigationManager NavMgr { get; set; }
protected override Task OnInitializedAsync()
{
var url = NavMgr.Uri;
Token = StringExtensions.GetParamFromUrl(url, "token");
Email = StringExtensions.GetParamFromUrl(url, "email");
log.Trace($"Initialised with email={Email} , token={Token}");
return ActivateEmailAsync();
}
private async Task ActivateEmailAsync()
{
isProcessing = true;
Error = null;
log.Trace($"ActivateEmailAsync started for {Email}");
isProcessing = true;
Error = null;
try
{
var user = await UserMgr.FindByEmailAsync(Email);
if (user != null)
{
if (!string.IsNullOrEmpty(Token))
{
var result = await UserMgr.ConfirmEmailAsync(user, Token);
if (result.Succeeded)
{
// Show user , that account is activated
}
else
{
foreach (var error in result.Errors)
{
Error += error.Description;
}
log.Error($"Setting new password failed for {Email} due to the: {Error}");
}
}
else
{
log.Error("This should not happen. Token is null or empty");
}
}
}
catch (Exception exc)
{
Error = $"Activation failed";
}
isProcessing = false;
}
public static class StringExtensions
{
/// <summary>
/// Encode string to be safe to use it in the URL param
/// </summary>
/// <param name="toBeEncoded"></param>
/// <returns></returns>
public static string Encode(string toBeEncoded)
{
var result = WebEncoders.Base64UrlEncode(Encoding.UTF8.GetBytes(toBeEncoded));
return result;
}
/// <summary>
/// Decode from the url safe string the original value
/// </summary>
/// <param name="toBeDecoded"></param>
/// <returns></returns>
public static string Decode(string toBeDecoded)
{
var decodedBytes = WebEncoders.Base64UrlDecode(toBeDecoded);
var result = Encoding.UTF8.GetString(decodedBytes);
return result;
}
public static string GenerateUrl(string token, string emailTo, string baseUri, string tokenParamName = "token", string emailParamName = "email")
{
var tokenEncoded = StringExtensions.Encode(token);
var emailEncoded = StringExtensions.Encode(emailTo);
var queryParams = new Dictionary<string, string>();
queryParams.Add(tokenParamName, tokenEncoded);
queryParams.Add(emailParamName, emailEncoded);
var urlWithParams = QueryHelpers.AddQueryString(baseUri, queryParams);
return urlWithParams;
}
public static string GetParamFromUrl(string uriWithParams, string paramName)
{
var uri = new Uri(uriWithParams, UriKind.Absolute);
var result = string.Empty;
if (QueryHelpers.ParseQuery(uri.Query).TryGetValue(paramName, out var paramToken))
{
var queryToken = paramToken.First();
result = StringExtensions.Decode(queryToken);
}
return result;
}
암호 재설정 시나리오에서 잘못된 토큰이 발생했습니다.에 대한 리셋 하고 있었기 때문입니다.IndentityUser
단순화된 코드에서는 쉽게 발견할 수 있지만, 좀 더 복잡한 코드에서는 시간이 좀 걸렸습니다.
코드를 사용했어야 했습니다.
var user = await UserMgr.FindByEmailAsync(Model.Email);
string resetToken = await _userManager.GeneratePasswordResetTokenAsync(user);
요 ( 하만내다어요했잘못것 (가만는것드을지른다▁another것)▁(▁but요는)IndentityUser
).
// This is example "How it should not be done"
var user = await UserMgr.FindByEmailAsync(Model.Email);
user = new IdentityUser { UserName = email, Email = email }; // This must not be her !!!! We need to use user found by UserMgr.FindByEmailAsync(Model.Email);
string resetToken = await _userManager.GeneratePasswordResetTokenAsync(user);
전체 단순화된 코드는 다음과 같습니다.
private async Task GenerateResetToken()
{
var user = await UserMgr.FindByEmailAsync(Model.Email);
if (user == null)
{
Model.Error = "Not registered";
}
else
{
try
{
var _userManager = SignInMgr.UserManager;
UserMgr.FindByEmailAsync(Model.Email);
string resetToken = await _userManager.GeneratePasswordResetTokenAsync(user);
if (resetToken == null)
{
log.Error("Cannot get token from GeneratePasswordResetTokenAsync");
}
else
{
// Reset token generated. Send email to user
}
}
catch (Exception exc)
{
log.Error(exc, $"Password reset failed due to the {exc.Message}");
}
}
}
제 문제는 확인이 포함된 이메일에 오타가 있다는 것입니다.토큰:
<p>Please confirm your account by <a href=@ViewBag.CallbackUrl'>clicking here</a>.</p>
이것은 추가 아포스트로피가 확인의 끝에 추가되었다는 것을 의미합니다.상품권.
안 돼요!
내 문제는 내가 a를 놓쳤다는 것이었습니다.<input asp-for="Input.Code" type="hidden" />
양식의 내호재양의어제식정
<form role="form" method="post">
<div asp-validation-summary="All" class="text-danger"></div>
<input asp-for="Input.Code" type="hidden" />
언급URL : https://stackoverflow.com/questions/25405307/asp-net-identity-2-giving-invalid-token-error
'programing' 카테고리의 다른 글
Mongoid 파인더가 작동하지 않나요? (0) | 2023.06.24 |
---|---|
파일 확장명을 기반으로 Gitdiff를 필터링하는 방법은 무엇입니까? (0) | 2023.06.24 |
앱스토어 릴리스 시 증가해야 하는 iOS 앱 버전/빌드 번호는 무엇입니까? (0) | 2023.06.24 |
세 번째 게시물마다 루프를 다시 실행하시겠습니까? (Wordpress) (0) | 2023.06.24 |
SQL의 DATTIME 열에서 DATE를 가져오는 방법은 무엇입니까? (0) | 2023.06.24 |