Token Revokation
For token without expiry date, or even revoked token, the standard JWT validation will pass. Hence the need to check if the token provided in request is revoked. Usually we confront the token with a blocklist of non-expired revoked token.
FastJWT enables this revoked token check by using a custom callback system.
Setup
Define and assign the callback
First we need to create a function with a first token
str positional argument.
This function should return True
is the token is considered revoked, False
otherwise.
FastJWT provides a FastJWT.set_callback_token_blocklist
decorator to assign a custom callback for revoked token validation.
Once a callback is assigned with FastJWT.set_callback_token_blocklist
, every time a valid token is required, the user defined callback is executed to check if the token is revoked.
We define the is_token_revoked
callback as a function taking token
as a main str positional argument and returning a bool
TYPE Callable[[str, ParamSpecKwargs], bool]
or (str) -> bool
- You can set callbacks with the
FastJWT
decorator syntax, but the following method call would also work
Setting Callback syntax
You can set callbacks with the FastJWT
decorator syntax, but the following method call would also work
Feature Proposal - Decorator Naming
The verbosity of FastJWT.set_callback_token_blocklist
might encourage us to add shorter aliases in next releases
Setting the dependencies
To avoid code repetition we define callbacks as follow. This step is not necessary but allows for cleaner code.
We use the FastJWT.get_token_from_request
to create a dependency that returns a fastjwt.RequestToken
instance.
RequestToken
describe the JWT information contained in request. It acts as a dataclass containing a str token
, a type
parameter to declare the type of token expected, a location
to define where to token is located in request and a csrf
double submit token if necessary.
We created 2 dependencies, get_token_dep
& get_optional_token_dep
, the main difference is based on the optional
argument.
get_optional_token_dep
return type is Optional[RequestToken]
, meaning the execution will continue even if no token is available in request.
get_token_dep
return type is RequestToken
, meaning the dependency will enforce token availability in request, and raise a MissingTokenError
otherwise.
Note
The FastJWT.get_token_from_request
dependency does not check the token validity, it only returns the token
if found in request. If no token appears in the request the dependency will return a None
value.
If you need to ensure a token is available use this dependency in conjunction with the FastJWT.token_required
dependency
To showcase differences from the previously defined dependencies we coded 2 routes with a dummy principle, return the token and its location.
The /token/mandatory
endpoint depends on both get_token_dep
as a function dependency to retrieve the token and FastJWT.access_token_required
as a route dependency to enforce JWT validation.
The /token/optional
endpoint does not require a token to be passed in request, and even if a token is passed it does not need to be a valid one.
Revoke a token
In our example when the user request the DELETE /logout
endpoint, we log out the user by adding the token in a blocklist. The same blocklist used to check for revoked tokens.
Revoking Access & Refresh
If during login you generated a couple of tokens as one access
and one refresh
token, ensure that both tokens are revoked.
Since a user can use a refresh
token to generate new access
tokens, revoking only the access
token is not enough to log out the user.
Note
Again, even if we require /logout
to have a token in request thanks to the get_token_dep
, we also need to ensure this token is valid and therefore we also add the FastJWT.access_token_required
dependency.
- The
500 Internal Server Error
HTTP Error is the expected behavior because no error handling has been done
# Endpoint with optional=True
$ curl -s http://0.0.0.0:8000/token/optional
"No token found"
# Endpoint with optional=False
$ curl -s http://0.0.0.0:8000/token/mandatory
Internal Server Error # (1)!
- The
500 Internal Server Error
HTTP Error is the expected behavior because no error handling has been done
# A genuine token is provided
$ curl -s --oauth2-bearer $TOKEN http://0.0.0.0:8000/profile
"You are authenticated"
# A revoked token is provided
$ curl -s --oauth2-bearer $REVOKED_TOKEN http://0.0.0.0:8000/profile
Internal Server Error # (1)!
- The
500 Internal Server Error
HTTP Error is the expected behavior because no error handling has been done
# /token/optional returns a 200 for any tokens because no validation is required
$ curl -s --oauth2-bearer $TOKEN http://0.0.0.0:8000/token/optional
"Your token is: $TOKEN and is located in headers"
$ curl -s --oauth2-bearer $REVOKED_TOKEN http://0.0.0.0:8000/token/optional
"Your token is: $REVOKED_TOKEN and is located in headers"
# /token/mandatory applies a validation step
$ curl -s --oauth2-bearer $TOKEN http://0.0.0.0:8000/token/mandatory
"Your token is: $TOKEN and is located in headers"
$ curl -s --oauth2-bearer $REVOKED_TOKEN http://0.0.0.0:8000/token/mandatory
Internal Server Error # (1)!
- The
500 Internal Server Error
HTTP Error is the expected behavior because no error handling has been done
With a database (sqlalchemy)
WIP
This section is work in progress