Skip to content

Commit 13301b2

Browse files
committed
Update documentation for token blacklisting
1 parent 95e5cf9 commit 13301b2

File tree

3 files changed

+39
-34
lines changed

3 files changed

+39
-34
lines changed

docs/blacklist_and_token_revoking.rst

+33-29
Original file line numberDiff line numberDiff line change
@@ -3,35 +3,39 @@ Blacklist and Token Revoking
33

44
This extension supports optional token revoking out of the box. This will
55
allow you to revoke a specific token so that it can no longer access your endpoints.
6-
In order to revoke a token, we need some storage where we can save a list of all
7-
the tokens we have created, as well as if they have been revoked or not. In order
8-
to make the underlying storage as agnostic as possible, we use `simplekv
9-
<http://pythonhosted.org/simplekv/>`_ to provide assess to a variety of backends.
10-
11-
In production, it is important to use a backend that can have some sort of
12-
persistent storage, so we don't 'forget' that we revoked a token if the flask
13-
process is restarted. We also need something that can be safely used by the
14-
multiple thread and processes running your application. At present we believe
15-
redis is a good fit for this. It has the added benefit of removing expired tokens
16-
from the store automatically, so it wont blow up into something huge.
17-
18-
We also have to choose what tokens we want to check against the blacklist. We could
19-
check all tokens (refresh and access), or only the refresh tokens. There are pros
20-
and cons to either way, namely extra overhead on jwt_required endpoints vs someone
21-
being able to use an access token freely until it expires. In this example, we are
22-
looking at all tokens:
6+
7+
You will have to choose what tokens you want to check against the blacklist. In
8+
most cases, you will probably want to check both refresh and access tokens, which
9+
is the default behavior. However, if the extra overhead of checking tokens is a
10+
concern you could instead only check the refresh tokens, and set the access
11+
tokens to have a short expires time so any damage a compromised token could
12+
cause is minimal.
13+
14+
Blacklisting works by is providing a callback function to this extension, using the
15+
**@jwt.token_in_blacklist_loader** decorator. This method will be called whenever the
16+
specified tokens (``'access'`` and/or ``'refresh'``) are used to access a protected endpoint.
17+
If the callback function says that the token is revoked, we will not allow the
18+
call to continue, otherwise we will allow the call to access the endpoint as normal.
19+
20+
21+
Here is a basic example of this in action.
22+
2323

2424
.. literalinclude:: ../examples/blacklist.py
2525

26-
If you want better performance (ie, not having to check the blacklist store
27-
with every request), you could check only the refresh tokens. This makes it
28-
so any call to a jwt_required endpoint does not need to check the blacklist
29-
store, but on the flip side would allow a compromised access token to be used
30-
until it expired. If using the approach, you should set the access tokens to
31-
have a very short lifetime to help combat this.
32-
33-
It's worth noting that if your selected backend support the `time to live mixin
34-
<http://pythonhosted.org/simplekv/#simplekv.TimeToLiveMixin>`_ (such as redis),
35-
keys will be automatically deleted from the store at some point after they have
36-
expired. This prevents your store from blowing up with old keys without you having
37-
to do any work to prune it back down.
26+
In production, you will likely want to use either a database or in memory store
27+
(such as redis) to store your tokens. In memory stores are great if you are wanting
28+
to revoke a token when the users logs out, as they are blazing fast. A downside
29+
to using redis is that in the case of a power outage or other such event, it's
30+
possible that you might 'forget' that some tokens have been revoked, depending
31+
on if the redis data was synced to disk.
32+
33+
In contrast to that, databases are great if the data persistance is of the highest
34+
importance (for example, if you have very long lived tokens that other developers
35+
use to access your api), or if you want to add some addition features like showing
36+
users all of their active tokens, and letting them revoke and unrevoke those tokens.
37+
38+
For more in depth examples of these, check out:
39+
40+
- https://github.com/vimalloc/flask-jwt-extended/examples/redis_blacklist.py
41+
- https://github.com/vimalloc/flask-jwt-extended/examples/database_blacklist

docs/options.rst

+3-5
Original file line numberDiff line numberDiff line change
@@ -111,10 +111,8 @@ Blacklist Options:
111111

112112
================================= =========================================
113113
``JWT_BLACKLIST_ENABLED`` Enable/disable token blacklisting and revoking. Defaults to ``False``
114-
``JWT_BLACKLIST_STORE`` Where to save created and revoked tokens. `See here
115-
<http://pythonhosted.org/simplekv/>`_ for options.
116-
Only used if blacklisting is enabled.
117-
``JWT_BLACKLIST_TOKEN_CHECKS`` What token types to check against the blacklist. Options are
118-
``'refresh'`` or ``'all'``. Defaults to ``'refresh'``.
114+
``JWT_BLACKLIST_TOKEN_CHECKS`` What token types to check against the blacklist. The options are
115+
``'refresh'`` or ``'access'``. You can pass in a list to check
116+
more then one type. Defaults to ``['access', 'refresh']``.
119117
Only used if blacklisting is enabled.
120118
================================= =========================================

examples/blacklist.py

+3
Original file line numberDiff line numberDiff line change
@@ -44,6 +44,7 @@ def check_if_token_in_blacklist(decrypted_token):
4444
return jti in blacklist
4545

4646

47+
# Standard login endpoint
4748
@app.route('/login', methods=['POST'])
4849
def login():
4950
username = request.json.get('username', None)
@@ -58,6 +59,8 @@ def login():
5859
return jsonify(ret), 200
5960

6061

62+
# Standard refresh endpoint. A blacklisted refresh token
63+
# will not be able to access this endpoint
6164
@app.route('/refresh', methods=['POST'])
6265
@jwt_refresh_token_required
6366
def refresh():

0 commit comments

Comments
 (0)