1
- import datetime
2
-
3
- import simplekv .memory
4
1
from flask import Flask , request , jsonify
5
2
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
+ )
11
8
12
9
13
10
# Setup flask
14
11
app = Flask (__name__ )
15
- app .secret_key = 'super-secret '
12
+ app .secret_key = 'ChangeMe! '
16
13
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
21
16
app .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' ]
30
18
jwt = JWTManager (app )
31
19
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
+
32
46
33
47
# Standard login endpoint
34
48
@app .route ('/login' , methods = ['POST' ])
@@ -45,7 +59,8 @@ def login():
45
59
return jsonify (ret ), 200
46
60
47
61
48
- # Standard refresh endpoint
62
+ # Standard refresh endpoint. A blacklisted refresh token
63
+ # will not be able to access this endpoint
49
64
@app .route ('/refresh' , methods = ['POST' ])
50
65
@jwt_refresh_token_required
51
66
def refresh ():
@@ -56,87 +71,26 @@ def refresh():
56
71
return jsonify (ret ), 200
57
72
58
73
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
-
67
74
# Endpoint for revoking the current users access token
68
- @app .route ('/logout' , methods = ['POST ' ])
75
+ @app .route ('/logout' , methods = ['DELETE ' ])
69
76
@jwt_required
70
77
def 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 )
77
80
return jsonify ({"msg" : "Successfully logged out" }), 200
78
81
79
82
80
83
# Endpoint for revoking the current users refresh token
81
- @app .route ('/logout2' , methods = ['POST ' ])
84
+ @app .route ('/logout2' , methods = ['DELETE ' ])
82
85
@jwt_refresh_token_required
83
86
def 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 )
90
89
return jsonify ({"msg" : "Successfully logged out" }), 200
91
90
92
91
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
140
94
@app .route ('/protected' , methods = ['GET' ])
141
95
@jwt_required
142
96
def protected ():
0 commit comments