Skip to content

Revoke refresh token #123

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 29 commits into from
Feb 13, 2018
Merged

Revoke refresh token #123

merged 29 commits into from
Feb 13, 2018

Conversation

avishalom
Copy link
Contributor

Adding the revokeRefreshToken,
and the ability to check revoked when validating a token.

@avishalom avishalom requested a review from hiranya911 January 29, 2018 17:21
@avishalom avishalom assigned avishalom and hiranya911 and unassigned avishalom Jan 29, 2018
Copy link
Contributor

@hiranya911 hiranya911 left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Looks mostly good. Just a number of nits to address.

CHANGELOG.md Outdated
@@ -1,11 +1,18 @@
# Unreleased
-
### Token revokaction
- [added] The ['verify_id_token(...)'](https://firebase.google.com/docs/reference/admin/python/firebase_admin.auth#verify_id_token)
method can now take an additional parameter `check_revoked`, when `True`,
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

...method now accepts an optional check_revoked argument. When set to True an additional check is performed to see...

@@ -113,6 +113,20 @@ def validate_photo_url(cls, photo_url):
except Exception:
raise ValueError('Malformed photo URL: "{0}".'.format(photo_url))

@classmethod
def validate_tokens_valid_after_time(cls, valid_since):
if not isinstance(valid_since, six.string_types) or not valid_since:
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

How come this is a string?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

the other timestamps in the UserMetadata are also returned as quoted strings. (passwordUpdate is a float in the response, but all other timestamps are quoted.)

try:
int(valid_since)
except ValueError:
raise ValueError('Malformed Tokens Valid After Time: "{0}" '
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

"Invalid tokens_valid_after_time value: {0}" should suffice here

@@ -85,17 +85,27 @@ def verify_id_token(id_token, app=None):
Args:
id_token: A string of the encoded JWT.
app: An App instance (optional).
check_revoked: check whether the token was revoked(optional).
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

A boolean indicating whether the token has been revoked (optional).


Returns:
dict: A dictionary of key-value pairs parsed from the decoded JWT.

Raises:
ValueError: If the JWT was found to be invalid, or if the App was not
initialized with a credentials.Certificate.
AuthError: If check_token is requested and the token was revoked.
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

If check_revoked is requested...

int: Timestap in seconds since the epoch. All tokens issued before that time
are considered revoked.
"""
return int(self._data.get('validSince', 0))
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We seem to return 0 by default.

  1. Is that consistent with the behavior in Node SDK (perhaps None is more appropriate here)?
  2. We should document this in the docstring.

ids=list(invalid_tokens))
@pytest.mark.parametrize('id_token', valid_tokens.values(), ids=list(valid_tokens))
def test_valid_token_check_revoked(self, user_mgt_app_for_verify_token, id_token):
_instrument_user_manager(user_mgt_app_for_verify_token, 200, MOCK_GET_USER_RESPONSE)
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Also need a test case for when the UserRecord contains a future timestamp.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

this is MOCK_GET_USER_REVOKED_TOKENS_RESPONSE

auth.verify_id_token(id_token, app=user_mgt_app_for_verify_token,
check_revoked=True)

assert excinfo.value.code == 'ID_TOKEN_REVOKED'
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Check against the constant once you have it

assert claims['admin'] is True
assert claims['uid'] == claims['sub']

def test_revoke_refresh_token(self, user_mgt_app):
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Add test case for setting this via update_user()

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

the revoke in turn calls update_user, do we want to explicitly do this with update user?

@@ -287,6 +326,13 @@ def user_mgt_app():
yield app
firebase_admin.delete_app(app)

@pytest.fixture(scope='module')
def user_mgt_app_for_verify_token():
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Can we just modify the existing user_mgt_app fixture to have the project_id option?

@hiranya911 hiranya911 assigned avishalom and unassigned hiranya911 Jan 29, 2018
@avishalom avishalom assigned hiranya911 and unassigned avishalom Feb 2, 2018
Copy link
Contributor

@hiranya911 hiranya911 left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Looks mostly good. Few more changes and we would be at an LGTM.

CHANGELOG.md Outdated
@@ -1,11 +1,19 @@
# Unreleased
-
### Token revokaction
- [added] The ['verify_id_token(...)'](https://firebase.google.com/docs/reference/admin/python/firebase_admin.auth#verify_id_token)
method now accepts an optional `check_revoked` parameter, when `True`, an
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

parameter. When True...

CHANGELOG.md Outdated
method now accepts an optional `check_revoked` parameter, when `True`, an
additional check is performed to see if the token has been revoked.
- [added] A new method ['auth.revoke_refresh_tokens(uid)'](https://firebase.google.com/docs/reference/admin/python/firebase_admin.auth#revoke_refresh_tokens)
has been added to invalidate all tokens issued before the current second.
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

...issued to a user.

CHANGELOG.md Outdated
- [added] A new method ['auth.revoke_refresh_tokens(uid)'](https://firebase.google.com/docs/reference/admin/python/firebase_admin.auth#revoke_refresh_tokens)
has been added to invalidate all tokens issued before the current second.
- [added] A new property `tokens_valid_after_time` has been added to the ['UserRecord'](https://firebase.google.com/docs/reference/admin/python/firebase_admin.auth#userrecord),
it denotes the time in epoch milliseconds before which tokens are not valid.
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

which denotes..

@@ -113,6 +113,20 @@ def validate_photo_url(cls, photo_url):
except Exception:
raise ValueError('Malformed photo URL: "{0}".'.format(photo_url))

@classmethod
def validate_valid_since(cls, valid_since):
if not isinstance(valid_since, six.string_types) or not valid_since:
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Why is this a string? It should just accept a timestamp.

@@ -85,17 +86,27 @@ def verify_id_token(id_token, app=None):
Args:
id_token: A string of the encoded JWT.
app: An App instance (optional).
check_revoked: A boolean indicating whether the token has been revoked (optional).
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

whether to check if the token has been...


def revoke_refresh_tokens(uid, app=None):
user_manager = _get_auth_service(app).user_manager
user_manager.update_user(uid, valid_since=str(int(time.time())))
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Why wrap this in str()? Just pass the int timestamp.

@@ -476,12 +497,16 @@ def __init__(self, data):

@property
def creation_timestamp(self):
""" Creation timestamp in epoch milliseconds.
"""
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Add the Returns: section too while we are at it?

@hiranya911 hiranya911 assigned avishalom and unassigned hiranya911 Feb 2, 2018
@avishalom avishalom assigned hiranya911 and unassigned avishalom Feb 5, 2018
Copy link
Contributor

@hiranya911 hiranya911 left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

LGTM with a few nits.

CHANGELOG.md Outdated
- [added] A new property `tokens_valid_after_time` has been added to the ['UserRecord'](https://firebase.google.com/docs/reference/admin/python/firebase_admin.auth#userrecord),
has been added to invalidate all tokens issued to a user before the current second.
- [added] A new property `tokens_valid_after_timestamp` has been added to the
['UserRecord'](https://firebase.google.com/docs/reference/admin/python/firebase_admin.auth#userrecord),
it denotes the time in epoch milliseconds before which tokens are not valid.
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

which denotes...

raise ValueError('Malformed Valid Since "{0}".'.format(valid_since))
'Invalid time string for: "{0}". Valid Since must be an int'.format(valid_since))
if int(valid_since) <= 0:
raise ValueError('Valid Since: must be a positive interger. {0}'.format(valid_since))
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Invalid valid_since: ... ?

While this revokes all sessions for a specified user and disables any new ID tokens for
existing sessions from getting minted, existing ID tokens may remain active until their
natural expiration (one hour). To verify that ID tokens are revoked, use
`verify_id_token(idToken, check_revoked=true)`.
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

check_revoked=True

@@ -49,7 +49,7 @@
"passwordUpdatedAt" : 1.494364393E+12,
"validSince" : "1494364393",
"disabled" : false,
"createdAt" : "1234567890",
"createdAt" : "1234567890000",
"customAttributes" : "{\"admin\": true, \"package\": \"gold\"}"
} ]
}
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Not really related to your change, but lets add a new line here.

@hiranya911 hiranya911 assigned avishalom and unassigned hiranya911 Feb 5, 2018
@avishalom avishalom merged commit a85865c into master Feb 13, 2018
@avishalom avishalom deleted the revoke-refresh-token branch February 13, 2018 01:37
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

2 participants