1 // Copyright (c) .NET Foundation. All rights reserved. 2 // Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. 3 4 using System; 5 using System.Collections.Generic; 6 using System.Globalization; 7 using System.Linq; 8 using System.Security.Claims; 9 using System.Security.Cryptography; 10 using System.Text; 11 using System.Threading; 12 using System.Threading.Tasks; 13 using Microsoft.Extensions.DependencyInjection; 14 using Microsoft.Extensions.Identity.Core; 15 using Microsoft.Extensions.Logging; 16 using Microsoft.Extensions.Options; 17 18 namespace Microsoft.AspNetCore.Identity 19 { 20 /// <summary> 21 /// Provides the APIs for managing user in a persistence store. 22 /// </summary> 23 /// <typeparam name="TUser">The type encapsulating a user.</typeparam> 24 public class UserManager<TUser> : IDisposable where TUser : class 25 { 26 /// <summary> 27 /// The data protection purpose used for the reset password related methods. 28 /// </summary> 29 public const string ResetPasswordTokenPurpose = "ResetPassword"; 30 31 /// <summary> 32 /// The data protection purpose used for the change phone number methods. 33 /// </summary> 34 public const string ChangePhoneNumberTokenPurpose = "ChangePhoneNumber"; 35 36 /// <summary> 37 /// The data protection purpose used for the email confirmation related methods. 38 /// </summary> 39 public const string ConfirmEmailTokenPurpose = "EmailConfirmation"; 40 41 private readonly Dictionary<string, IUserTwoFactorTokenProvider<TUser>> _tokenProviders = 42 new Dictionary<string, IUserTwoFactorTokenProvider<TUser>>(); 43 44 private TimeSpan _defaultLockout = TimeSpan.Zero; 45 private bool _disposed; 46 private static readonly RandomNumberGenerator _rng = RandomNumberGenerator.Create(); 47 private IServiceProvider _services; 48 49 /// <summary> 50 /// The cancellation token used to cancel operations. 51 /// </summary> 52 protected virtual CancellationToken CancellationToken => CancellationToken.None; 53 54 /// <summary> 55 /// Constructs a new instance of <see cref="UserManager{TUser}"/>. 56 /// </summary> 57 /// <param name="store">The persistence store the manager will operate over.</param> 58 /// <param name="optionsAccessor">The accessor used to access the <see cref="IdentityOptions"/>.</param> 59 /// <param name="passwordHasher">The password hashing implementation to use when saving passwords.</param> 60 /// <param name="userValidators">A collection of <see cref="IUserValidator{TUser}"/> to validate users against.</param> 61 /// <param name="passwordValidators">A collection of <see cref="IPasswordValidator{TUser}"/> to validate passwords against.</param> 62 /// <param name="keyNormalizer">The <see cref="ILookupNormalizer"/> to use when generating index keys for users.</param> 63 /// <param name="errors">The <see cref="IdentityErrorDescriber"/> used to provider error messages.</param> 64 /// <param name="services">The <see cref="IServiceProvider"/> used to resolve services.</param> 65 /// <param name="logger">The logger used to log messages, warnings and errors.</param> UserManager(IUserStore<TUser> store, IOptions<IdentityOptions> optionsAccessor, IPasswordHasher<TUser> passwordHasher, IEnumerable<IUserValidator<TUser>> userValidators, IEnumerable<IPasswordValidator<TUser>> passwordValidators, ILookupNormalizer keyNormalizer, IdentityErrorDescriber errors, IServiceProvider services, ILogger<UserManager<TUser>> logger)66 public UserManager(IUserStore<TUser> store, 67 IOptions<IdentityOptions> optionsAccessor, 68 IPasswordHasher<TUser> passwordHasher, 69 IEnumerable<IUserValidator<TUser>> userValidators, 70 IEnumerable<IPasswordValidator<TUser>> passwordValidators, 71 ILookupNormalizer keyNormalizer, 72 IdentityErrorDescriber errors, 73 IServiceProvider services, 74 ILogger<UserManager<TUser>> logger) 75 { 76 if (store == null) 77 { 78 throw new ArgumentNullException(nameof(store)); 79 } 80 Store = store; 81 Options = optionsAccessor?.Value ?? new IdentityOptions(); 82 PasswordHasher = passwordHasher; 83 KeyNormalizer = keyNormalizer; 84 ErrorDescriber = errors; 85 Logger = logger; 86 87 if (userValidators != null) 88 { 89 foreach (var v in userValidators) 90 { 91 UserValidators.Add(v); 92 } 93 } 94 if (passwordValidators != null) 95 { 96 foreach (var v in passwordValidators) 97 { 98 PasswordValidators.Add(v); 99 } 100 } 101 102 _services = services; 103 if (services != null) 104 { 105 foreach (var providerName in Options.Tokens.ProviderMap.Keys) 106 { 107 var description = Options.Tokens.ProviderMap[providerName]; 108 109 var provider = (description.ProviderInstance ?? services.GetRequiredService(description.ProviderType)) 110 as IUserTwoFactorTokenProvider<TUser>; 111 if (provider != null) 112 { 113 RegisterTokenProvider(providerName, provider); 114 } 115 } 116 } 117 118 if (Options.Stores.ProtectPersonalData) 119 { 120 if (!(Store is IProtectedUserStore<TUser>)) 121 { 122 throw new InvalidOperationException(Resources.StoreNotIProtectedUserStore); 123 } 124 if (services.GetService<ILookupProtector>() == null) 125 { 126 throw new InvalidOperationException(Resources.NoPersonalDataProtector); 127 } 128 } 129 } 130 131 /// <summary> 132 /// Gets or sets the persistence store the manager operates over. 133 /// </summary> 134 /// <value>The persistence store the manager operates over.</value> 135 protected internal IUserStore<TUser> Store { get; set; } 136 137 /// <summary> 138 /// The <see cref="ILogger"/> used to log messages from the manager. 139 /// </summary> 140 /// <value> 141 /// The <see cref="ILogger"/> used to log messages from the manager. 142 /// </value> 143 public virtual ILogger Logger { get; set; } 144 145 /// <summary> 146 /// The <see cref="IPasswordHasher{TUser}"/> used to hash passwords. 147 /// </summary> 148 public IPasswordHasher<TUser> PasswordHasher { get; set; } 149 150 /// <summary> 151 /// The <see cref="IUserValidator{TUser}"/> used to validate users. 152 /// </summary> 153 public IList<IUserValidator<TUser>> UserValidators { get; } = new List<IUserValidator<TUser>>(); 154 155 /// <summary> 156 /// The <see cref="IPasswordValidator{TUser}"/> used to validate passwords. 157 /// </summary> 158 public IList<IPasswordValidator<TUser>> PasswordValidators { get; } = new List<IPasswordValidator<TUser>>(); 159 160 /// <summary> 161 /// The <see cref="ILookupNormalizer"/> used to normalize things like user and role names. 162 /// </summary> 163 public ILookupNormalizer KeyNormalizer { get; set; } 164 165 /// <summary> 166 /// The <see cref="IdentityErrorDescriber"/> used to generate error messages. 167 /// </summary> 168 public IdentityErrorDescriber ErrorDescriber { get; set; } 169 170 /// <summary> 171 /// The <see cref="IdentityOptions"/> used to configure Identity. 172 /// </summary> 173 public IdentityOptions Options { get; set; } 174 175 /// <summary> 176 /// Gets a flag indicating whether the backing user store supports authentication tokens. 177 /// </summary> 178 /// <value> 179 /// true if the backing user store supports authentication tokens, otherwise false. 180 /// </value> 181 public virtual bool SupportsUserAuthenticationTokens 182 { 183 get 184 { 185 ThrowIfDisposed(); 186 return Store is IUserAuthenticationTokenStore<TUser>; 187 } 188 } 189 190 /// <summary> 191 /// Gets a flag indicating whether the backing user store supports a user authenticator. 192 /// </summary> 193 /// <value> 194 /// true if the backing user store supports a user authenticator, otherwise false. 195 /// </value> 196 public virtual bool SupportsUserAuthenticatorKey 197 { 198 get 199 { 200 ThrowIfDisposed(); 201 return Store is IUserAuthenticatorKeyStore<TUser>; 202 } 203 } 204 205 /// <summary> 206 /// Gets a flag indicating whether the backing user store supports recovery codes. 207 /// </summary> 208 /// <value> 209 /// true if the backing user store supports a user authenticator, otherwise false. 210 /// </value> 211 public virtual bool SupportsUserTwoFactorRecoveryCodes 212 { 213 get 214 { 215 ThrowIfDisposed(); 216 return Store is IUserTwoFactorRecoveryCodeStore<TUser>; 217 } 218 } 219 220 /// <summary> 221 /// Gets a flag indicating whether the backing user store supports two factor authentication. 222 /// </summary> 223 /// <value> 224 /// true if the backing user store supports user two factor authentication, otherwise false. 225 /// </value> 226 public virtual bool SupportsUserTwoFactor 227 { 228 get 229 { 230 ThrowIfDisposed(); 231 return Store is IUserTwoFactorStore<TUser>; 232 } 233 } 234 235 /// <summary> 236 /// Gets a flag indicating whether the backing user store supports user passwords. 237 /// </summary> 238 /// <value> 239 /// true if the backing user store supports user passwords, otherwise false. 240 /// </value> 241 public virtual bool SupportsUserPassword 242 { 243 get 244 { 245 ThrowIfDisposed(); 246 return Store is IUserPasswordStore<TUser>; 247 } 248 } 249 250 /// <summary> 251 /// Gets a flag indicating whether the backing user store supports security stamps. 252 /// </summary> 253 /// <value> 254 /// true if the backing user store supports user security stamps, otherwise false. 255 /// </value> 256 public virtual bool SupportsUserSecurityStamp 257 { 258 get 259 { 260 ThrowIfDisposed(); 261 return Store is IUserSecurityStampStore<TUser>; 262 } 263 } 264 265 /// <summary> 266 /// Gets a flag indicating whether the backing user store supports user roles. 267 /// </summary> 268 /// <value> 269 /// true if the backing user store supports user roles, otherwise false. 270 /// </value> 271 public virtual bool SupportsUserRole 272 { 273 get 274 { 275 ThrowIfDisposed(); 276 return Store is IUserRoleStore<TUser>; 277 } 278 } 279 280 /// <summary> 281 /// Gets a flag indicating whether the backing user store supports external logins. 282 /// </summary> 283 /// <value> 284 /// true if the backing user store supports external logins, otherwise false. 285 /// </value> 286 public virtual bool SupportsUserLogin 287 { 288 get 289 { 290 ThrowIfDisposed(); 291 return Store is IUserLoginStore<TUser>; 292 } 293 } 294 295 /// <summary> 296 /// Gets a flag indicating whether the backing user store supports user emails. 297 /// </summary> 298 /// <value> 299 /// true if the backing user store supports user emails, otherwise false. 300 /// </value> 301 public virtual bool SupportsUserEmail 302 { 303 get 304 { 305 ThrowIfDisposed(); 306 return Store is IUserEmailStore<TUser>; 307 } 308 } 309 310 /// <summary> 311 /// Gets a flag indicating whether the backing user store supports user telephone numbers. 312 /// </summary> 313 /// <value> 314 /// true if the backing user store supports user telephone numbers, otherwise false. 315 /// </value> 316 public virtual bool SupportsUserPhoneNumber 317 { 318 get 319 { 320 ThrowIfDisposed(); 321 return Store is IUserPhoneNumberStore<TUser>; 322 } 323 } 324 325 /// <summary> 326 /// Gets a flag indicating whether the backing user store supports user claims. 327 /// </summary> 328 /// <value> 329 /// true if the backing user store supports user claims, otherwise false. 330 /// </value> 331 public virtual bool SupportsUserClaim 332 { 333 get 334 { 335 ThrowIfDisposed(); 336 return Store is IUserClaimStore<TUser>; 337 } 338 } 339 340 /// <summary> 341 /// Gets a flag indicating whether the backing user store supports user lock-outs. 342 /// </summary> 343 /// <value> 344 /// true if the backing user store supports user lock-outs, otherwise false. 345 /// </value> 346 public virtual bool SupportsUserLockout 347 { 348 get 349 { 350 ThrowIfDisposed(); 351 return Store is IUserLockoutStore<TUser>; 352 } 353 } 354 355 /// <summary> 356 /// Gets a flag indicating whether the backing user store supports returning 357 /// <see cref="IQueryable"/> collections of information. 358 /// </summary> 359 /// <value> 360 /// true if the backing user store supports returning <see cref="IQueryable"/> collections of 361 /// information, otherwise false. 362 /// </value> 363 public virtual bool SupportsQueryableUsers 364 { 365 get 366 { 367 ThrowIfDisposed(); 368 return Store is IQueryableUserStore<TUser>; 369 } 370 } 371 372 /// <summary> 373 /// Returns an IQueryable of users if the store is an IQueryableUserStore 374 /// </summary> 375 public virtual IQueryable<TUser> Users 376 { 377 get 378 { 379 var queryableStore = Store as IQueryableUserStore<TUser>; 380 if (queryableStore == null) 381 { 382 throw new NotSupportedException(Resources.StoreNotIQueryableUserStore); 383 } 384 return queryableStore.Users; 385 } 386 } 387 388 /// <summary> 389 /// Releases all resources used by the user manager. 390 /// </summary> Dispose()391 public void Dispose() 392 { 393 Dispose(true); 394 GC.SuppressFinalize(this); 395 } 396 397 /// <summary> 398 /// Returns the Name claim value if present otherwise returns null. 399 /// </summary> 400 /// <param name="principal">The <see cref="ClaimsPrincipal"/> instance.</param> 401 /// <returns>The Name claim value, or null if the claim is not present.</returns> 402 /// <remarks>The Name claim is identified by <see cref="ClaimsIdentity.DefaultNameClaimType"/>.</remarks> GetUserName(ClaimsPrincipal principal)403 public virtual string GetUserName(ClaimsPrincipal principal) 404 { 405 if (principal == null) 406 { 407 throw new ArgumentNullException(nameof(principal)); 408 } 409 return principal.FindFirstValue(Options.ClaimsIdentity.UserNameClaimType); 410 } 411 412 /// <summary> 413 /// Returns the User ID claim value if present otherwise returns null. 414 /// </summary> 415 /// <param name="principal">The <see cref="ClaimsPrincipal"/> instance.</param> 416 /// <returns>The User ID claim value, or null if the claim is not present.</returns> 417 /// <remarks>The User ID claim is identified by <see cref="ClaimTypes.NameIdentifier"/>.</remarks> GetUserId(ClaimsPrincipal principal)418 public virtual string GetUserId(ClaimsPrincipal principal) 419 { 420 if (principal == null) 421 { 422 throw new ArgumentNullException(nameof(principal)); 423 } 424 return principal.FindFirstValue(Options.ClaimsIdentity.UserIdClaimType); 425 } 426 427 /// <summary> 428 /// Returns the user corresponding to the IdentityOptions.ClaimsIdentity.UserIdClaimType claim in 429 /// the principal or null. 430 /// </summary> 431 /// <param name="principal">The principal which contains the user id claim.</param> 432 /// <returns>The user corresponding to the IdentityOptions.ClaimsIdentity.UserIdClaimType claim in 433 /// the principal or null</returns> GetUserAsync(ClaimsPrincipal principal)434 public virtual Task<TUser> GetUserAsync(ClaimsPrincipal principal) 435 { 436 if (principal == null) 437 { 438 throw new ArgumentNullException(nameof(principal)); 439 } 440 var id = GetUserId(principal); 441 return id == null ? Task.FromResult<TUser>(null) : FindByIdAsync(id); 442 } 443 444 /// <summary> 445 /// Generates a value suitable for use in concurrency tracking. 446 /// </summary> 447 /// <param name="user">The user to generate the stamp for.</param> 448 /// <returns> 449 /// The <see cref="Task"/> that represents the asynchronous operation, containing the security 450 /// stamp for the specified <paramref name="user"/>. 451 /// </returns> GenerateConcurrencyStampAsync(TUser user)452 public virtual Task<string> GenerateConcurrencyStampAsync(TUser user) 453 { 454 return Task.FromResult(Guid.NewGuid().ToString()); 455 } 456 457 /// <summary> 458 /// Creates the specified <paramref name="user"/> in the backing store with no password, 459 /// as an asynchronous operation. 460 /// </summary> 461 /// <param name="user">The user to create.</param> 462 /// <returns> 463 /// The <see cref="Task"/> that represents the asynchronous operation, containing the <see cref="IdentityResult"/> 464 /// of the operation. 465 /// </returns> CreateAsync(TUser user)466 public virtual async Task<IdentityResult> CreateAsync(TUser user) 467 { 468 ThrowIfDisposed(); 469 await UpdateSecurityStampInternal(user); 470 var result = await ValidateUserAsync(user); 471 if (!result.Succeeded) 472 { 473 return result; 474 } 475 if (Options.Lockout.AllowedForNewUsers && SupportsUserLockout) 476 { 477 await GetUserLockoutStore().SetLockoutEnabledAsync(user, true, CancellationToken); 478 } 479 await UpdateNormalizedUserNameAsync(user); 480 await UpdateNormalizedEmailAsync(user); 481 482 return await Store.CreateAsync(user, CancellationToken); 483 } 484 485 /// <summary> 486 /// Updates the specified <paramref name="user"/> in the backing store. 487 /// </summary> 488 /// <param name="user">The user to update.</param> 489 /// <returns> 490 /// The <see cref="Task"/> that represents the asynchronous operation, containing the <see cref="IdentityResult"/> 491 /// of the operation. 492 /// </returns> UpdateAsync(TUser user)493 public virtual Task<IdentityResult> UpdateAsync(TUser user) 494 { 495 ThrowIfDisposed(); 496 if (user == null) 497 { 498 throw new ArgumentNullException(nameof(user)); 499 } 500 501 return UpdateUserAsync(user); 502 } 503 504 /// <summary> 505 /// Deletes the specified <paramref name="user"/> from the backing store. 506 /// </summary> 507 /// <param name="user">The user to delete.</param> 508 /// <returns> 509 /// The <see cref="Task"/> that represents the asynchronous operation, containing the <see cref="IdentityResult"/> 510 /// of the operation. 511 /// </returns> DeleteAsync(TUser user)512 public virtual Task<IdentityResult> DeleteAsync(TUser user) 513 { 514 ThrowIfDisposed(); 515 if (user == null) 516 { 517 throw new ArgumentNullException(nameof(user)); 518 } 519 520 return Store.DeleteAsync(user, CancellationToken); 521 } 522 523 /// <summary> 524 /// Finds and returns a user, if any, who has the specified <paramref name="userId"/>. 525 /// </summary> 526 /// <param name="userId">The user ID to search for.</param> 527 /// <returns> 528 /// The <see cref="Task"/> that represents the asynchronous operation, containing the user matching the specified <paramref name="userId"/> if it exists. 529 /// </returns> FindByIdAsync(string userId)530 public virtual Task<TUser> FindByIdAsync(string userId) 531 { 532 ThrowIfDisposed(); 533 return Store.FindByIdAsync(userId, CancellationToken); 534 } 535 536 /// <summary> 537 /// Finds and returns a user, if any, who has the specified user name. 538 /// </summary> 539 /// <param name="userName">The user name to search for.</param> 540 /// <returns> 541 /// The <see cref="Task"/> that represents the asynchronous operation, containing the user matching the specified <paramref name="userName"/> if it exists. 542 /// </returns> FindByNameAsync(string userName)543 public virtual async Task<TUser> FindByNameAsync(string userName) 544 { 545 ThrowIfDisposed(); 546 if (userName == null) 547 { 548 throw new ArgumentNullException(nameof(userName)); 549 } 550 userName = NormalizeKey(userName); 551 552 var user = await Store.FindByNameAsync(userName, CancellationToken); 553 554 // Need to potentially check all keys 555 if (user == null && Options.Stores.ProtectPersonalData) 556 { 557 var keyRing = _services.GetService<ILookupProtectorKeyRing>(); 558 var protector = _services.GetService<ILookupProtector>(); 559 if (keyRing != null && protector != null) 560 { 561 foreach (var key in keyRing.GetAllKeyIds()) 562 { 563 var oldKey = protector.Protect(key, userName); 564 user = await Store.FindByNameAsync(oldKey, CancellationToken); 565 if (user != null) 566 { 567 return user; 568 } 569 } 570 } 571 } 572 return user; 573 } 574 575 /// <summary> 576 /// Creates the specified <paramref name="user"/> in the backing store with given password, 577 /// as an asynchronous operation. 578 /// </summary> 579 /// <param name="user">The user to create.</param> 580 /// <param name="password">The password for the user to hash and store.</param> 581 /// <returns> 582 /// The <see cref="Task"/> that represents the asynchronous operation, containing the <see cref="IdentityResult"/> 583 /// of the operation. 584 /// </returns> CreateAsync(TUser user, string password)585 public virtual async Task<IdentityResult> CreateAsync(TUser user, string password) 586 { 587 ThrowIfDisposed(); 588 var passwordStore = GetPasswordStore(); 589 if (user == null) 590 { 591 throw new ArgumentNullException(nameof(user)); 592 } 593 if (password == null) 594 { 595 throw new ArgumentNullException(nameof(password)); 596 } 597 var result = await UpdatePasswordHash(passwordStore, user, password); 598 if (!result.Succeeded) 599 { 600 return result; 601 } 602 return await CreateAsync(user); 603 } 604 605 /// <summary> 606 /// Normalize a key (user name, email) for consistent comparisons. 607 /// </summary> 608 /// <param name="key">The key to normalize.</param> 609 /// <returns>A normalized value representing the specified <paramref name="key"/>.</returns> NormalizeKey(string key)610 public virtual string NormalizeKey(string key) 611 { 612 return (KeyNormalizer == null) ? key : KeyNormalizer.Normalize(key); 613 } 614 ProtectPersonalData(string data)615 private string ProtectPersonalData(string data) 616 { 617 if (Options.Stores.ProtectPersonalData) 618 { 619 var keyRing = _services.GetService<ILookupProtectorKeyRing>(); 620 var protector = _services.GetService<ILookupProtector>(); 621 return protector.Protect(keyRing.CurrentKeyId, data); 622 } 623 return data; 624 } 625 626 /// <summary> 627 /// Updates the normalized user name for the specified <paramref name="user"/>. 628 /// </summary> 629 /// <param name="user">The user whose user name should be normalized and updated.</param> 630 /// <returns>The <see cref="Task"/> that represents the asynchronous operation.</returns> UpdateNormalizedUserNameAsync(TUser user)631 public virtual async Task UpdateNormalizedUserNameAsync(TUser user) 632 { 633 var normalizedName = NormalizeKey(await GetUserNameAsync(user)); 634 normalizedName = ProtectPersonalData(normalizedName); 635 await Store.SetNormalizedUserNameAsync(user, normalizedName, CancellationToken); 636 } 637 638 /// <summary> 639 /// Gets the user name for the specified <paramref name="user"/>. 640 /// </summary> 641 /// <param name="user">The user whose name should be retrieved.</param> 642 /// <returns>The <see cref="Task"/> that represents the asynchronous operation, containing the name for the specified <paramref name="user"/>.</returns> GetUserNameAsync(TUser user)643 public virtual async Task<string> GetUserNameAsync(TUser user) 644 { 645 ThrowIfDisposed(); 646 if (user == null) 647 { 648 throw new ArgumentNullException(nameof(user)); 649 } 650 return await Store.GetUserNameAsync(user, CancellationToken); 651 } 652 653 /// <summary> 654 /// Sets the given <paramref name="userName" /> for the specified <paramref name="user"/>. 655 /// </summary> 656 /// <param name="user">The user whose name should be set.</param> 657 /// <param name="userName">The user name to set.</param> 658 /// <returns>The <see cref="Task"/> that represents the asynchronous operation.</returns> SetUserNameAsync(TUser user, string userName)659 public virtual async Task<IdentityResult> SetUserNameAsync(TUser user, string userName) 660 { 661 ThrowIfDisposed(); 662 if (user == null) 663 { 664 throw new ArgumentNullException(nameof(user)); 665 } 666 667 await Store.SetUserNameAsync(user, userName, CancellationToken); 668 await UpdateSecurityStampInternal(user); 669 return await UpdateUserAsync(user); 670 } 671 672 /// <summary> 673 /// Gets the user identifier for the specified <paramref name="user"/>. 674 /// </summary> 675 /// <param name="user">The user whose identifier should be retrieved.</param> 676 /// <returns>The <see cref="Task"/> that represents the asynchronous operation, containing the identifier for the specified <paramref name="user"/>.</returns> GetUserIdAsync(TUser user)677 public virtual async Task<string> GetUserIdAsync(TUser user) 678 { 679 ThrowIfDisposed(); 680 return await Store.GetUserIdAsync(user, CancellationToken); 681 } 682 683 /// <summary> 684 /// Returns a flag indicating whether the given <paramref name="password"/> is valid for the 685 /// specified <paramref name="user"/>. 686 /// </summary> 687 /// <param name="user">The user whose password should be validated.</param> 688 /// <param name="password">The password to validate</param> 689 /// <returns>The <see cref="Task"/> that represents the asynchronous operation, containing true if 690 /// the specified <paramref name="password" /> matches the one store for the <paramref name="user"/>, 691 /// otherwise false.</returns> CheckPasswordAsync(TUser user, string password)692 public virtual async Task<bool> CheckPasswordAsync(TUser user, string password) 693 { 694 ThrowIfDisposed(); 695 var passwordStore = GetPasswordStore(); 696 if (user == null) 697 { 698 return false; 699 } 700 701 var result = await VerifyPasswordAsync(passwordStore, user, password); 702 if (result == PasswordVerificationResult.SuccessRehashNeeded) 703 { 704 await UpdatePasswordHash(passwordStore, user, password, validatePassword: false); 705 await UpdateUserAsync(user); 706 } 707 708 var success = result != PasswordVerificationResult.Failed; 709 if (!success) 710 { 711 Logger.LogWarning(0, "Invalid password for user {userId}.", await GetUserIdAsync(user)); 712 } 713 return success; 714 } 715 716 /// <summary> 717 /// Gets a flag indicating whether the specified <paramref name="user"/> has a password. 718 /// </summary> 719 /// <param name="user">The user to return a flag for, indicating whether they have a password or not.</param> 720 /// <returns> 721 /// The <see cref="Task"/> that represents the asynchronous operation, returning true if the specified <paramref name="user"/> has a password 722 /// otherwise false. 723 /// </returns> HasPasswordAsync(TUser user)724 public virtual Task<bool> HasPasswordAsync(TUser user) 725 { 726 ThrowIfDisposed(); 727 var passwordStore = GetPasswordStore(); 728 if (user == null) 729 { 730 throw new ArgumentNullException(nameof(user)); 731 } 732 733 return passwordStore.HasPasswordAsync(user, CancellationToken); 734 } 735 736 /// <summary> 737 /// Adds the <paramref name="password"/> to the specified <paramref name="user"/> only if the user 738 /// does not already have a password. 739 /// </summary> 740 /// <param name="user">The user whose password should be set.</param> 741 /// <param name="password">The password to set.</param> 742 /// <returns> 743 /// The <see cref="Task"/> that represents the asynchronous operation, containing the <see cref="IdentityResult"/> 744 /// of the operation. 745 /// </returns> AddPasswordAsync(TUser user, string password)746 public virtual async Task<IdentityResult> AddPasswordAsync(TUser user, string password) 747 { 748 ThrowIfDisposed(); 749 var passwordStore = GetPasswordStore(); 750 if (user == null) 751 { 752 throw new ArgumentNullException(nameof(user)); 753 } 754 755 var hash = await passwordStore.GetPasswordHashAsync(user, CancellationToken); 756 if (hash != null) 757 { 758 Logger.LogWarning(1, "User {userId} already has a password.", await GetUserIdAsync(user)); 759 return IdentityResult.Failed(ErrorDescriber.UserAlreadyHasPassword()); 760 } 761 var result = await UpdatePasswordHash(passwordStore, user, password); 762 if (!result.Succeeded) 763 { 764 return result; 765 } 766 return await UpdateUserAsync(user); 767 } 768 769 /// <summary> 770 /// Changes a user's password after confirming the specified <paramref name="currentPassword"/> is correct, 771 /// as an asynchronous operation. 772 /// </summary> 773 /// <param name="user">The user whose password should be set.</param> 774 /// <param name="currentPassword">The current password to validate before changing.</param> 775 /// <param name="newPassword">The new password to set for the specified <paramref name="user"/>.</param> 776 /// <returns> 777 /// The <see cref="Task"/> that represents the asynchronous operation, containing the <see cref="IdentityResult"/> 778 /// of the operation. 779 /// </returns> ChangePasswordAsync(TUser user, string currentPassword, string newPassword)780 public virtual async Task<IdentityResult> ChangePasswordAsync(TUser user, string currentPassword, string newPassword) 781 { 782 ThrowIfDisposed(); 783 var passwordStore = GetPasswordStore(); 784 if (user == null) 785 { 786 throw new ArgumentNullException(nameof(user)); 787 } 788 789 790 if (await VerifyPasswordAsync(passwordStore, user, currentPassword) != PasswordVerificationResult.Failed) 791 { 792 var result = await UpdatePasswordHash(passwordStore, user, newPassword); 793 if (!result.Succeeded) 794 { 795 return result; 796 } 797 return await UpdateUserAsync(user); 798 } 799 Logger.LogWarning(2, "Change password failed for user {userId}.", await GetUserIdAsync(user)); 800 return IdentityResult.Failed(ErrorDescriber.PasswordMismatch()); 801 } 802 803 /// <summary> 804 /// Removes a user's password. 805 /// </summary> 806 /// <param name="user">The user whose password should be removed.</param> 807 /// <returns> 808 /// The <see cref="Task"/> that represents the asynchronous operation, containing the <see cref="IdentityResult"/> 809 /// of the operation. 810 /// </returns> RemovePasswordAsync(TUser user)811 public virtual async Task<IdentityResult> RemovePasswordAsync(TUser user) 812 { 813 ThrowIfDisposed(); 814 var passwordStore = GetPasswordStore(); 815 if (user == null) 816 { 817 throw new ArgumentNullException(nameof(user)); 818 } 819 820 await UpdatePasswordHash(passwordStore, user, null, validatePassword: false); 821 return await UpdateUserAsync(user); 822 } 823 824 /// <summary> 825 /// Returns a <see cref="PasswordVerificationResult"/> indicating the result of a password hash comparison. 826 /// </summary> 827 /// <param name="store">The store containing a user's password.</param> 828 /// <param name="user">The user whose password should be verified.</param> 829 /// <param name="password">The password to verify.</param> 830 /// <returns> 831 /// The <see cref="Task"/> that represents the asynchronous operation, containing the <see cref="PasswordVerificationResult"/> 832 /// of the operation. 833 /// </returns> VerifyPasswordAsync(IUserPasswordStore<TUser> store, TUser user, string password)834 protected virtual async Task<PasswordVerificationResult> VerifyPasswordAsync(IUserPasswordStore<TUser> store, TUser user, string password) 835 { 836 var hash = await store.GetPasswordHashAsync(user, CancellationToken); 837 if (hash == null) 838 { 839 return PasswordVerificationResult.Failed; 840 } 841 return PasswordHasher.VerifyHashedPassword(user, hash, password); 842 } 843 844 /// <summary> 845 /// Get the security stamp for the specified <paramref name="user" />. 846 /// </summary> 847 /// <param name="user">The user whose security stamp should be set.</param> 848 /// <returns>The <see cref="Task"/> that represents the asynchronous operation, containing the security stamp for the specified <paramref name="user"/>.</returns> GetSecurityStampAsync(TUser user)849 public virtual async Task<string> GetSecurityStampAsync(TUser user) 850 { 851 ThrowIfDisposed(); 852 var securityStore = GetSecurityStore(); 853 if (user == null) 854 { 855 throw new ArgumentNullException(nameof(user)); 856 } 857 return await securityStore.GetSecurityStampAsync(user, CancellationToken); 858 } 859 860 /// <summary> 861 /// Regenerates the security stamp for the specified <paramref name="user" />. 862 /// </summary> 863 /// <param name="user">The user whose security stamp should be regenerated.</param> 864 /// <returns> 865 /// The <see cref="Task"/> that represents the asynchronous operation, containing the <see cref="IdentityResult"/> 866 /// of the operation. 867 /// </returns> 868 /// <remarks> 869 /// Regenerating a security stamp will sign out any saved login for the user. 870 /// </remarks> UpdateSecurityStampAsync(TUser user)871 public virtual async Task<IdentityResult> UpdateSecurityStampAsync(TUser user) 872 { 873 ThrowIfDisposed(); 874 GetSecurityStore(); 875 if (user == null) 876 { 877 throw new ArgumentNullException(nameof(user)); 878 } 879 880 await UpdateSecurityStampInternal(user); 881 return await UpdateUserAsync(user); 882 } 883 884 /// <summary> 885 /// Generates a password reset token for the specified <paramref name="user"/>, using 886 /// the configured password reset token provider. 887 /// </summary> 888 /// <param name="user">The user to generate a password reset token for.</param> 889 /// <returns>The <see cref="Task"/> that represents the asynchronous operation, 890 /// containing a password reset token for the specified <paramref name="user"/>.</returns> GeneratePasswordResetTokenAsync(TUser user)891 public virtual Task<string> GeneratePasswordResetTokenAsync(TUser user) 892 { 893 ThrowIfDisposed(); 894 return GenerateUserTokenAsync(user, Options.Tokens.PasswordResetTokenProvider, ResetPasswordTokenPurpose); 895 } 896 897 /// <summary> 898 /// Resets the <paramref name="user"/>'s password to the specified <paramref name="newPassword"/> after 899 /// validating the given password reset <paramref name="token"/>. 900 /// </summary> 901 /// <param name="user">The user whose password should be reset.</param> 902 /// <param name="token">The password reset token to verify.</param> 903 /// <param name="newPassword">The new password to set if reset token verification succeeds.</param> 904 /// <returns> 905 /// The <see cref="Task"/> that represents the asynchronous operation, containing the <see cref="IdentityResult"/> 906 /// of the operation. 907 /// </returns> ResetPasswordAsync(TUser user, string token, string newPassword)908 public virtual async Task<IdentityResult> ResetPasswordAsync(TUser user, string token, string newPassword) 909 { 910 ThrowIfDisposed(); 911 if (user == null) 912 { 913 throw new ArgumentNullException(nameof(user)); 914 } 915 916 // Make sure the token is valid and the stamp matches 917 if (!await VerifyUserTokenAsync(user, Options.Tokens.PasswordResetTokenProvider, ResetPasswordTokenPurpose, token)) 918 { 919 return IdentityResult.Failed(ErrorDescriber.InvalidToken()); 920 } 921 var result = await UpdatePasswordHash(user, newPassword, validatePassword: true); 922 if (!result.Succeeded) 923 { 924 return result; 925 } 926 return await UpdateUserAsync(user); 927 } 928 929 /// <summary> 930 /// Retrieves the user associated with the specified external login provider and login provider key. 931 /// </summary> 932 /// <param name="loginProvider">The login provider who provided the <paramref name="providerKey"/>.</param> 933 /// <param name="providerKey">The key provided by the <paramref name="loginProvider"/> to identify a user.</param> 934 /// <returns> 935 /// The <see cref="Task"/> for the asynchronous operation, containing the user, if any which matched the specified login provider and key. 936 /// </returns> FindByLoginAsync(string loginProvider, string providerKey)937 public virtual Task<TUser> FindByLoginAsync(string loginProvider, string providerKey) 938 { 939 ThrowIfDisposed(); 940 var loginStore = GetLoginStore(); 941 if (loginProvider == null) 942 { 943 throw new ArgumentNullException(nameof(loginProvider)); 944 } 945 if (providerKey == null) 946 { 947 throw new ArgumentNullException(nameof(providerKey)); 948 } 949 return loginStore.FindByLoginAsync(loginProvider, providerKey, CancellationToken); 950 } 951 952 /// <summary> 953 /// Attempts to remove the provided external login information from the specified <paramref name="user"/>. 954 /// and returns a flag indicating whether the removal succeed or not. 955 /// </summary> 956 /// <param name="user">The user to remove the login information from.</param> 957 /// <param name="loginProvider">The login provide whose information should be removed.</param> 958 /// <param name="providerKey">The key given by the external login provider for the specified user.</param> 959 /// <returns> 960 /// The <see cref="Task"/> that represents the asynchronous operation, containing the <see cref="IdentityResult"/> 961 /// of the operation. 962 /// </returns> RemoveLoginAsync(TUser user, string loginProvider, string providerKey)963 public virtual async Task<IdentityResult> RemoveLoginAsync(TUser user, string loginProvider, string providerKey) 964 { 965 ThrowIfDisposed(); 966 var loginStore = GetLoginStore(); 967 if (loginProvider == null) 968 { 969 throw new ArgumentNullException(nameof(loginProvider)); 970 } 971 if (providerKey == null) 972 { 973 throw new ArgumentNullException(nameof(providerKey)); 974 } 975 if (user == null) 976 { 977 throw new ArgumentNullException(nameof(user)); 978 } 979 980 await loginStore.RemoveLoginAsync(user, loginProvider, providerKey, CancellationToken); 981 await UpdateSecurityStampInternal(user); 982 return await UpdateUserAsync(user); 983 } 984 985 /// <summary> 986 /// Adds an external <see cref="UserLoginInfo"/> to the specified <paramref name="user"/>. 987 /// </summary> 988 /// <param name="user">The user to add the login to.</param> 989 /// <param name="login">The external <see cref="UserLoginInfo"/> to add to the specified <paramref name="user"/>.</param> 990 /// <returns> 991 /// The <see cref="Task"/> that represents the asynchronous operation, containing the <see cref="IdentityResult"/> 992 /// of the operation. 993 /// </returns> AddLoginAsync(TUser user, UserLoginInfo login)994 public virtual async Task<IdentityResult> AddLoginAsync(TUser user, UserLoginInfo login) 995 { 996 ThrowIfDisposed(); 997 var loginStore = GetLoginStore(); 998 if (login == null) 999 { 1000 throw new ArgumentNullException(nameof(login)); 1001 } 1002 if (user == null) 1003 { 1004 throw new ArgumentNullException(nameof(user)); 1005 } 1006 1007 var existingUser = await FindByLoginAsync(login.LoginProvider, login.ProviderKey); 1008 if (existingUser != null) 1009 { 1010 Logger.LogWarning(4, "AddLogin for user {userId} failed because it was already associated with another user.", await GetUserIdAsync(user)); 1011 return IdentityResult.Failed(ErrorDescriber.LoginAlreadyAssociated()); 1012 } 1013 await loginStore.AddLoginAsync(user, login, CancellationToken); 1014 return await UpdateUserAsync(user); 1015 } 1016 1017 /// <summary> 1018 /// Retrieves the associated logins for the specified <param ref="user"/>. 1019 /// </summary> 1020 /// <param name="user">The user whose associated logins to retrieve.</param> 1021 /// <returns> 1022 /// The <see cref="Task"/> for the asynchronous operation, containing a list of <see cref="UserLoginInfo"/> for the specified <paramref name="user"/>, if any. 1023 /// </returns> GetLoginsAsync(TUser user)1024 public virtual async Task<IList<UserLoginInfo>> GetLoginsAsync(TUser user) 1025 { 1026 ThrowIfDisposed(); 1027 var loginStore = GetLoginStore(); 1028 if (user == null) 1029 { 1030 throw new ArgumentNullException(nameof(user)); 1031 } 1032 return await loginStore.GetLoginsAsync(user, CancellationToken); 1033 } 1034 1035 /// <summary> 1036 /// Adds the specified <paramref name="claim"/> to the <paramref name="user"/>. 1037 /// </summary> 1038 /// <param name="user">The user to add the claim to.</param> 1039 /// <param name="claim">The claim to add.</param> 1040 /// <returns> 1041 /// The <see cref="Task"/> that represents the asynchronous operation, containing the <see cref="IdentityResult"/> 1042 /// of the operation. 1043 /// </returns> AddClaimAsync(TUser user, Claim claim)1044 public virtual Task<IdentityResult> AddClaimAsync(TUser user, Claim claim) 1045 { 1046 ThrowIfDisposed(); 1047 var claimStore = GetClaimStore(); 1048 if (claim == null) 1049 { 1050 throw new ArgumentNullException(nameof(claim)); 1051 } 1052 if (user == null) 1053 { 1054 throw new ArgumentNullException(nameof(user)); 1055 } 1056 return AddClaimsAsync(user, new Claim[] { claim }); 1057 } 1058 1059 /// <summary> 1060 /// Adds the specified <paramref name="claims"/> to the <paramref name="user"/>. 1061 /// </summary> 1062 /// <param name="user">The user to add the claim to.</param> 1063 /// <param name="claims">The claims to add.</param> 1064 /// <returns> 1065 /// The <see cref="Task"/> that represents the asynchronous operation, containing the <see cref="IdentityResult"/> 1066 /// of the operation. 1067 /// </returns> AddClaimsAsync(TUser user, IEnumerable<Claim> claims)1068 public virtual async Task<IdentityResult> AddClaimsAsync(TUser user, IEnumerable<Claim> claims) 1069 { 1070 ThrowIfDisposed(); 1071 var claimStore = GetClaimStore(); 1072 if (claims == null) 1073 { 1074 throw new ArgumentNullException(nameof(claims)); 1075 } 1076 if (user == null) 1077 { 1078 throw new ArgumentNullException(nameof(user)); 1079 } 1080 1081 await claimStore.AddClaimsAsync(user, claims, CancellationToken); 1082 return await UpdateUserAsync(user); 1083 } 1084 1085 /// <summary> 1086 /// Replaces the given <paramref name="claim"/> on the specified <paramref name="user"/> with the <paramref name="newClaim"/> 1087 /// </summary> 1088 /// <param name="user">The user to replace the claim on.</param> 1089 /// <param name="claim">The claim to replace.</param> 1090 /// <param name="newClaim">The new claim to replace the existing <paramref name="claim"/> with.</param> 1091 /// <returns> 1092 /// The <see cref="Task"/> that represents the asynchronous operation, containing the <see cref="IdentityResult"/> 1093 /// of the operation. 1094 /// </returns> ReplaceClaimAsync(TUser user, Claim claim, Claim newClaim)1095 public virtual async Task<IdentityResult> ReplaceClaimAsync(TUser user, Claim claim, Claim newClaim) 1096 { 1097 ThrowIfDisposed(); 1098 var claimStore = GetClaimStore(); 1099 if (claim == null) 1100 { 1101 throw new ArgumentNullException(nameof(claim)); 1102 } 1103 if (newClaim == null) 1104 { 1105 throw new ArgumentNullException(nameof(newClaim)); 1106 } 1107 if (user == null) 1108 { 1109 throw new ArgumentNullException(nameof(user)); 1110 } 1111 1112 await claimStore.ReplaceClaimAsync(user, claim, newClaim, CancellationToken); 1113 return await UpdateUserAsync(user); 1114 } 1115 1116 /// <summary> 1117 /// Removes the specified <paramref name="claim"/> from the given <paramref name="user"/>. 1118 /// </summary> 1119 /// <param name="user">The user to remove the specified <paramref name="claim"/> from.</param> 1120 /// <param name="claim">The <see cref="Claim"/> to remove.</param> 1121 /// <returns> 1122 /// The <see cref="Task"/> that represents the asynchronous operation, containing the <see cref="IdentityResult"/> 1123 /// of the operation. 1124 /// </returns> RemoveClaimAsync(TUser user, Claim claim)1125 public virtual Task<IdentityResult> RemoveClaimAsync(TUser user, Claim claim) 1126 { 1127 ThrowIfDisposed(); 1128 var claimStore = GetClaimStore(); 1129 if (user == null) 1130 { 1131 throw new ArgumentNullException(nameof(user)); 1132 } 1133 if (claim == null) 1134 { 1135 throw new ArgumentNullException(nameof(claim)); 1136 } 1137 return RemoveClaimsAsync(user, new Claim[] { claim }); 1138 } 1139 1140 /// <summary> 1141 /// Removes the specified <paramref name="claims"/> from the given <paramref name="user"/>. 1142 /// </summary> 1143 /// <param name="user">The user to remove the specified <paramref name="claims"/> from.</param> 1144 /// <param name="claims">A collection of <see cref="Claim"/>s to remove.</param> 1145 /// <returns> 1146 /// The <see cref="Task"/> that represents the asynchronous operation, containing the <see cref="IdentityResult"/> 1147 /// of the operation. 1148 /// </returns> RemoveClaimsAsync(TUser user, IEnumerable<Claim> claims)1149 public virtual async Task<IdentityResult> RemoveClaimsAsync(TUser user, IEnumerable<Claim> claims) 1150 { 1151 ThrowIfDisposed(); 1152 var claimStore = GetClaimStore(); 1153 if (user == null) 1154 { 1155 throw new ArgumentNullException(nameof(user)); 1156 } 1157 if (claims == null) 1158 { 1159 throw new ArgumentNullException(nameof(claims)); 1160 } 1161 1162 await claimStore.RemoveClaimsAsync(user, claims, CancellationToken); 1163 return await UpdateUserAsync(user); 1164 } 1165 1166 /// <summary> 1167 /// Gets a list of <see cref="Claim"/>s to be belonging to the specified <paramref name="user"/> as an asynchronous operation. 1168 /// </summary> 1169 /// <param name="user">The user whose claims to retrieve.</param> 1170 /// <returns> 1171 /// A <see cref="Task{TResult}"/> that represents the result of the asynchronous query, a list of <see cref="Claim"/>s. 1172 /// </returns> GetClaimsAsync(TUser user)1173 public virtual async Task<IList<Claim>> GetClaimsAsync(TUser user) 1174 { 1175 ThrowIfDisposed(); 1176 var claimStore = GetClaimStore(); 1177 if (user == null) 1178 { 1179 throw new ArgumentNullException(nameof(user)); 1180 } 1181 return await claimStore.GetClaimsAsync(user, CancellationToken); 1182 } 1183 1184 /// <summary> 1185 /// Add the specified <paramref name="user"/> to the named role. 1186 /// </summary> 1187 /// <param name="user">The user to add to the named role.</param> 1188 /// <param name="role">The name of the role to add the user to.</param> 1189 /// <returns> 1190 /// The <see cref="Task"/> that represents the asynchronous operation, containing the <see cref="IdentityResult"/> 1191 /// of the operation. 1192 /// </returns> AddToRoleAsync(TUser user, string role)1193 public virtual async Task<IdentityResult> AddToRoleAsync(TUser user, string role) 1194 { 1195 ThrowIfDisposed(); 1196 var userRoleStore = GetUserRoleStore(); 1197 if (user == null) 1198 { 1199 throw new ArgumentNullException(nameof(user)); 1200 } 1201 1202 var normalizedRole = NormalizeKey(role); 1203 if (await userRoleStore.IsInRoleAsync(user, normalizedRole, CancellationToken)) 1204 { 1205 return await UserAlreadyInRoleError(user, role); 1206 } 1207 await userRoleStore.AddToRoleAsync(user, normalizedRole, CancellationToken); 1208 return await UpdateUserAsync(user); 1209 } 1210 1211 /// <summary> 1212 /// Add the specified <paramref name="user"/> to the named roles. 1213 /// </summary> 1214 /// <param name="user">The user to add to the named roles.</param> 1215 /// <param name="roles">The name of the roles to add the user to.</param> 1216 /// <returns> 1217 /// The <see cref="Task"/> that represents the asynchronous operation, containing the <see cref="IdentityResult"/> 1218 /// of the operation. 1219 /// </returns> AddToRolesAsync(TUser user, IEnumerable<string> roles)1220 public virtual async Task<IdentityResult> AddToRolesAsync(TUser user, IEnumerable<string> roles) 1221 { 1222 ThrowIfDisposed(); 1223 var userRoleStore = GetUserRoleStore(); 1224 if (user == null) 1225 { 1226 throw new ArgumentNullException(nameof(user)); 1227 } 1228 if (roles == null) 1229 { 1230 throw new ArgumentNullException(nameof(roles)); 1231 } 1232 1233 foreach (var role in roles.Distinct()) 1234 { 1235 var normalizedRole = NormalizeKey(role); 1236 if (await userRoleStore.IsInRoleAsync(user, normalizedRole, CancellationToken)) 1237 { 1238 return await UserAlreadyInRoleError(user, role); 1239 } 1240 await userRoleStore.AddToRoleAsync(user, normalizedRole, CancellationToken); 1241 } 1242 return await UpdateUserAsync(user); 1243 } 1244 1245 /// <summary> 1246 /// Removes the specified <paramref name="user"/> from the named role. 1247 /// </summary> 1248 /// <param name="user">The user to remove from the named role.</param> 1249 /// <param name="role">The name of the role to remove the user from.</param> 1250 /// <returns> 1251 /// The <see cref="Task"/> that represents the asynchronous operation, containing the <see cref="IdentityResult"/> 1252 /// of the operation. 1253 /// </returns> RemoveFromRoleAsync(TUser user, string role)1254 public virtual async Task<IdentityResult> RemoveFromRoleAsync(TUser user, string role) 1255 { 1256 ThrowIfDisposed(); 1257 var userRoleStore = GetUserRoleStore(); 1258 if (user == null) 1259 { 1260 throw new ArgumentNullException(nameof(user)); 1261 } 1262 1263 var normalizedRole = NormalizeKey(role); 1264 if (!await userRoleStore.IsInRoleAsync(user, normalizedRole, CancellationToken)) 1265 { 1266 return await UserNotInRoleError(user, role); 1267 } 1268 await userRoleStore.RemoveFromRoleAsync(user, normalizedRole, CancellationToken); 1269 return await UpdateUserAsync(user); 1270 } 1271 UserAlreadyInRoleError(TUser user, string role)1272 private async Task<IdentityResult> UserAlreadyInRoleError(TUser user, string role) 1273 { 1274 Logger.LogWarning(5, "User {userId} is already in role {role}.", await GetUserIdAsync(user), role); 1275 return IdentityResult.Failed(ErrorDescriber.UserAlreadyInRole(role)); 1276 } 1277 UserNotInRoleError(TUser user, string role)1278 private async Task<IdentityResult> UserNotInRoleError(TUser user, string role) 1279 { 1280 Logger.LogWarning(6, "User {userId} is not in role {role}.", await GetUserIdAsync(user), role); 1281 return IdentityResult.Failed(ErrorDescriber.UserNotInRole(role)); 1282 } 1283 1284 /// <summary> 1285 /// Removes the specified <paramref name="user"/> from the named roles. 1286 /// </summary> 1287 /// <param name="user">The user to remove from the named roles.</param> 1288 /// <param name="roles">The name of the roles to remove the user from.</param> 1289 /// <returns> 1290 /// The <see cref="Task"/> that represents the asynchronous operation, containing the <see cref="IdentityResult"/> 1291 /// of the operation. 1292 /// </returns> RemoveFromRolesAsync(TUser user, IEnumerable<string> roles)1293 public virtual async Task<IdentityResult> RemoveFromRolesAsync(TUser user, IEnumerable<string> roles) 1294 { 1295 ThrowIfDisposed(); 1296 var userRoleStore = GetUserRoleStore(); 1297 if (user == null) 1298 { 1299 throw new ArgumentNullException(nameof(user)); 1300 } 1301 if (roles == null) 1302 { 1303 throw new ArgumentNullException(nameof(roles)); 1304 } 1305 1306 foreach (var role in roles) 1307 { 1308 var normalizedRole = NormalizeKey(role); 1309 if (!await userRoleStore.IsInRoleAsync(user, normalizedRole, CancellationToken)) 1310 { 1311 return await UserNotInRoleError(user, role); 1312 } 1313 await userRoleStore.RemoveFromRoleAsync(user, normalizedRole, CancellationToken); 1314 } 1315 return await UpdateUserAsync(user); 1316 } 1317 1318 /// <summary> 1319 /// Gets a list of role names the specified <paramref name="user"/> belongs to. 1320 /// </summary> 1321 /// <param name="user">The user whose role names to retrieve.</param> 1322 /// <returns>The <see cref="Task"/> that represents the asynchronous operation, containing a list of role names.</returns> GetRolesAsync(TUser user)1323 public virtual async Task<IList<string>> GetRolesAsync(TUser user) 1324 { 1325 ThrowIfDisposed(); 1326 var userRoleStore = GetUserRoleStore(); 1327 if (user == null) 1328 { 1329 throw new ArgumentNullException(nameof(user)); 1330 } 1331 return await userRoleStore.GetRolesAsync(user, CancellationToken); 1332 } 1333 1334 /// <summary> 1335 /// Returns a flag indicating whether the specified <paramref name="user"/> is a member of the give named role. 1336 /// </summary> 1337 /// <param name="user">The user whose role membership should be checked.</param> 1338 /// <param name="role">The name of the role to be checked.</param> 1339 /// <returns> 1340 /// The <see cref="Task"/> that represents the asynchronous operation, containing a flag indicating whether the specified <paramref name="user"/> is 1341 /// a member of the named role. 1342 /// </returns> IsInRoleAsync(TUser user, string role)1343 public virtual async Task<bool> IsInRoleAsync(TUser user, string role) 1344 { 1345 ThrowIfDisposed(); 1346 var userRoleStore = GetUserRoleStore(); 1347 if (user == null) 1348 { 1349 throw new ArgumentNullException(nameof(user)); 1350 } 1351 return await userRoleStore.IsInRoleAsync(user, NormalizeKey(role), CancellationToken); 1352 } 1353 1354 /// <summary> 1355 /// Gets the email address for the specified <paramref name="user"/>. 1356 /// </summary> 1357 /// <param name="user">The user whose email should be returned.</param> 1358 /// <returns>The task object containing the results of the asynchronous operation, the email address for the specified <paramref name="user"/>.</returns> GetEmailAsync(TUser user)1359 public virtual async Task<string> GetEmailAsync(TUser user) 1360 { 1361 ThrowIfDisposed(); 1362 var store = GetEmailStore(); 1363 if (user == null) 1364 { 1365 throw new ArgumentNullException(nameof(user)); 1366 } 1367 return await store.GetEmailAsync(user, CancellationToken); 1368 } 1369 1370 /// <summary> 1371 /// Sets the <paramref name="email"/> address for a <paramref name="user"/>. 1372 /// </summary> 1373 /// <param name="user">The user whose email should be set.</param> 1374 /// <param name="email">The email to set.</param> 1375 /// <returns> 1376 /// The <see cref="Task"/> that represents the asynchronous operation, containing the <see cref="IdentityResult"/> 1377 /// of the operation. 1378 /// </returns> SetEmailAsync(TUser user, string email)1379 public virtual async Task<IdentityResult> SetEmailAsync(TUser user, string email) 1380 { 1381 ThrowIfDisposed(); 1382 var store = GetEmailStore(); 1383 if (user == null) 1384 { 1385 throw new ArgumentNullException(nameof(user)); 1386 } 1387 1388 await store.SetEmailAsync(user, email, CancellationToken); 1389 await store.SetEmailConfirmedAsync(user, false, CancellationToken); 1390 await UpdateSecurityStampInternal(user); 1391 return await UpdateUserAsync(user); 1392 } 1393 1394 /// <summary> 1395 /// Gets the user, if any, associated with the normalized value of the specified email address. 1396 /// </summary> 1397 /// <param name="email">The email address to return the user for.</param> 1398 /// <returns> 1399 /// The task object containing the results of the asynchronous lookup operation, the user, if any, associated with a normalized value of the specified email address. 1400 /// </returns> FindByEmailAsync(string email)1401 public virtual async Task<TUser> FindByEmailAsync(string email) 1402 { 1403 ThrowIfDisposed(); 1404 var store = GetEmailStore(); 1405 if (email == null) 1406 { 1407 throw new ArgumentNullException(nameof(email)); 1408 } 1409 1410 email = NormalizeKey(email); 1411 var user = await store.FindByEmailAsync(email, CancellationToken); 1412 1413 // Need to potentially check all keys 1414 if (user == null && Options.Stores.ProtectPersonalData) 1415 { 1416 var keyRing = _services.GetService<ILookupProtectorKeyRing>(); 1417 var protector = _services.GetService<ILookupProtector>(); 1418 if (keyRing != null && protector != null) 1419 { 1420 foreach (var key in keyRing.GetAllKeyIds()) 1421 { 1422 var oldKey = protector.Protect(key, email); 1423 user = await store.FindByEmailAsync(oldKey, CancellationToken); 1424 if (user != null) 1425 { 1426 return user; 1427 } 1428 } 1429 } 1430 } 1431 return user; 1432 } 1433 1434 /// <summary> 1435 /// Updates the normalized email for the specified <paramref name="user"/>. 1436 /// </summary> 1437 /// <param name="user">The user whose email address should be normalized and updated.</param> 1438 /// <returns>The task object representing the asynchronous operation.</returns> UpdateNormalizedEmailAsync(TUser user)1439 public virtual async Task UpdateNormalizedEmailAsync(TUser user) 1440 { 1441 var store = GetEmailStore(throwOnFail: false); 1442 if (store != null) 1443 { 1444 var email = await GetEmailAsync(user); 1445 await store.SetNormalizedEmailAsync(user, ProtectPersonalData(NormalizeKey(email)), CancellationToken); 1446 } 1447 } 1448 1449 /// <summary> 1450 /// Generates an email confirmation token for the specified user. 1451 /// </summary> 1452 /// <param name="user">The user to generate an email confirmation token for.</param> 1453 /// <returns> 1454 /// The <see cref="Task"/> that represents the asynchronous operation, an email confirmation token. 1455 /// </returns> GenerateEmailConfirmationTokenAsync(TUser user)1456 public virtual Task<string> GenerateEmailConfirmationTokenAsync(TUser user) 1457 { 1458 ThrowIfDisposed(); 1459 return GenerateUserTokenAsync(user, Options.Tokens.EmailConfirmationTokenProvider, ConfirmEmailTokenPurpose); 1460 } 1461 1462 /// <summary> 1463 /// Validates that an email confirmation token matches the specified <paramref name="user"/>. 1464 /// </summary> 1465 /// <param name="user">The user to validate the token against.</param> 1466 /// <param name="token">The email confirmation token to validate.</param> 1467 /// <returns> 1468 /// The <see cref="Task"/> that represents the asynchronous operation, containing the <see cref="IdentityResult"/> 1469 /// of the operation. 1470 /// </returns> ConfirmEmailAsync(TUser user, string token)1471 public virtual async Task<IdentityResult> ConfirmEmailAsync(TUser user, string token) 1472 { 1473 ThrowIfDisposed(); 1474 var store = GetEmailStore(); 1475 if (user == null) 1476 { 1477 throw new ArgumentNullException(nameof(user)); 1478 } 1479 1480 if (!await VerifyUserTokenAsync(user, Options.Tokens.EmailConfirmationTokenProvider, ConfirmEmailTokenPurpose, token)) 1481 { 1482 return IdentityResult.Failed(ErrorDescriber.InvalidToken()); 1483 } 1484 await store.SetEmailConfirmedAsync(user, true, CancellationToken); 1485 return await UpdateUserAsync(user); 1486 } 1487 1488 /// <summary> 1489 /// Gets a flag indicating whether the email address for the specified <paramref name="user"/> has been verified, true if the email address is verified otherwise 1490 /// false. 1491 /// </summary> 1492 /// <param name="user">The user whose email confirmation status should be returned.</param> 1493 /// <returns> 1494 /// The task object containing the results of the asynchronous operation, a flag indicating whether the email address for the specified <paramref name="user"/> 1495 /// has been confirmed or not. 1496 /// </returns> IsEmailConfirmedAsync(TUser user)1497 public virtual async Task<bool> IsEmailConfirmedAsync(TUser user) 1498 { 1499 ThrowIfDisposed(); 1500 var store = GetEmailStore(); 1501 if (user == null) 1502 { 1503 throw new ArgumentNullException(nameof(user)); 1504 } 1505 return await store.GetEmailConfirmedAsync(user, CancellationToken); 1506 } 1507 1508 /// <summary> 1509 /// Generates an email change token for the specified user. 1510 /// </summary> 1511 /// <param name="user">The user to generate an email change token for.</param> 1512 /// <param name="newEmail">The new email address.</param> 1513 /// <returns> 1514 /// The <see cref="Task"/> that represents the asynchronous operation, an email change token. 1515 /// </returns> GenerateChangeEmailTokenAsync(TUser user, string newEmail)1516 public virtual Task<string> GenerateChangeEmailTokenAsync(TUser user, string newEmail) 1517 { 1518 ThrowIfDisposed(); 1519 return GenerateUserTokenAsync(user, Options.Tokens.ChangeEmailTokenProvider, GetChangeEmailTokenPurpose(newEmail)); 1520 } 1521 1522 /// <summary> 1523 /// Updates a users emails if the specified email change <paramref name="token"/> is valid for the user. 1524 /// </summary> 1525 /// <param name="user">The user whose email should be updated.</param> 1526 /// <param name="newEmail">The new email address.</param> 1527 /// <param name="token">The change email token to be verified.</param> 1528 /// <returns> 1529 /// The <see cref="Task"/> that represents the asynchronous operation, containing the <see cref="IdentityResult"/> 1530 /// of the operation. 1531 /// </returns> ChangeEmailAsync(TUser user, string newEmail, string token)1532 public virtual async Task<IdentityResult> ChangeEmailAsync(TUser user, string newEmail, string token) 1533 { 1534 ThrowIfDisposed(); 1535 if (user == null) 1536 { 1537 throw new ArgumentNullException(nameof(user)); 1538 } 1539 1540 // Make sure the token is valid and the stamp matches 1541 if (!await VerifyUserTokenAsync(user, Options.Tokens.ChangeEmailTokenProvider, GetChangeEmailTokenPurpose(newEmail), token)) 1542 { 1543 return IdentityResult.Failed(ErrorDescriber.InvalidToken()); 1544 } 1545 var store = GetEmailStore(); 1546 await store.SetEmailAsync(user, newEmail, CancellationToken); 1547 await store.SetEmailConfirmedAsync(user, true, CancellationToken); 1548 await UpdateSecurityStampInternal(user); 1549 return await UpdateUserAsync(user); 1550 } 1551 1552 /// <summary> 1553 /// Gets the telephone number, if any, for the specified <paramref name="user"/>. 1554 /// </summary> 1555 /// <param name="user">The user whose telephone number should be retrieved.</param> 1556 /// <returns>The <see cref="Task"/> that represents the asynchronous operation, containing the user's telephone number, if any.</returns> GetPhoneNumberAsync(TUser user)1557 public virtual async Task<string> GetPhoneNumberAsync(TUser user) 1558 { 1559 ThrowIfDisposed(); 1560 var store = GetPhoneNumberStore(); 1561 if (user == null) 1562 { 1563 throw new ArgumentNullException(nameof(user)); 1564 } 1565 return await store.GetPhoneNumberAsync(user, CancellationToken); 1566 } 1567 1568 /// <summary> 1569 /// Sets the phone number for the specified <paramref name="user"/>. 1570 /// </summary> 1571 /// <param name="user">The user whose phone number to set.</param> 1572 /// <param name="phoneNumber">The phone number to set.</param> 1573 /// <returns> 1574 /// The <see cref="Task"/> that represents the asynchronous operation, containing the <see cref="IdentityResult"/> 1575 /// of the operation. 1576 /// </returns> SetPhoneNumberAsync(TUser user, string phoneNumber)1577 public virtual async Task<IdentityResult> SetPhoneNumberAsync(TUser user, string phoneNumber) 1578 { 1579 ThrowIfDisposed(); 1580 var store = GetPhoneNumberStore(); 1581 if (user == null) 1582 { 1583 throw new ArgumentNullException(nameof(user)); 1584 } 1585 1586 await store.SetPhoneNumberAsync(user, phoneNumber, CancellationToken); 1587 await store.SetPhoneNumberConfirmedAsync(user, false, CancellationToken); 1588 await UpdateSecurityStampInternal(user); 1589 return await UpdateUserAsync(user); 1590 } 1591 1592 /// <summary> 1593 /// Sets the phone number for the specified <paramref name="user"/> if the specified 1594 /// change <paramref name="token"/> is valid. 1595 /// </summary> 1596 /// <param name="user">The user whose phone number to set.</param> 1597 /// <param name="phoneNumber">The phone number to set.</param> 1598 /// <param name="token">The phone number confirmation token to validate.</param> 1599 /// <returns> 1600 /// The <see cref="Task"/> that represents the asynchronous operation, containing the <see cref="IdentityResult"/> 1601 /// of the operation. 1602 /// </returns> ChangePhoneNumberAsync(TUser user, string phoneNumber, string token)1603 public virtual async Task<IdentityResult> ChangePhoneNumberAsync(TUser user, string phoneNumber, string token) 1604 { 1605 ThrowIfDisposed(); 1606 var store = GetPhoneNumberStore(); 1607 if (user == null) 1608 { 1609 throw new ArgumentNullException(nameof(user)); 1610 } 1611 1612 if (!await VerifyChangePhoneNumberTokenAsync(user, token, phoneNumber)) 1613 { 1614 Logger.LogWarning(7, "Change phone number for user {userId} failed with invalid token.", await GetUserIdAsync(user)); 1615 return IdentityResult.Failed(ErrorDescriber.InvalidToken()); 1616 } 1617 await store.SetPhoneNumberAsync(user, phoneNumber, CancellationToken); 1618 await store.SetPhoneNumberConfirmedAsync(user, true, CancellationToken); 1619 await UpdateSecurityStampInternal(user); 1620 return await UpdateUserAsync(user); 1621 } 1622 1623 /// <summary> 1624 /// Gets a flag indicating whether the specified <paramref name="user"/>'s telephone number has been confirmed. 1625 /// </summary> 1626 /// <param name="user">The user to return a flag for, indicating whether their telephone number is confirmed.</param> 1627 /// <returns> 1628 /// The <see cref="Task"/> that represents the asynchronous operation, returning true if the specified <paramref name="user"/> has a confirmed 1629 /// telephone number otherwise false. 1630 /// </returns> IsPhoneNumberConfirmedAsync(TUser user)1631 public virtual Task<bool> IsPhoneNumberConfirmedAsync(TUser user) 1632 { 1633 ThrowIfDisposed(); 1634 var store = GetPhoneNumberStore(); 1635 if (user == null) 1636 { 1637 throw new ArgumentNullException(nameof(user)); 1638 } 1639 return store.GetPhoneNumberConfirmedAsync(user, CancellationToken); 1640 } 1641 1642 /// <summary> 1643 /// Generates a telephone number change token for the specified user. 1644 /// </summary> 1645 /// <param name="user">The user to generate a telephone number token for.</param> 1646 /// <param name="phoneNumber">The new phone number the validation token should be sent to.</param> 1647 /// <returns> 1648 /// The <see cref="Task"/> that represents the asynchronous operation, containing the telephone change number token. 1649 /// </returns> GenerateChangePhoneNumberTokenAsync(TUser user, string phoneNumber)1650 public virtual Task<string> GenerateChangePhoneNumberTokenAsync(TUser user, string phoneNumber) 1651 { 1652 ThrowIfDisposed(); 1653 return GenerateUserTokenAsync(user, Options.Tokens.ChangePhoneNumberTokenProvider, ChangePhoneNumberTokenPurpose + ":" + phoneNumber); 1654 } 1655 1656 /// <summary> 1657 /// Returns a flag indicating whether the specified <paramref name="user"/>'s phone number change verification 1658 /// token is valid for the given <paramref name="phoneNumber"/>. 1659 /// </summary> 1660 /// <param name="user">The user to validate the token against.</param> 1661 /// <param name="token">The telephone number change token to validate.</param> 1662 /// <param name="phoneNumber">The telephone number the token was generated for.</param> 1663 /// <returns> 1664 /// The <see cref="Task"/> that represents the asynchronous operation, returning true if the <paramref name="token"/> 1665 /// is valid, otherwise false. 1666 /// </returns> VerifyChangePhoneNumberTokenAsync(TUser user, string token, string phoneNumber)1667 public virtual Task<bool> VerifyChangePhoneNumberTokenAsync(TUser user, string token, string phoneNumber) 1668 { 1669 ThrowIfDisposed(); 1670 if (user == null) 1671 { 1672 throw new ArgumentNullException(nameof(user)); 1673 } 1674 1675 // Make sure the token is valid and the stamp matches 1676 return VerifyUserTokenAsync(user, Options.Tokens.ChangePhoneNumberTokenProvider, ChangePhoneNumberTokenPurpose+":"+ phoneNumber, token); 1677 } 1678 1679 /// <summary> 1680 /// Returns a flag indicating whether the specified <paramref name="token"/> is valid for 1681 /// the given <paramref name="user"/> and <paramref name="purpose"/>. 1682 /// </summary> 1683 /// <param name="user">The user to validate the token against.</param> 1684 /// <param name="tokenProvider">The token provider used to generate the token.</param> 1685 /// <param name="purpose">The purpose the token should be generated for.</param> 1686 /// <param name="token">The token to validate</param> 1687 /// <returns> 1688 /// The <see cref="Task"/> that represents the asynchronous operation, returning true if the <paramref name="token"/> 1689 /// is valid, otherwise false. 1690 /// </returns> VerifyUserTokenAsync(TUser user, string tokenProvider, string purpose, string token)1691 public virtual async Task<bool> VerifyUserTokenAsync(TUser user, string tokenProvider, string purpose, string token) 1692 { 1693 ThrowIfDisposed(); 1694 if (user == null) 1695 { 1696 throw new ArgumentNullException(nameof(user)); 1697 } 1698 if (tokenProvider == null) 1699 { 1700 throw new ArgumentNullException(nameof(tokenProvider)); 1701 } 1702 1703 if (!_tokenProviders.ContainsKey(tokenProvider)) 1704 { 1705 throw new NotSupportedException(Resources.FormatNoTokenProvider(nameof(TUser), tokenProvider)); 1706 } 1707 // Make sure the token is valid 1708 var result = await _tokenProviders[tokenProvider].ValidateAsync(purpose, token, this, user); 1709 1710 if (!result) 1711 { 1712 Logger.LogWarning(9, "VerifyUserTokenAsync() failed with purpose: {purpose} for user {userId}.", purpose, await GetUserIdAsync(user)); 1713 } 1714 return result; 1715 } 1716 1717 /// <summary> 1718 /// Generates a token for the given <paramref name="user"/> and <paramref name="purpose"/>. 1719 /// </summary> 1720 /// <param name="purpose">The purpose the token will be for.</param> 1721 /// <param name="user">The user the token will be for.</param> 1722 /// <param name="tokenProvider">The provider which will generate the token.</param> 1723 /// <returns> 1724 /// The <see cref="Task"/> that represents result of the asynchronous operation, a token for 1725 /// the given user and purpose. 1726 /// </returns> GenerateUserTokenAsync(TUser user, string tokenProvider, string purpose)1727 public virtual Task<string> GenerateUserTokenAsync(TUser user, string tokenProvider, string purpose) 1728 { 1729 ThrowIfDisposed(); 1730 if (user == null) 1731 { 1732 throw new ArgumentNullException(nameof(user)); 1733 } 1734 if (tokenProvider == null) 1735 { 1736 throw new ArgumentNullException(nameof(tokenProvider)); 1737 } 1738 if (!_tokenProviders.ContainsKey(tokenProvider)) 1739 { 1740 throw new NotSupportedException(Resources.FormatNoTokenProvider(nameof(TUser), tokenProvider)); 1741 } 1742 1743 return _tokenProviders[tokenProvider].GenerateAsync(purpose, this, user); 1744 } 1745 1746 /// <summary> 1747 /// Registers a token provider. 1748 /// </summary> 1749 /// <param name="providerName">The name of the provider to register.</param> 1750 /// <param name="provider">The provider to register.</param> RegisterTokenProvider(string providerName, IUserTwoFactorTokenProvider<TUser> provider)1751 public virtual void RegisterTokenProvider(string providerName, IUserTwoFactorTokenProvider<TUser> provider) 1752 { 1753 ThrowIfDisposed(); 1754 if (provider == null) 1755 { 1756 throw new ArgumentNullException(nameof(provider)); 1757 } 1758 _tokenProviders[providerName] = provider; 1759 } 1760 1761 /// <summary> 1762 /// Gets a list of valid two factor token providers for the specified <paramref name="user"/>, 1763 /// as an asynchronous operation. 1764 /// </summary> 1765 /// <param name="user">The user the whose two factor authentication providers will be returned.</param> 1766 /// <returns> 1767 /// The <see cref="Task"/> that represents result of the asynchronous operation, a list of two 1768 /// factor authentication providers for the specified user. 1769 /// </returns> GetValidTwoFactorProvidersAsync(TUser user)1770 public virtual async Task<IList<string>> GetValidTwoFactorProvidersAsync(TUser user) 1771 { 1772 ThrowIfDisposed(); 1773 if (user == null) 1774 { 1775 throw new ArgumentNullException(nameof(user)); 1776 } 1777 var results = new List<string>(); 1778 foreach (var f in _tokenProviders) 1779 { 1780 if (await f.Value.CanGenerateTwoFactorTokenAsync(this, user)) 1781 { 1782 results.Add(f.Key); 1783 } 1784 } 1785 return results; 1786 } 1787 1788 /// <summary> 1789 /// Verifies the specified two factor authentication <paramref name="token" /> against the <paramref name="user"/>. 1790 /// </summary> 1791 /// <param name="user">The user the token is supposed to be for.</param> 1792 /// <param name="tokenProvider">The provider which will verify the token.</param> 1793 /// <param name="token">The token to verify.</param> 1794 /// <returns> 1795 /// The <see cref="Task"/> that represents result of the asynchronous operation, true if the token is valid, 1796 /// otherwise false. 1797 /// </returns> VerifyTwoFactorTokenAsync(TUser user, string tokenProvider, string token)1798 public virtual async Task<bool> VerifyTwoFactorTokenAsync(TUser user, string tokenProvider, string token) 1799 { 1800 ThrowIfDisposed(); 1801 if (user == null) 1802 { 1803 throw new ArgumentNullException(nameof(user)); 1804 } 1805 if (!_tokenProviders.ContainsKey(tokenProvider)) 1806 { 1807 throw new NotSupportedException(Resources.FormatNoTokenProvider(nameof(TUser), tokenProvider)); 1808 } 1809 1810 // Make sure the token is valid 1811 var result = await _tokenProviders[tokenProvider].ValidateAsync("TwoFactor", token, this, user); 1812 if (!result) 1813 { 1814 Logger.LogWarning(10, $"{nameof(VerifyTwoFactorTokenAsync)}() failed for user {await GetUserIdAsync(user)}."); 1815 } 1816 return result; 1817 } 1818 1819 /// <summary> 1820 /// Gets a two factor authentication token for the specified <paramref name="user"/>. 1821 /// </summary> 1822 /// <param name="user">The user the token is for.</param> 1823 /// <param name="tokenProvider">The provider which will generate the token.</param> 1824 /// <returns> 1825 /// The <see cref="Task"/> that represents result of the asynchronous operation, a two factor authentication token 1826 /// for the user. 1827 /// </returns> GenerateTwoFactorTokenAsync(TUser user, string tokenProvider)1828 public virtual Task<string> GenerateTwoFactorTokenAsync(TUser user, string tokenProvider) 1829 { 1830 ThrowIfDisposed(); 1831 if (user == null) 1832 { 1833 throw new ArgumentNullException(nameof(user)); 1834 } 1835 if (!_tokenProviders.ContainsKey(tokenProvider)) 1836 { 1837 throw new NotSupportedException(Resources.FormatNoTokenProvider(nameof(TUser), tokenProvider)); 1838 } 1839 1840 return _tokenProviders[tokenProvider].GenerateAsync("TwoFactor", this, user); 1841 } 1842 1843 /// <summary> 1844 /// Returns a flag indicating whether the specified <paramref name="user"/> has two factor authentication enabled or not, 1845 /// as an asynchronous operation. 1846 /// </summary> 1847 /// <param name="user">The user whose two factor authentication enabled status should be retrieved.</param> 1848 /// <returns> 1849 /// The <see cref="Task"/> that represents the asynchronous operation, true if the specified <paramref name="user "/> 1850 /// has two factor authentication enabled, otherwise false. 1851 /// </returns> GetTwoFactorEnabledAsync(TUser user)1852 public virtual async Task<bool> GetTwoFactorEnabledAsync(TUser user) 1853 { 1854 ThrowIfDisposed(); 1855 var store = GetUserTwoFactorStore(); 1856 if (user == null) 1857 { 1858 throw new ArgumentNullException(nameof(user)); 1859 } 1860 return await store.GetTwoFactorEnabledAsync(user, CancellationToken); 1861 } 1862 1863 /// <summary> 1864 /// Sets a flag indicating whether the specified <paramref name="user"/> has two factor authentication enabled or not, 1865 /// as an asynchronous operation. 1866 /// </summary> 1867 /// <param name="user">The user whose two factor authentication enabled status should be set.</param> 1868 /// <param name="enabled">A flag indicating whether the specified <paramref name="user"/> has two factor authentication enabled.</param> 1869 /// <returns> 1870 /// The <see cref="Task"/> that represents the asynchronous operation, the <see cref="IdentityResult"/> of the operation 1871 /// </returns> SetTwoFactorEnabledAsync(TUser user, bool enabled)1872 public virtual async Task<IdentityResult> SetTwoFactorEnabledAsync(TUser user, bool enabled) 1873 { 1874 ThrowIfDisposed(); 1875 var store = GetUserTwoFactorStore(); 1876 if (user == null) 1877 { 1878 throw new ArgumentNullException(nameof(user)); 1879 } 1880 1881 await store.SetTwoFactorEnabledAsync(user, enabled, CancellationToken); 1882 await UpdateSecurityStampInternal(user); 1883 return await UpdateUserAsync(user); 1884 } 1885 1886 /// <summary> 1887 /// Returns a flag indicating whether the specified <paramref name="user"/> his locked out, 1888 /// as an asynchronous operation. 1889 /// </summary> 1890 /// <param name="user">The user whose locked out status should be retrieved.</param> 1891 /// <returns> 1892 /// The <see cref="Task"/> that represents the asynchronous operation, true if the specified <paramref name="user "/> 1893 /// is locked out, otherwise false. 1894 /// </returns> IsLockedOutAsync(TUser user)1895 public virtual async Task<bool> IsLockedOutAsync(TUser user) 1896 { 1897 ThrowIfDisposed(); 1898 var store = GetUserLockoutStore(); 1899 if (user == null) 1900 { 1901 throw new ArgumentNullException(nameof(user)); 1902 } 1903 if (!await store.GetLockoutEnabledAsync(user, CancellationToken)) 1904 { 1905 return false; 1906 } 1907 var lockoutTime = await store.GetLockoutEndDateAsync(user, CancellationToken); 1908 return lockoutTime >= DateTimeOffset.UtcNow; 1909 } 1910 1911 /// <summary> 1912 /// Sets a flag indicating whether the specified <paramref name="user"/> is locked out, 1913 /// as an asynchronous operation. 1914 /// </summary> 1915 /// <param name="user">The user whose locked out status should be set.</param> 1916 /// <param name="enabled">Flag indicating whether the user is locked out or not.</param> 1917 /// <returns> 1918 /// The <see cref="Task"/> that represents the asynchronous operation, the <see cref="IdentityResult"/> of the operation 1919 /// </returns> SetLockoutEnabledAsync(TUser user, bool enabled)1920 public virtual async Task<IdentityResult> SetLockoutEnabledAsync(TUser user, bool enabled) 1921 { 1922 ThrowIfDisposed(); 1923 var store = GetUserLockoutStore(); 1924 if (user == null) 1925 { 1926 throw new ArgumentNullException(nameof(user)); 1927 } 1928 1929 await store.SetLockoutEnabledAsync(user, enabled, CancellationToken); 1930 return await UpdateUserAsync(user); 1931 } 1932 1933 /// <summary> 1934 /// Retrieves a flag indicating whether user lockout can enabled for the specified user. 1935 /// </summary> 1936 /// <param name="user">The user whose ability to be locked out should be returned.</param> 1937 /// <returns> 1938 /// The <see cref="Task"/> that represents the asynchronous operation, true if a user can be locked out, otherwise false. 1939 /// </returns> GetLockoutEnabledAsync(TUser user)1940 public virtual async Task<bool> GetLockoutEnabledAsync(TUser user) 1941 { 1942 ThrowIfDisposed(); 1943 var store = GetUserLockoutStore(); 1944 if (user == null) 1945 { 1946 throw new ArgumentNullException(nameof(user)); 1947 } 1948 return await store.GetLockoutEnabledAsync(user, CancellationToken); 1949 } 1950 1951 /// <summary> 1952 /// Gets the last <see cref="DateTimeOffset"/> a user's last lockout expired, if any. 1953 /// Any time in the past should be indicates a user is not locked out. 1954 /// </summary> 1955 /// <param name="user">The user whose lockout date should be retrieved.</param> 1956 /// <returns> 1957 /// A <see cref="Task{TResult}"/> that represents the lookup, a <see cref="DateTimeOffset"/> containing the last time a user's lockout expired, if any. 1958 /// </returns> GetLockoutEndDateAsync(TUser user)1959 public virtual async Task<DateTimeOffset?> GetLockoutEndDateAsync(TUser user) 1960 { 1961 ThrowIfDisposed(); 1962 var store = GetUserLockoutStore(); 1963 if (user == null) 1964 { 1965 throw new ArgumentNullException(nameof(user)); 1966 } 1967 return await store.GetLockoutEndDateAsync(user, CancellationToken); 1968 } 1969 1970 /// <summary> 1971 /// Locks out a user until the specified end date has passed. Setting a end date in the past immediately unlocks a user. 1972 /// </summary> 1973 /// <param name="user">The user whose lockout date should be set.</param> 1974 /// <param name="lockoutEnd">The <see cref="DateTimeOffset"/> after which the <paramref name="user"/>'s lockout should end.</param> 1975 /// <returns>The <see cref="Task"/> that represents the asynchronous operation, containing the <see cref="IdentityResult"/> of the operation.</returns> SetLockoutEndDateAsync(TUser user, DateTimeOffset? lockoutEnd)1976 public virtual async Task<IdentityResult> SetLockoutEndDateAsync(TUser user, DateTimeOffset? lockoutEnd) 1977 { 1978 ThrowIfDisposed(); 1979 var store = GetUserLockoutStore(); 1980 if (user == null) 1981 { 1982 throw new ArgumentNullException(nameof(user)); 1983 } 1984 1985 if (!await store.GetLockoutEnabledAsync(user, CancellationToken)) 1986 { 1987 Logger.LogWarning(11, "Lockout for user {userId} failed because lockout is not enabled for this user.", await GetUserIdAsync(user)); 1988 return IdentityResult.Failed(ErrorDescriber.UserLockoutNotEnabled()); 1989 } 1990 await store.SetLockoutEndDateAsync(user, lockoutEnd, CancellationToken); 1991 return await UpdateUserAsync(user); 1992 } 1993 1994 /// <summary> 1995 /// Increments the access failed count for the user as an asynchronous operation. 1996 /// If the failed access account is greater than or equal to the configured maximum number of attempts, 1997 /// the user will be locked out for the configured lockout time span. 1998 /// </summary> 1999 /// <param name="user">The user whose failed access count to increment.</param> 2000 /// <returns>The <see cref="Task"/> that represents the asynchronous operation, containing the <see cref="IdentityResult"/> of the operation.</returns> AccessFailedAsync(TUser user)2001 public virtual async Task<IdentityResult> AccessFailedAsync(TUser user) 2002 { 2003 ThrowIfDisposed(); 2004 var store = GetUserLockoutStore(); 2005 if (user == null) 2006 { 2007 throw new ArgumentNullException(nameof(user)); 2008 } 2009 2010 // If this puts the user over the threshold for lockout, lock them out and reset the access failed count 2011 var count = await store.IncrementAccessFailedCountAsync(user, CancellationToken); 2012 if (count < Options.Lockout.MaxFailedAccessAttempts) 2013 { 2014 return await UpdateUserAsync(user); 2015 } 2016 Logger.LogWarning(12, "User {userId} is locked out.", await GetUserIdAsync(user)); 2017 await store.SetLockoutEndDateAsync(user, DateTimeOffset.UtcNow.Add(Options.Lockout.DefaultLockoutTimeSpan), 2018 CancellationToken); 2019 await store.ResetAccessFailedCountAsync(user, CancellationToken); 2020 return await UpdateUserAsync(user); 2021 } 2022 2023 /// <summary> 2024 /// Resets the access failed count for the specified <paramref name="user"/>. 2025 /// </summary> 2026 /// <param name="user">The user whose failed access count should be reset.</param> 2027 /// <returns>The <see cref="Task"/> that represents the asynchronous operation, containing the <see cref="IdentityResult"/> of the operation.</returns> ResetAccessFailedCountAsync(TUser user)2028 public virtual async Task<IdentityResult> ResetAccessFailedCountAsync(TUser user) 2029 { 2030 ThrowIfDisposed(); 2031 var store = GetUserLockoutStore(); 2032 if (user == null) 2033 { 2034 throw new ArgumentNullException(nameof(user)); 2035 } 2036 2037 if (await GetAccessFailedCountAsync(user) == 0) 2038 { 2039 return IdentityResult.Success; 2040 } 2041 await store.ResetAccessFailedCountAsync(user, CancellationToken); 2042 return await UpdateUserAsync(user); 2043 } 2044 2045 /// <summary> 2046 /// Retrieves the current number of failed accesses for the given <paramref name="user"/>. 2047 /// </summary> 2048 /// <param name="user">The user whose access failed count should be retrieved for.</param> 2049 /// <returns>The <see cref="Task"/> that contains the result the asynchronous operation, the current failed access count 2050 /// for the user.</returns> GetAccessFailedCountAsync(TUser user)2051 public virtual async Task<int> GetAccessFailedCountAsync(TUser user) 2052 { 2053 ThrowIfDisposed(); 2054 var store = GetUserLockoutStore(); 2055 if (user == null) 2056 { 2057 throw new ArgumentNullException(nameof(user)); 2058 } 2059 return await store.GetAccessFailedCountAsync(user, CancellationToken); 2060 } 2061 2062 /// <summary> 2063 /// Returns a list of users from the user store who have the specified <paramref name="claim"/>. 2064 /// </summary> 2065 /// <param name="claim">The claim to look for.</param> 2066 /// <returns> 2067 /// A <see cref="Task{TResult}"/> that represents the result of the asynchronous query, a list of <typeparamref name="TUser"/>s who 2068 /// have the specified claim. 2069 /// </returns> GetUsersForClaimAsync(Claim claim)2070 public virtual Task<IList<TUser>> GetUsersForClaimAsync(Claim claim) 2071 { 2072 ThrowIfDisposed(); 2073 var store = GetClaimStore(); 2074 if (claim == null) 2075 { 2076 throw new ArgumentNullException(nameof(claim)); 2077 } 2078 return store.GetUsersForClaimAsync(claim, CancellationToken); 2079 } 2080 2081 /// <summary> 2082 /// Returns a list of users from the user store who are members of the specified <paramref name="roleName"/>. 2083 /// </summary> 2084 /// <param name="roleName">The name of the role whose users should be returned.</param> 2085 /// <returns> 2086 /// A <see cref="Task{TResult}"/> that represents the result of the asynchronous query, a list of <typeparamref name="TUser"/>s who 2087 /// are members of the specified role. 2088 /// </returns> GetUsersInRoleAsync(string roleName)2089 public virtual Task<IList<TUser>> GetUsersInRoleAsync(string roleName) 2090 { 2091 ThrowIfDisposed(); 2092 var store = GetUserRoleStore(); 2093 if (roleName == null) 2094 { 2095 throw new ArgumentNullException(nameof(roleName)); 2096 } 2097 2098 return store.GetUsersInRoleAsync(NormalizeKey(roleName), CancellationToken); 2099 } 2100 2101 /// <summary> 2102 /// Returns an authentication token for a user. 2103 /// </summary> 2104 /// <param name="user"></param> 2105 /// <param name="loginProvider">The authentication scheme for the provider the token is associated with.</param> 2106 /// <param name="tokenName">The name of the token.</param> 2107 /// <returns>The authentication token for a user</returns> GetAuthenticationTokenAsync(TUser user, string loginProvider, string tokenName)2108 public virtual Task<string> GetAuthenticationTokenAsync(TUser user, string loginProvider, string tokenName) 2109 { 2110 ThrowIfDisposed(); 2111 var store = GetAuthenticationTokenStore(); 2112 if (user == null) 2113 { 2114 throw new ArgumentNullException(nameof(user)); 2115 } 2116 if (loginProvider == null) 2117 { 2118 throw new ArgumentNullException(nameof(loginProvider)); 2119 } 2120 if (tokenName == null) 2121 { 2122 throw new ArgumentNullException(nameof(tokenName)); 2123 } 2124 2125 return store.GetTokenAsync(user, loginProvider, tokenName, CancellationToken); 2126 } 2127 2128 /// <summary> 2129 /// Sets an authentication token for a user. 2130 /// </summary> 2131 /// <param name="user"></param> 2132 /// <param name="loginProvider">The authentication scheme for the provider the token is associated with.</param> 2133 /// <param name="tokenName">The name of the token.</param> 2134 /// <param name="tokenValue">The value of the token.</param> 2135 /// <returns>Whether the user was successfully updated.</returns> SetAuthenticationTokenAsync(TUser user, string loginProvider, string tokenName, string tokenValue)2136 public virtual async Task<IdentityResult> SetAuthenticationTokenAsync(TUser user, string loginProvider, string tokenName, string tokenValue) 2137 { 2138 ThrowIfDisposed(); 2139 var store = GetAuthenticationTokenStore(); 2140 if (user == null) 2141 { 2142 throw new ArgumentNullException(nameof(user)); 2143 } 2144 if (loginProvider == null) 2145 { 2146 throw new ArgumentNullException(nameof(loginProvider)); 2147 } 2148 if (tokenName == null) 2149 { 2150 throw new ArgumentNullException(nameof(tokenName)); 2151 } 2152 2153 // REVIEW: should updating any tokens affect the security stamp? 2154 await store.SetTokenAsync(user, loginProvider, tokenName, tokenValue, CancellationToken); 2155 return await UpdateUserAsync(user); 2156 } 2157 2158 /// <summary> 2159 /// Remove an authentication token for a user. 2160 /// </summary> 2161 /// <param name="user"></param> 2162 /// <param name="loginProvider">The authentication scheme for the provider the token is associated with.</param> 2163 /// <param name="tokenName">The name of the token.</param> 2164 /// <returns>Whether a token was removed.</returns> RemoveAuthenticationTokenAsync(TUser user, string loginProvider, string tokenName)2165 public virtual async Task<IdentityResult> RemoveAuthenticationTokenAsync(TUser user, string loginProvider, string tokenName) 2166 { 2167 ThrowIfDisposed(); 2168 var store = GetAuthenticationTokenStore(); 2169 if (user == null) 2170 { 2171 throw new ArgumentNullException(nameof(user)); 2172 } 2173 if (loginProvider == null) 2174 { 2175 throw new ArgumentNullException(nameof(loginProvider)); 2176 } 2177 if (tokenName == null) 2178 { 2179 throw new ArgumentNullException(nameof(tokenName)); 2180 } 2181 2182 await store.RemoveTokenAsync(user, loginProvider, tokenName, CancellationToken); 2183 return await UpdateUserAsync(user); 2184 } 2185 2186 /// <summary> 2187 /// Returns the authenticator key for the user. 2188 /// </summary> 2189 /// <param name="user">The user.</param> 2190 /// <returns>The authenticator key</returns> GetAuthenticatorKeyAsync(TUser user)2191 public virtual Task<string> GetAuthenticatorKeyAsync(TUser user) 2192 { 2193 ThrowIfDisposed(); 2194 var store = GetAuthenticatorKeyStore(); 2195 if (user == null) 2196 { 2197 throw new ArgumentNullException(nameof(user)); 2198 } 2199 return store.GetAuthenticatorKeyAsync(user, CancellationToken); 2200 } 2201 2202 /// <summary> 2203 /// Resets the authenticator key for the user. 2204 /// </summary> 2205 /// <param name="user">The user.</param> 2206 /// <returns>Whether the user was successfully updated.</returns> ResetAuthenticatorKeyAsync(TUser user)2207 public virtual async Task<IdentityResult> ResetAuthenticatorKeyAsync(TUser user) 2208 { 2209 ThrowIfDisposed(); 2210 var store = GetAuthenticatorKeyStore(); 2211 if (user == null) 2212 { 2213 throw new ArgumentNullException(nameof(user)); 2214 } 2215 await store.SetAuthenticatorKeyAsync(user, GenerateNewAuthenticatorKey(), CancellationToken); 2216 await UpdateSecurityStampInternal(user); 2217 return await UpdateAsync(user); 2218 } 2219 2220 /// <summary> 2221 /// Generates a new base32 encoded 160-bit security secret (size of SHA1 hash). 2222 /// </summary> 2223 /// <returns>The new security secret.</returns> GenerateNewAuthenticatorKey()2224 public virtual string GenerateNewAuthenticatorKey() 2225 => NewSecurityStamp(); 2226 2227 /// <summary> 2228 /// Generates recovery codes for the user, this invalidates any previous recovery codes for the user. 2229 /// </summary> 2230 /// <param name="user">The user to generate recovery codes for.</param> 2231 /// <param name="number">The number of codes to generate.</param> 2232 /// <returns>The new recovery codes for the user. Note: there may be less than number returned, as duplicates will be removed.</returns> GenerateNewTwoFactorRecoveryCodesAsync(TUser user, int number)2233 public virtual async Task<IEnumerable<string>> GenerateNewTwoFactorRecoveryCodesAsync(TUser user, int number) 2234 { 2235 ThrowIfDisposed(); 2236 var store = GetRecoveryCodeStore(); 2237 if (user == null) 2238 { 2239 throw new ArgumentNullException(nameof(user)); 2240 } 2241 2242 var newCodes = new List<string>(number); 2243 for (var i = 0; i < number; i++) 2244 { 2245 newCodes.Add(CreateTwoFactorRecoveryCode()); 2246 } 2247 2248 await store.ReplaceCodesAsync(user, newCodes.Distinct(), CancellationToken); 2249 var update = await UpdateAsync(user); 2250 if (update.Succeeded) 2251 { 2252 return newCodes; 2253 } 2254 return null; 2255 } 2256 2257 /// <summary> 2258 /// Generate a new recovery code. 2259 /// </summary> 2260 /// <returns></returns> CreateTwoFactorRecoveryCode()2261 protected virtual string CreateTwoFactorRecoveryCode() 2262 => Guid.NewGuid().ToString().Substring(0, 8); 2263 2264 /// <summary> 2265 /// Returns whether a recovery code is valid for a user. Note: recovery codes are only valid 2266 /// once, and will be invalid after use. 2267 /// </summary> 2268 /// <param name="user">The user who owns the recovery code.</param> 2269 /// <param name="code">The recovery code to use.</param> 2270 /// <returns>True if the recovery code was found for the user.</returns> RedeemTwoFactorRecoveryCodeAsync(TUser user, string code)2271 public virtual async Task<IdentityResult> RedeemTwoFactorRecoveryCodeAsync(TUser user, string code) 2272 { 2273 ThrowIfDisposed(); 2274 var store = GetRecoveryCodeStore(); 2275 if (user == null) 2276 { 2277 throw new ArgumentNullException(nameof(user)); 2278 } 2279 2280 var success = await store.RedeemCodeAsync(user, code, CancellationToken); 2281 if (success) 2282 { 2283 return await UpdateAsync(user); 2284 } 2285 return IdentityResult.Failed(ErrorDescriber.RecoveryCodeRedemptionFailed()); 2286 } 2287 2288 /// <summary> 2289 /// Returns how many recovery code are still valid for a user. 2290 /// </summary> 2291 /// <param name="user">The user.</param> 2292 /// <returns>How many recovery code are still valid for a user.</returns> CountRecoveryCodesAsync(TUser user)2293 public virtual Task<int> CountRecoveryCodesAsync(TUser user) 2294 { 2295 ThrowIfDisposed(); 2296 var store = GetRecoveryCodeStore(); 2297 if (user == null) 2298 { 2299 throw new ArgumentNullException(nameof(user)); 2300 } 2301 2302 return store.CountCodesAsync(user, CancellationToken); 2303 } 2304 2305 /// <summary> 2306 /// Releases the unmanaged resources used by the role manager and optionally releases the managed resources. 2307 /// </summary> 2308 /// <param name="disposing">true to release both managed and unmanaged resources; false to release only unmanaged resources.</param> Dispose(bool disposing)2309 protected virtual void Dispose(bool disposing) 2310 { 2311 if (disposing && !_disposed) 2312 { 2313 Store.Dispose(); 2314 _disposed = true; 2315 } 2316 } 2317 GetUserTwoFactorStore()2318 private IUserTwoFactorStore<TUser> GetUserTwoFactorStore() 2319 { 2320 var cast = Store as IUserTwoFactorStore<TUser>; 2321 if (cast == null) 2322 { 2323 throw new NotSupportedException(Resources.StoreNotIUserTwoFactorStore); 2324 } 2325 return cast; 2326 } 2327 GetUserLockoutStore()2328 private IUserLockoutStore<TUser> GetUserLockoutStore() 2329 { 2330 var cast = Store as IUserLockoutStore<TUser>; 2331 if (cast == null) 2332 { 2333 throw new NotSupportedException(Resources.StoreNotIUserLockoutStore); 2334 } 2335 return cast; 2336 } 2337 GetEmailStore(bool throwOnFail = true)2338 private IUserEmailStore<TUser> GetEmailStore(bool throwOnFail = true) 2339 { 2340 var cast = Store as IUserEmailStore<TUser>; 2341 if (throwOnFail && cast == null) 2342 { 2343 throw new NotSupportedException(Resources.StoreNotIUserEmailStore); 2344 } 2345 return cast; 2346 } 2347 GetPhoneNumberStore()2348 private IUserPhoneNumberStore<TUser> GetPhoneNumberStore() 2349 { 2350 var cast = Store as IUserPhoneNumberStore<TUser>; 2351 if (cast == null) 2352 { 2353 throw new NotSupportedException(Resources.StoreNotIUserPhoneNumberStore); 2354 } 2355 return cast; 2356 } 2357 2358 /// <summary> 2359 /// Creates bytes to use as a security token from the user's security stamp. 2360 /// </summary> 2361 /// <param name="user">The user.</param> 2362 /// <returns>The security token bytes.</returns> CreateSecurityTokenAsync(TUser user)2363 public virtual async Task<byte[]> CreateSecurityTokenAsync(TUser user) 2364 { 2365 return Encoding.Unicode.GetBytes(await GetSecurityStampAsync(user)); 2366 } 2367 2368 // Update the security stamp if the store supports it UpdateSecurityStampInternal(TUser user)2369 private async Task UpdateSecurityStampInternal(TUser user) 2370 { 2371 if (SupportsUserSecurityStamp) 2372 { 2373 await GetSecurityStore().SetSecurityStampAsync(user, NewSecurityStamp(), CancellationToken); 2374 } 2375 } 2376 2377 /// <summary> 2378 /// Updates a user's password hash. 2379 /// </summary> 2380 /// <param name="user">The user.</param> 2381 /// <param name="newPassword">The new password.</param> 2382 /// <param name="validatePassword">Whether to validate the password.</param> 2383 /// <returns>Whether the password has was successfully updated.</returns> UpdatePasswordHash(TUser user, string newPassword, bool validatePassword)2384 protected virtual Task<IdentityResult> UpdatePasswordHash(TUser user, string newPassword, bool validatePassword) 2385 => UpdatePasswordHash(GetPasswordStore(), user, newPassword, validatePassword); 2386 UpdatePasswordHash(IUserPasswordStore<TUser> passwordStore, TUser user, string newPassword, bool validatePassword = true)2387 private async Task<IdentityResult> UpdatePasswordHash(IUserPasswordStore<TUser> passwordStore, 2388 TUser user, string newPassword, bool validatePassword = true) 2389 { 2390 if (validatePassword) 2391 { 2392 var validate = await ValidatePasswordAsync(user, newPassword); 2393 if (!validate.Succeeded) 2394 { 2395 return validate; 2396 } 2397 } 2398 var hash = newPassword != null ? PasswordHasher.HashPassword(user, newPassword) : null; 2399 await passwordStore.SetPasswordHashAsync(user, hash, CancellationToken); 2400 await UpdateSecurityStampInternal(user); 2401 return IdentityResult.Success; 2402 } 2403 GetUserRoleStore()2404 private IUserRoleStore<TUser> GetUserRoleStore() 2405 { 2406 var cast = Store as IUserRoleStore<TUser>; 2407 if (cast == null) 2408 { 2409 throw new NotSupportedException(Resources.StoreNotIUserRoleStore); 2410 } 2411 return cast; 2412 } 2413 NewSecurityStamp()2414 private static string NewSecurityStamp() 2415 { 2416 byte[] bytes = new byte[20]; 2417 _rng.GetBytes(bytes); 2418 return Base32.ToBase32(bytes); 2419 } 2420 2421 // IUserLoginStore methods GetLoginStore()2422 private IUserLoginStore<TUser> GetLoginStore() 2423 { 2424 var cast = Store as IUserLoginStore<TUser>; 2425 if (cast == null) 2426 { 2427 throw new NotSupportedException(Resources.StoreNotIUserLoginStore); 2428 } 2429 return cast; 2430 } 2431 GetSecurityStore()2432 private IUserSecurityStampStore<TUser> GetSecurityStore() 2433 { 2434 var cast = Store as IUserSecurityStampStore<TUser>; 2435 if (cast == null) 2436 { 2437 throw new NotSupportedException(Resources.StoreNotIUserSecurityStampStore); 2438 } 2439 return cast; 2440 } 2441 GetClaimStore()2442 private IUserClaimStore<TUser> GetClaimStore() 2443 { 2444 var cast = Store as IUserClaimStore<TUser>; 2445 if (cast == null) 2446 { 2447 throw new NotSupportedException(Resources.StoreNotIUserClaimStore); 2448 } 2449 return cast; 2450 } 2451 2452 2453 /// <summary> 2454 /// Generates the token purpose used to change email. 2455 /// </summary> 2456 /// <param name="newEmail">The new email address.</param> 2457 /// <returns>The token purpose.</returns> GetChangeEmailTokenPurpose(string newEmail)2458 protected static string GetChangeEmailTokenPurpose(string newEmail) 2459 { 2460 return "ChangeEmail:" + newEmail; 2461 } 2462 2463 /// <summary> 2464 /// Should return <see cref="IdentityResult.Success"/> if validation is successful. This is 2465 /// called before saving the user via Create or Update. 2466 /// </summary> 2467 /// <param name="user">The user</param> 2468 /// <returns>A <see cref="IdentityResult"/> representing whether validation was successful.</returns> ValidateUserAsync(TUser user)2469 protected async Task<IdentityResult> ValidateUserAsync(TUser user) 2470 { 2471 if (SupportsUserSecurityStamp) 2472 { 2473 var stamp = await GetSecurityStampAsync(user); 2474 if (stamp == null) 2475 { 2476 throw new InvalidOperationException(Resources.NullSecurityStamp); 2477 } 2478 } 2479 var errors = new List<IdentityError>(); 2480 foreach (var v in UserValidators) 2481 { 2482 var result = await v.ValidateAsync(this, user); 2483 if (!result.Succeeded) 2484 { 2485 errors.AddRange(result.Errors); 2486 } 2487 } 2488 if (errors.Count > 0) 2489 { 2490 Logger.LogWarning(13, "User {userId} validation failed: {errors}.", await GetUserIdAsync(user), string.Join(";", errors.Select(e => e.Code))); 2491 return IdentityResult.Failed(errors.ToArray()); 2492 } 2493 return IdentityResult.Success; 2494 } 2495 2496 /// <summary> 2497 /// Should return <see cref="IdentityResult.Success"/> if validation is successful. This is 2498 /// called before updating the password hash. 2499 /// </summary> 2500 /// <param name="user">The user.</param> 2501 /// <param name="password">The password.</param> 2502 /// <returns>A <see cref="IdentityResult"/> representing whether validation was successful.</returns> ValidatePasswordAsync(TUser user, string password)2503 protected async Task<IdentityResult> ValidatePasswordAsync(TUser user, string password) 2504 { 2505 var errors = new List<IdentityError>(); 2506 foreach (var v in PasswordValidators) 2507 { 2508 var result = await v.ValidateAsync(this, user, password); 2509 if (!result.Succeeded) 2510 { 2511 errors.AddRange(result.Errors); 2512 } 2513 } 2514 if (errors.Count > 0) 2515 { 2516 Logger.LogWarning(14, "User {userId} password validation failed: {errors}.", await GetUserIdAsync(user), string.Join(";", errors.Select(e => e.Code))); 2517 return IdentityResult.Failed(errors.ToArray()); 2518 } 2519 return IdentityResult.Success; 2520 } 2521 2522 /// <summary> 2523 /// Called to update the user after validating and updating the normalized email/user name. 2524 /// </summary> 2525 /// <param name="user">The user.</param> 2526 /// <returns>Whether the operation was successful.</returns> UpdateUserAsync(TUser user)2527 protected virtual async Task<IdentityResult> UpdateUserAsync(TUser user) 2528 { 2529 var result = await ValidateUserAsync(user); 2530 if (!result.Succeeded) 2531 { 2532 return result; 2533 } 2534 await UpdateNormalizedUserNameAsync(user); 2535 await UpdateNormalizedEmailAsync(user); 2536 return await Store.UpdateAsync(user, CancellationToken); 2537 } 2538 GetAuthenticatorKeyStore()2539 private IUserAuthenticatorKeyStore<TUser> GetAuthenticatorKeyStore() 2540 { 2541 var cast = Store as IUserAuthenticatorKeyStore<TUser>; 2542 if (cast == null) 2543 { 2544 throw new NotSupportedException(Resources.StoreNotIUserAuthenticatorKeyStore); 2545 } 2546 return cast; 2547 } 2548 GetRecoveryCodeStore()2549 private IUserTwoFactorRecoveryCodeStore<TUser> GetRecoveryCodeStore() 2550 { 2551 var cast = Store as IUserTwoFactorRecoveryCodeStore<TUser>; 2552 if (cast == null) 2553 { 2554 throw new NotSupportedException(Resources.StoreNotIUserTwoFactorRecoveryCodeStore); 2555 } 2556 return cast; 2557 } 2558 GetAuthenticationTokenStore()2559 private IUserAuthenticationTokenStore<TUser> GetAuthenticationTokenStore() 2560 { 2561 var cast = Store as IUserAuthenticationTokenStore<TUser>; 2562 if (cast == null) 2563 { 2564 throw new NotSupportedException(Resources.StoreNotIUserAuthenticationTokenStore); 2565 } 2566 return cast; 2567 } 2568 GetPasswordStore()2569 private IUserPasswordStore<TUser> GetPasswordStore() 2570 { 2571 var cast = Store as IUserPasswordStore<TUser>; 2572 if (cast == null) 2573 { 2574 throw new NotSupportedException(Resources.StoreNotIUserPasswordStore); 2575 } 2576 return cast; 2577 } 2578 2579 /// <summary> 2580 /// Throws if this class has been disposed. 2581 /// </summary> ThrowIfDisposed()2582 protected void ThrowIfDisposed() 2583 { 2584 if (_disposed) 2585 { 2586 throw new ObjectDisposedException(GetType().Name); 2587 } 2588 } 2589 2590 } 2591 } 2592