1919creating and managing user accounts in Firebase projects.
2020"""
2121
22+ import json
2223import time
2324
2425from google .auth import jwt
@@ -163,6 +164,36 @@ def get_user_by_phone_number(phone_number, app=None):
163164 except _user_mgt .ApiCallError as error :
164165 raise AuthError (error .code , str (error ), error .detail )
165166
167+ def list_users (page_token = None , max_results = _user_mgt .MAX_LIST_USERS_RESULTS , app = None ):
168+ """Retrieves a page of user accounts from a Firebase project.
169+
170+ The ``page_token`` argument governs the starting point of the page. The ``max_results``
171+ argument governs the maximum number of user accounts that may be included in the returned page.
172+ This function never returns None. If there are no user accounts in the Firebase project, this
173+ returns an empty page.
174+
175+ Args:
176+ page_token: A non-empty page token string, which indicates the starting point of the page
177+ (optional). Defaults to ``None``, which will retrieve the first page of users.
178+ max_results: A positive integer indicating the maximum number of users to include in the
179+ returned page (optional). Defaults to 1000, which is also the maximum number allowed.
180+ app: An App instance (optional).
181+
182+ Returns:
183+ ListUsersPage: A ListUsersPage instance.
184+
185+ Raises:
186+ ValueError: If max_results or page_token are invalid.
187+ AuthError: If an error occurs while retrieving the user accounts.
188+ """
189+ user_manager = _get_auth_service (app ).user_manager
190+ def download (page_token , max_results ):
191+ try :
192+ return user_manager .list_users (page_token , max_results )
193+ except _user_mgt .ApiCallError as error :
194+ raise AuthError (error .code , str (error ), error .detail )
195+ return ListUsersPage (download , page_token , max_results )
196+
166197
167198def create_user (** kwargs ):
168199 """Creates a new user account with the specified properties.
@@ -195,11 +226,12 @@ def create_user(**kwargs):
195226 raise AuthError (error .code , str (error ), error .detail )
196227
197228
198- def update_user (uid , ** kwargs ): # pylint: disable=missing-param-doc
229+ def update_user (uid , ** kwargs ):
199230 """Updates an existing user account with the specified properties.
200231
201232 Args:
202233 uid: A user ID string.
234+ kwargs: A series of keyword arguments (optional).
203235
204236 Keyword Args:
205237 display_name: The user's display name (optional). Can be removed by explicitly passing
@@ -212,7 +244,8 @@ def update_user(uid, **kwargs): # pylint: disable=missing-param-doc
212244 photo_url: The user's photo URL (optional). Can be removed by explicitly passing None.
213245 password: The user's raw, unhashed password. (optional).
214246 disabled: A boolean indicating whether or not the user account is disabled (optional).
215- app: An App instance (optional).
247+ custom_claims: A dictionary or a JSON string contining the custom claims to be set on the
248+ user account (optional).
216249
217250 Returns:
218251 UserRecord: An updated UserRecord instance for the user.
@@ -229,6 +262,31 @@ def update_user(uid, **kwargs): # pylint: disable=missing-param-doc
229262 except _user_mgt .ApiCallError as error :
230263 raise AuthError (error .code , str (error ), error .detail )
231264
265+ def set_custom_user_claims (uid , custom_claims , app = None ):
266+ """Sets additional claims on an existing user account.
267+
268+ Custom claims set via this function can be used to define user roles and privilege levels.
269+ These claims propagate to all the devices where the user is already signed in (after token
270+ expiration or when token refresh is forced), and next time the user signs in. The claims
271+ can be accessed via the user's ID token JWT. If a reserved OIDC claim is specified (sub, iat,
272+ iss, etc), an error is thrown. Claims payload must also not be larger then 1000 characters
273+ when serialized into a JSON string.
274+
275+ Args:
276+ uid: A user ID string.
277+ custom_claims: A dictionary or a JSON string of custom claims. Pass None to unset any
278+ claims set previously.
279+ app: An App instance (optional).
280+
281+ Raises:
282+ ValueError: If the specified user ID or the custom claims are invalid.
283+ AuthError: If an error occurs while updating the user account.
284+ """
285+ user_manager = _get_auth_service (app ).user_manager
286+ try :
287+ user_manager .update_user (uid , custom_claims = custom_claims )
288+ except _user_mgt .ApiCallError as error :
289+ raise AuthError (error .code , str (error ), error .detail )
232290
233291def delete_user (uid , app = None ):
234292 """Deletes the user identified by the specified user ID.
@@ -393,6 +451,20 @@ def provider_data(self):
393451 providers = self ._data .get ('providerUserInfo' , [])
394452 return [_ProviderUserInfo (entry ) for entry in providers ]
395453
454+ @property
455+ def custom_claims (self ):
456+ """Returns any custom claims set on this user account.
457+
458+ Returns:
459+ dict: A dictionary of claims or None.
460+ """
461+ claims = self ._data .get ('customAttributes' )
462+ if claims :
463+ parsed = json .loads (claims )
464+ if parsed != {}:
465+ return parsed
466+ return None
467+
396468
397469class UserMetadata (object ):
398470 """Contains additional metadata associated with a user account."""
@@ -414,6 +486,85 @@ def last_sign_in_timestamp(self):
414486 return int (self ._data ['lastLoginAt' ])
415487 return None
416488
489+ class ExportedUserRecord (UserRecord ):
490+ """Contains metadata associated with a user including password hash and salt."""
491+
492+ def __init__ (self , data ):
493+ super (ExportedUserRecord , self ).__init__ (data )
494+
495+ @property
496+ def password_hash (self ):
497+ """The user's password hash as a base64-encoded string.
498+
499+ If the Firebase Auth hashing algorithm (SCRYPT) was used to create the user account, this
500+ will be the base64-encoded password hash of the user. If a different hashing algorithm was
501+ used to create this user, as is typical when migrating from another Auth system, this
502+ will be an empty string. If no password is set, this will be None.
503+ """
504+ return self ._data .get ('passwordHash' )
505+
506+ @property
507+ def password_salt (self ):
508+ """The user's password salt as a base64-encoded string.
509+
510+ If the Firebase Auth hashing algorithm (SCRYPT) was used to create the user account, this
511+ will be the base64-encoded password salt of the user. If a different hashing algorithm was
512+ used to create this user, as is typical when migrating from another Auth system, this will
513+ be an empty string. If no password is set, this will be None.
514+ """
515+ return self ._data .get ('salt' )
516+
517+
518+ class ListUsersPage (object ):
519+ """Represents a page of user records exported from a Firebase project.
520+
521+ Provides methods for traversing the user accounts included in this page, as well as retrieving
522+ subsequent pages of users. The iterator returned by ``iterate_all()`` can be used to iterate
523+ through all users in the Firebase project starting from this page.
524+ """
525+
526+ def __init__ (self , download , page_token , max_results ):
527+ self ._download = download
528+ self ._max_results = max_results
529+ self ._current = download (page_token , max_results )
530+
531+ @property
532+ def users (self ):
533+ """A list of ``ExportedUserRecord`` instances available in this page."""
534+ return [ExportedUserRecord (user ) for user in self ._current .get ('users' , [])]
535+
536+ @property
537+ def next_page_token (self ):
538+ """Page token string for the next page (empty string indicates no more pages)."""
539+ return self ._current .get ('nextPageToken' , '' )
540+
541+ @property
542+ def has_next_page (self ):
543+ """A boolean indicating whether more pages are available."""
544+ return bool (self .next_page_token )
545+
546+ def get_next_page (self ):
547+ """Retrieves the next page of user accounts, if available.
548+
549+ Returns:
550+ ListUsersPage: Next page of users, or None if this is the last page.
551+ """
552+ if self .has_next_page :
553+ return ListUsersPage (self ._download , self .next_page_token , self ._max_results )
554+ return None
555+
556+ def iterate_all (self ):
557+ """Retrieves an iterator for user accounts.
558+
559+ Returned iterator will iterate through all the user accounts in the Firebase project
560+ starting from this page. The iterator will never buffer more than one page of users
561+ in memory at a time.
562+
563+ Returns:
564+ iterator: An iterator of ExportedUserRecord instances.
565+ """
566+ return _user_mgt .UserIterator (self )
567+
417568
418569class _ProviderUserInfo (UserInfo ):
419570 """Contains metadata regarding how a user is known by a particular identity provider."""
0 commit comments