From b0af5c5ae4b06681f0fc4b1550c8ed25f540b2a3 Mon Sep 17 00:00:00 2001 From: Arunmozhi Date: Sun, 26 Aug 2018 20:54:38 +0530 Subject: [PATCH 1/3] added example and docs for revoking both tokens --- docs/blacklist_and_token_revoking.rst | 10 ++++++ examples/backlist_both_tokens.py | 47 +++++++++++++++++++++++++++ 2 files changed, 57 insertions(+) create mode 100644 examples/backlist_both_tokens.py diff --git a/docs/blacklist_and_token_revoking.rst b/docs/blacklist_and_token_revoking.rst index a0b38c82..ffef5c89 100644 --- a/docs/blacklist_and_token_revoking.rst +++ b/docs/blacklist_and_token_revoking.rst @@ -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 +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 + + +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 diff --git a/examples/backlist_both_tokens.py b/examples/backlist_both_tokens.py new file mode 100644 index 00000000..b0dabf11 --- /dev/null +++ b/examples/backlist_both_tokens.py @@ -0,0 +1,47 @@ +from flask_jwt_extended import decode_token + +# Endpoint for loggin 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 + + user = { id: 1, username: 'test' } + refresh_token = create_refresh_token(identity=user) + + # Embed the refresh token's jti in the access_token + user["refresh_jti"] = decode_token(refresh_token)["jti"] + 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 + ret = { + 'access_token': create_access_token(identity=current_user) + } + return jsonify(ret), 200 + + +# Endpoint which backlists 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_raw_jwt()["identity"]["refresh_jti"] + backlist.add(access_jti) + backlist.add(refresh_jti) + return jsonify({"msg": "Successfully logged out"}), 200 \ No newline at end of file From faa1e8f652ff87ffc319ba8f840f18e41c5979db Mon Sep 17 00:00:00 2001 From: Arunmozhi Date: Sun, 26 Aug 2018 21:00:12 +0530 Subject: [PATCH 2/3] pep8 lint fixes --- examples/backlist_both_tokens.py | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/examples/backlist_both_tokens.py b/examples/backlist_both_tokens.py index b0dabf11..f73e4c2a 100644 --- a/examples/backlist_both_tokens.py +++ b/examples/backlist_both_tokens.py @@ -1,5 +1,6 @@ from flask_jwt_extended import decode_token + # Endpoint for loggin in @app.route('/login', methods=['POST']) def login(): @@ -7,8 +8,8 @@ def login(): password = request.json.get('password', None) if username != 'test' or password != 'test': return jsonify({"msg": "Bad username or password"}), 401 - - user = { id: 1, username: 'test' } + + user = {id: 1, username: 'test'} refresh_token = create_refresh_token(identity=user) # Embed the refresh token's jti in the access_token @@ -44,4 +45,4 @@ def logout(): refresh_jti = get_raw_jwt()["identity"]["refresh_jti"] backlist.add(access_jti) backlist.add(refresh_jti) - return jsonify({"msg": "Successfully logged out"}), 200 \ No newline at end of file + return jsonify({"msg": "Successfully logged out"}), 200 From fa51fa845d26a2aa8da2a905c8d222864237e14a Mon Sep 17 00:00:00 2001 From: Arunmozhi Date: Tue, 1 Jan 2019 22:13:08 +1100 Subject: [PATCH 3/3] made the example fully runnable --- examples/backlist_both_tokens.py | 45 ++++++++++++++++++++++++++------ 1 file changed, 37 insertions(+), 8 deletions(-) diff --git a/examples/backlist_both_tokens.py b/examples/backlist_both_tokens.py index f73e4c2a..83d5110c 100644 --- a/examples/backlist_both_tokens.py +++ b/examples/backlist_both_tokens.py @@ -1,7 +1,30 @@ -from flask_jwt_extended import decode_token +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 +) -# Endpoint for loggin in + +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) @@ -9,11 +32,13 @@ def login(): if username != 'test' or password != 'test': return jsonify({"msg": "Bad username or password"}), 401 - user = {id: 1, username: 'test'} + # 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"] = decode_token(refresh_token)["jti"] + user["refresh_jti"] = get_jti(refresh_token) access_token = create_access_token(identity=user) ret = { @@ -36,13 +61,17 @@ def refresh(): return jsonify(ret), 200 -# Endpoint which backlists both the refresh token and the access token with a +# 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_raw_jwt()["identity"]["refresh_jti"] - backlist.add(access_jti) - backlist.add(refresh_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()