Skip to content

Commit 31b7239

Browse files
committed
add last_refresh_timestamp
1 parent 18f4b41 commit 31b7239

File tree

3 files changed

+43
-4
lines changed

3 files changed

+43
-4
lines changed

firebase_admin/_user_mgt.py

Lines changed: 21 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,7 @@
1919
from urllib import parse
2020

2121
import requests
22+
import iso8601
2223

2324
from firebase_admin import _auth_utils
2425
from firebase_admin import _identifier
@@ -42,11 +43,14 @@ def __init__(self, description):
4243
class UserMetadata:
4344
"""Contains additional metadata associated with a user account."""
4445

45-
def __init__(self, creation_timestamp=None, last_sign_in_timestamp=None):
46+
def __init__(self, creation_timestamp=None, last_sign_in_timestamp=None,
47+
last_refresh_timestamp=None):
4648
self._creation_timestamp = _auth_utils.validate_timestamp(
4749
creation_timestamp, 'creation_timestamp')
4850
self._last_sign_in_timestamp = _auth_utils.validate_timestamp(
4951
last_sign_in_timestamp, 'last_sign_in_timestamp')
52+
self._last_refresh_timestamp = _auth_utils.validate_timestamp(
53+
last_refresh_timestamp, 'last_refresh_timestamp')
5054

5155
@property
5256
def creation_timestamp(self):
@@ -66,6 +70,16 @@ def last_sign_in_timestamp(self):
6670
"""
6771
return self._last_sign_in_timestamp
6872

73+
@property
74+
def last_refresh_timestamp(self):
75+
"""The time at which the user was last active (ID token refreshed).
76+
77+
Returns:
78+
integer: Milliseconds since epoch timestamp, or None if the user was
79+
never active.
80+
"""
81+
return self._last_refresh_timestamp
82+
6983

7084
class UserInfo:
7185
"""A collection of standard profile information for a user.
@@ -217,7 +231,12 @@ def _int_or_none(key):
217231
if key in self._data:
218232
return int(self._data[key])
219233
return None
220-
return UserMetadata(_int_or_none('createdAt'), _int_or_none('lastLoginAt'))
234+
last_refresh_at_millis = None
235+
last_refresh_at_iso8601 = self._data.get('lastRefreshAt', None)
236+
if last_refresh_at_iso8601 is not None:
237+
last_refresh_at_millis = iso8601.parse_date(last_refresh_at_iso8601).timestamp() * 1000
238+
return UserMetadata(
239+
_int_or_none('createdAt'), _int_or_none('lastLoginAt'), last_refresh_at_millis)
221240

222241
@property
223242
def provider_data(self):

integration/test_auth.py

Lines changed: 21 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -46,7 +46,7 @@ def _sign_in(custom_token, api_key):
4646
return resp.json().get('idToken')
4747

4848
def _sign_in_with_password(email, password, api_key):
49-
body = {'email': email, 'password': password}
49+
body = {'email': email, 'password': password, 'returnSecureToken': True}
5050
params = {'key' : api_key}
5151
resp = requests.request('post', _verify_password_url, params=params, json=body)
5252
resp.raise_for_status()
@@ -162,7 +162,7 @@ def new_user():
162162
auth.delete_user(user.uid)
163163

164164
@pytest.fixture
165-
def new_user_with_params():
165+
def new_user_with_params() -> auth.UserRecord:
166166
random_id, email = _random_id()
167167
phone = _random_phone()
168168
user = auth.create_user(
@@ -281,6 +281,25 @@ def test_de_dups_duplicate_users(self):
281281
users = map(self._map_user_record_to_uid_email_phones, users)
282282
assert list(users) == [self.test_user1]
283283

284+
def test_last_refresh_timestamp(new_user_with_params: auth.UserRecord, api_key):
285+
# new users should not have a last_refresh_timestamp set
286+
assert new_user_with_params.user_metadata.last_refresh_timestamp is None
287+
288+
# login to cause the last_refresh_timestamp to be set
289+
_sign_in_with_password(new_user_with_params.email, 'secret', api_key)
290+
new_user_with_params = auth.get_user(new_user_with_params.uid)
291+
292+
# Ensure the last refresh time occurred at approximately 'now'. (With a
293+
# tolerance of up to 1 minute; we ideally want to ensure that any timezone
294+
# considerations are handled properly, so as long as we're within an hour,
295+
# we're in good shape.)
296+
millis_per_second = 1000
297+
seconds_per_minute = 60
298+
millis_per_minute = millis_per_second * seconds_per_minute
299+
300+
last_refresh_timestamp = new_user_with_params.user_metadata.last_refresh_timestamp
301+
assert last_refresh_timestamp == pytest.approx(
302+
time.time()*millis_per_second, 1*millis_per_minute)
284303

285304
def test_list_users(new_user_list):
286305
err_msg_template = (

requirements.txt

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -8,3 +8,4 @@ google-api-core[grpc] >= 1.14.0, < 2.0.0dev; platform.python_implementation != '
88
google-api-python-client >= 1.7.8
99
google-cloud-firestore >= 1.4.0; platform.python_implementation != 'PyPy'
1010
google-cloud-storage >= 1.18.0
11+
iso8601 >= 0.1.12

0 commit comments

Comments
 (0)