Closed henri-hulski closed 7 years ago
There are some risks related with using long-term tokens:
A solution is to use short-term tokens and refresh them either just before
they expire or even after until the refresh_until
claim not expires.
To help you with this more.jwtauth has a refresh API, which uses 4 settings:
allow_refresh
: Enables the token refresh API when True.
Default is Falserefresh_delta
: The time delta in which the token can be refreshed
considering the leeway.
Default is 7 days. When None you can always refresh the token.refresh_nonce_handler
: Dotted path to callback function, which receives
the userid as argument and returns a nonce which will be validated before
refreshing. When None no nonce will be created or validated for refreshing.verify_expiration_on_refresh
: If False, expiration_delta for the JWT
token will not be checked during refresh. Otherwise you can refresh the
token only if it's not yet expired. Default is False.When refreshing is enabled by setting refresh_delta
the token gets
2 additional claims:
refresh_until
: Timestamp until which the token can be refreshed.nonce
: The nonce which was generated by refresh_nonce_handler
.So when you want to refresh your token, either because it has expires or
just before, if you set the verify_expiration_on_refresh
setting to True,
you send a request to the refresh end-point:
class Refresh(object):
pass
@App.path(model=Refresh, path='refresh')
def get_refresh():
return Refresh()
@App.view(model=Refresh)
def refresh(self, request):
try:
# Verifies if we're allowed to refresh the token.
# In this case returns the userid.
# If not raises exceptions based on InvalidTokenError.
# If expired this is a ExpiredSignatureError.
userid = verify_refresh_request(request)
except ExpiredSignatureError:
@request.after
def expired_nonce_or_token(response):
response.status_code = 403
return {'validationError': 'Your session has expired'}
except InvalidTokenError:
@request.after
def invalid_token(response):
response.status_code = 403
return {'validationError': 'Could not refresh your token'}
else:
# get user info from the database to update the claims
user = User.get[userid]
@request.after
def remember(response):
# create the identity with the userid and updated user info
identity = morepath.Identity(
email, nickname=user.nickname, role=user.role
)
# create the updated token and set it in the response header
request.app.remember_identity(response, request, identity)
return "Token sucessfully refreshed."
So now on every token refresh the user data gets updated.
When you use the refresh_nonce_handler like this example:
def refresh_nonce_handler(userid):
# This returns a nonce from the user endity
# which can just be an UUID you created before.
return User[userid].nonce
If the token gets compromised you can just change the nonce by storing a new UUID in the user endity and the existing tokens will not be refreshed anymore.
Closes #6.