1- import datetime
2-
3- import simplekv .memory
41from flask import Flask , request , jsonify
52
6- from flask_jwt_extended import JWTManager , jwt_required , \
7- get_jwt_identity , revoke_token , unrevoke_token , \
8- get_stored_tokens , get_all_stored_tokens , create_access_token , \
9- create_refresh_token , jwt_refresh_token_required , \
10- get_raw_jwt , get_stored_token
3+ from flask_jwt_extended import (
4+ JWTManager , jwt_required , get_jwt_identity ,
5+ create_access_token , create_refresh_token ,
6+ jwt_refresh_token_required , get_raw_jwt
7+ )
118
129
1310# Setup flask
1411app = Flask (__name__ )
15- app .secret_key = 'super-secret '
12+ app .secret_key = 'ChangeMe! '
1613
17- # Enable and configure the JWT blacklist / token revoke. We are using
18- # an in memory store for this example. In production, you should
19- # use something persistent (such as redis, memcached, sqlalchemy).
20- # See here for options: http://pythonhosted.org/simplekv/
14+ # Enable blacklisting and specify what kind of tokens to check
15+ # against the blacklist
2116app .config ['JWT_BLACKLIST_ENABLED' ] = True
22- app .config ['JWT_BLACKLIST_STORE' ] = simplekv .memory .DictStore ()
23-
24- # Check all tokens (access and refresh) to see if they have been revoked.
25- # You can alternately check only the refresh tokens here, by setting this
26- # to 'refresh' instead of 'all'
27- app .config ['JWT_BLACKLIST_TOKEN_CHECKS' ] = 'all'
28- app .config ['JWT_ACCESS_TOKEN_EXPIRES' ] = datetime .timedelta (minutes = 5 )
29-
17+ app .config ['JWT_BLACKLIST_TOKEN_CHECKS' ] = ['access' , 'refresh' ]
3018jwt = JWTManager (app )
3119
20+ # A storage engine to save revoked tokens. In production if
21+ # speed is the primary concern, redis is a good bet. If data
22+ # persistence is more important for you, postgres is another
23+ # great option. In this example, we will be using an in memory
24+ # store, just to show you how this might work. For more
25+ # complete examples, check out these:
26+ # https://github.com/vimalloc/flask-jwt-extended/examples/redis_blacklist.py
27+ # https://github.com/vimalloc/flask-jwt-extended/examples/database_blacklist
28+ blacklist = set ()
29+
30+
31+ # For this example, we are just checking if the tokens jti
32+ # (unique identifier) is in the blacklist set. This could
33+ # be made more complex, for example storing all tokens
34+ # into the blacklist with a revoked status when created,
35+ # and returning the revoked status in this call. This
36+ # would allow you to have a list of all created tokens,
37+ # and to consider tokens that aren't in the blacklist
38+ # (aka tokens you didn't create) as revoked. These are
39+ # just two options, and this can be tailored to whatever
40+ # your application needs.
41+ @jwt .token_in_blacklist_loader
42+ def check_if_token_in_blacklist (decrypted_token ):
43+ jti = decrypted_token ['jti' ]
44+ return jti in blacklist
45+
3246
3347# Standard login endpoint
3448@app .route ('/login' , methods = ['POST' ])
@@ -45,7 +59,8 @@ def login():
4559 return jsonify (ret ), 200
4660
4761
48- # Standard refresh endpoint
62+ # Standard refresh endpoint. A blacklisted refresh token
63+ # will not be able to access this endpoint
4964@app .route ('/refresh' , methods = ['POST' ])
5065@jwt_refresh_token_required
5166def refresh ():
@@ -56,87 +71,26 @@ def refresh():
5671 return jsonify (ret ), 200
5772
5873
59- # Helper method to revoke the current token used to access
60- # a protected endpoint
61- def _revoke_current_token ():
62- current_token = get_raw_jwt ()
63- jti = current_token ['jti' ]
64- revoke_token (jti )
65-
66-
6774# Endpoint for revoking the current users access token
68- @app .route ('/logout' , methods = ['POST ' ])
75+ @app .route ('/logout' , methods = ['DELETE ' ])
6976@jwt_required
7077def logout ():
71- try :
72- _revoke_current_token ()
73- except KeyError :
74- return jsonify ({
75- 'msg' : 'Access token not found in the blacklist store'
76- }), 500
78+ jti = get_raw_jwt ()['jti' ]
79+ blacklist .add (jti )
7780 return jsonify ({"msg" : "Successfully logged out" }), 200
7881
7982
8083# Endpoint for revoking the current users refresh token
81- @app .route ('/logout2' , methods = ['POST ' ])
84+ @app .route ('/logout2' , methods = ['DELETE ' ])
8285@jwt_refresh_token_required
8386def logout2 ():
84- try :
85- _revoke_current_token ()
86- except KeyError :
87- return jsonify ({
88- 'msg' : 'Refresh token not found in the blacklist store'
89- }), 500
87+ jti = get_raw_jwt ()['jti' ]
88+ blacklist .add (jti )
9089 return jsonify ({"msg" : "Successfully logged out" }), 200
9190
9291
93- # Endpoint for listing tokens that have the same identity as you
94- # NOTE: This is currently very inefficient.
95- @app .route ('/auth/tokens' , methods = ['GET' ])
96- @jwt_required
97- def list_identity_tokens ():
98- username = get_jwt_identity ()
99- return jsonify (get_stored_tokens (username )), 200
100-
101-
102- # Endpoint for listing all tokens. In your app, you should either
103- # not expose this endpoint, or put some addition security on top
104- # of it so only trusted users (administrators, etc) can access it
105- @app .route ('/auth/all-tokens' )
106- def list_all_tokens ():
107- return jsonify (get_all_stored_tokens ()), 200
108-
109-
110- # Endpoint for allowing users to revoke their own tokens.
111- @app .route ('/auth/tokens/revoke/<string:jti>' , methods = ['PUT' ])
112- @jwt_required
113- def change_jwt_revoke_state (jti ):
114- username = get_jwt_identity ()
115- try :
116- token_data = get_stored_token (jti )
117- if token_data ['token' ]['identity' ] != username :
118- raise KeyError
119- revoke_token (jti )
120- return jsonify ({"msg" : "Token successfully revoked" }), 200
121- except KeyError :
122- return jsonify ({'msg' : 'Token not found' }), 404
123-
124-
125- # Endpoint for allowing users to un-revoke their own tokens.
126- @app .route ('/auth/tokens/unrevoke/<string:jti>' , methods = ['PUT' ])
127- @jwt_required
128- def change_jwt_unrevoke_state (jti ):
129- username = get_jwt_identity ()
130- try :
131- token_data = get_stored_token (jti )
132- if token_data ['token' ]['identity' ] != username :
133- raise KeyError
134- unrevoke_token (jti )
135- return jsonify ({"msg" : "Token successfully unrevoked" }), 200
136- except KeyError :
137- return jsonify ({'msg' : 'Token not found' }), 404
138-
139-
92+ # This will now prevent users with blacklisted tokens from
93+ # accessing this endpoint
14094@app .route ('/protected' , methods = ['GET' ])
14195@jwt_required
14296def protected ():
0 commit comments