morepath / more.jwtauth

JWT Authentication integration for Morepath
BSD 3-Clause "New" or "Revised" License
3 stars 3 forks source link

feat: refresh token API #7

Closed henri-hulski closed 7 years ago

henri-hulski commented 7 years ago

Closes #6.

henri-hulski commented 7 years ago

Refreshing the token

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:

When refreshing is enabled by setting refresh_delta the token gets 2 additional claims:

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.