19
19
creating and managing user accounts in Firebase projects.
20
20
"""
21
21
22
+ import json
22
23
import time
23
24
24
25
from google .auth import jwt
@@ -163,6 +164,36 @@ def get_user_by_phone_number(phone_number, app=None):
163
164
except _user_mgt .ApiCallError as error :
164
165
raise AuthError (error .code , str (error ), error .detail )
165
166
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
+
166
197
167
198
def create_user (** kwargs ):
168
199
"""Creates a new user account with the specified properties.
@@ -195,11 +226,12 @@ def create_user(**kwargs):
195
226
raise AuthError (error .code , str (error ), error .detail )
196
227
197
228
198
- def update_user (uid , ** kwargs ): # pylint: disable=missing-param-doc
229
+ def update_user (uid , ** kwargs ):
199
230
"""Updates an existing user account with the specified properties.
200
231
201
232
Args:
202
233
uid: A user ID string.
234
+ kwargs: A series of keyword arguments (optional).
203
235
204
236
Keyword Args:
205
237
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
212
244
photo_url: The user's photo URL (optional). Can be removed by explicitly passing None.
213
245
password: The user's raw, unhashed password. (optional).
214
246
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).
216
249
217
250
Returns:
218
251
UserRecord: An updated UserRecord instance for the user.
@@ -229,6 +262,31 @@ def update_user(uid, **kwargs): # pylint: disable=missing-param-doc
229
262
except _user_mgt .ApiCallError as error :
230
263
raise AuthError (error .code , str (error ), error .detail )
231
264
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 )
232
290
233
291
def delete_user (uid , app = None ):
234
292
"""Deletes the user identified by the specified user ID.
@@ -393,6 +451,20 @@ def provider_data(self):
393
451
providers = self ._data .get ('providerUserInfo' , [])
394
452
return [_ProviderUserInfo (entry ) for entry in providers ]
395
453
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
+
396
468
397
469
class UserMetadata (object ):
398
470
"""Contains additional metadata associated with a user account."""
@@ -414,6 +486,85 @@ def last_sign_in_timestamp(self):
414
486
return int (self ._data ['lastLoginAt' ])
415
487
return None
416
488
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
+
417
568
418
569
class _ProviderUserInfo (UserInfo ):
419
570
"""Contains metadata regarding how a user is known by a particular identity provider."""
0 commit comments