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