Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
10 changes: 10 additions & 0 deletions docs/blacklist_and_token_revoking.rst
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,16 @@ Here is a basic example of this in action.

.. literalinclude:: ../examples/blacklist.py

Sometimes there will be situations where we would want to backlist both the access token and the refresh token in the logout call. It could be done by
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

... to blacklist both ...

embedding the reference to the refresh token in the access token. For that
we could write the login, logout and refresh functions as:


.. literalinclude:: ../examples/backlist_both_tokens.py
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

blacklist_both_tokens.py



Using databases for Blacklisting
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
In production, you will likely want to use either a database or in memory store
(such as redis) to store your tokens. In memory stores are great if you are wanting
to revoke a token when the users logs out, as they are blazing fast. A downside
Expand Down
77 changes: 77 additions & 0 deletions examples/backlist_both_tokens.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,77 @@
from flask import Flask, request, jsonify

from flask_jwt_extended import (
jwt_refresh_token_required, jwt_required, create_access_token, get_jti,
create_refresh_token, get_jwt_identity, get_raw_jwt, JWTManager
)


app = Flask(__name__)

# Enable blacklisting and specify what kind of tokens to check
# against the blacklist
app.config['JWT_SECRET_KEY'] = 'super-secret' # Change this!
app.config['JWT_BLACKLIST_ENABLED'] = True
app.config['JWT_BLACKLIST_TOKEN_CHECKS'] = ['access', 'refresh']
jwt = JWTManager(app)

blacklist = set()


@jwt.token_in_blacklist_loader
def check_if_token_in_blacklist(decrypted_token):
jti = decrypted_token['jti']
return jti in blacklist


# Endpoint for logging in
@app.route('/login', methods=['POST'])
def login():
username = request.json.get('username', None)
password = request.json.get('password', None)
if username != 'test' or password != 'test':
return jsonify({"msg": "Bad username or password"}), 401

# use a dict for identity, so that we can link the access token with
# the refresh token as a user claim
user = {"username": username}
refresh_token = create_refresh_token(identity=user)

# Embed the refresh token's jti in the access_token
user["refresh_jti"] = get_jti(refresh_token)
Copy link
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I don't really like having having this data stored in what will become the current_user for the JWT. It complicates things, especially when using the complex objects from token and vice versa features in this extension. Could we use the @jwt.user_claims_loader instead to put the data into the JWT, then access it via get_jwt_claims() instead of current_user bellow?

access_token = create_access_token(identity=user)

ret = {
'access_token': access_token,
'refresh_token': refresh_token
}
return jsonify(ret), 200


# Endpoint for generating a new access token using refresh token
@app.route('/refresh', methods=['POST'])
@jwt_refresh_token_required
def refresh():
current_user = get_jwt_identity()
refresh_jti = get_raw_jwt()['jti']
current_user['refresh_jti'] = refresh_jti
Copy link
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Same thing here regarding the user_claims_loader

ret = {
'access_token': create_access_token(identity=current_user)
}
return jsonify(ret), 200


# Endpoint which blacklists both the refresh token and the access token with a
# single call
@app.route('/logout', methods=['DELETE'])
@jwt_required
def logout():
access_jti = get_raw_jwt()["jti"]
refresh_jti = get_jwt_identity()["refresh_jti"]
blacklist.add(access_jti)
blacklist.add(refresh_jti)
return jsonify({"msg": "Successfully logged out"}), 200


if __name__ == "__main__":
app.run()