Revoking Tokens#

Note

This functionality is introduced in version 0.3.0.

Sometimes, you want to revoke a token. As João Angelo wrote:

In general the easiest answer would be to say that you cannot revoke a JWT token, but that’s simply not true. The honest answer is that the cost of supporting JWT revocation is sufficiently big for not being worth most of the times….

There are plenty of times that you want to revoke a token. However, you can’t just reach into the person’s browser and remove it, though. You have to indicate to the browser (or whatever client) that the token is no longer good.

If expiration is not good enough, and you need “immediate token revocation” as it’s called, then the Authenticator has the functionality for you.

Each token generated by an Authenticator has the "jti" claim in it. This allows you to “track” the issued JWT and, if needed, invalidate it.

To invalidate a token, you’ll need to store the identifier somewhere so that you can tell if it’s good or not. This is essentially a session-tracking mechanism.

Here’s an example of the two-step process for how to do this with the jwtdown-fastapi.

Override jti methods#

There are there are three methods to override:

  • get_session_getter which uses FastAPI’s dependency injection mechanism to get an instance of the thing you use to manage session data

  • jti_created which alerts you to when a new JWT is created and there is a new "jti" claim which will happen during the login process

  • jti_destroyed which alerts you to when a JWT has expired and the "jti" claim is no longer any good, or during the logout process

  • validate_jti will get called whenever someone tries to access a protected endpoint and, if it returns False, then the person will not be allowed to access that protected endpoint

# authenticator.py
import os
from fastapi import Depends
from jwtdown_fastapi.authentication import Authenticator
from queries.accounts import AccountRepo, AccountOut, Account
from queries.sessions import SessionRepo


class MyAuthenticator(Authenticator):
    # Other overridden methods

    def get_session_getter(self, session_repo: SessionRepo = Depends()):
        return session_repo

    async def jti_created(self, jti, account, session_repo):
        await session_repo.create(jti, account)

    async def jti_destroyed(self, jti, session_repo):
        await session_repo.delete(jti)

    async def validate_jti(self, jti, session_repo):
        return session_repo.get(jti) is not None


authenticator = MyAuthenticator(os.environ["SIGNING_KEY"])

Implement a revocation endpoint#

In your application, you will need a way to revoke a valid JWT. You can create an endpoint that lets you do that.

# router.py

@router.delete("/api/sessions/{account_id}", response_model=bool)
async def delete_session(
    account_id: str,
    account_data: dict = Depends(authenticator.get_current_account_data),
    session_repo: SessionRepo = Depends(),
) -> bool:
    if "admin" in account_data["roles"]:
        session_repo.delete_sessions(account_id)
        return True
    return False