1818 get_access_cookie_name , get_cookie_secure , get_access_cookie_path , \
1919 get_cookie_csrf_protect , get_access_csrf_cookie_name , \
2020 get_refresh_cookie_name , get_refresh_cookie_path , \
21- get_refresh_csrf_cookie_name
21+ get_refresh_csrf_cookie_name , get_token_location , \
22+ get_access_csrf_header_name , get_refresh_csrf_header_name
2223from flask_jwt_extended .exceptions import JWTEncodeError , JWTDecodeError , \
23- InvalidHeaderError , NoAuthHeaderError , WrongTokenError , RevokedTokenError , \
24+ InvalidHeaderError , NoAuthorizationError , WrongTokenError , RevokedTokenError , \
2425 FreshTokenRequired
2526from flask_jwt_extended .blacklist import check_if_token_revoked , store_token
2627
@@ -42,8 +43,8 @@ def get_jwt_claims():
4243
4344
4445# TODO set csrf token in jwt when creating tokens (if enabled)
45- def _create_xsrf_token ():
46- return binascii . hexlify ( os . urandom ( 60 ))
46+ def _create_csrf_token ():
47+ return str ( uuid . uuid4 ( ))
4748
4849
4950def _encode_access_token (identity , secret , algorithm , token_expire_delta ,
@@ -80,6 +81,8 @@ def _encode_access_token(identity, secret, algorithm, token_expire_delta,
8081 'type' : 'access' ,
8182 'user_claims' : user_claims ,
8283 }
84+ if get_token_location () == 'cookies' and get_cookie_csrf_protect ():
85+ token_data ['csrf' ] = _create_csrf_token ()
8386 encoded_token = jwt .encode (token_data , secret , algorithm ).decode ('utf-8' )
8487
8588 # If blacklisting is enabled and configured to store access and refresh tokens,
@@ -110,6 +113,8 @@ def _encode_refresh_token(identity, secret, algorithm, token_expire_delta):
110113 'identity' : identity ,
111114 'type' : 'refresh' ,
112115 }
116+ if get_token_location () == 'cookies' and get_cookie_csrf_protect ():
117+ token_data ['csrf' ] = _create_csrf_token ()
113118 encoded_token = jwt .encode (token_data , secret , algorithm ).decode ('utf-8' )
114119
115120 # If blacklisting is enabled, store this token in our key-value store
@@ -142,14 +147,17 @@ def _decode_jwt(token, secret, algorithm):
142147 raise JWTDecodeError ("Missing or invalid claim: fresh" )
143148 if 'user_claims' not in data or not isinstance (data ['user_claims' ], dict ):
144149 raise JWTDecodeError ("Missing or invalid claim: user_claims" )
150+ if get_token_location () == 'cookies' and get_cookie_csrf_protect ():
151+ if 'csrf' not in data or not isinstance (data ['csrf' ], six .string_types ):
152+ raise JWTDecodeError ("Missing or invalid claim: csrf" )
145153 return data
146154
147155
148- def _decode_jwt_from_request ():
156+ def _decode_jwt_from_headers ():
149157 # Verify we have the auth header
150158 auth_header = request .headers .get ('Authorization' , None )
151159 if not auth_header :
152- raise NoAuthHeaderError ("Missing Authorization Header" )
160+ raise NoAuthorizationError ("Missing Authorization Header" )
153161
154162 # Make sure the header is valid
155163 expected_header = get_jwt_header_type ()
@@ -170,6 +178,37 @@ def _decode_jwt_from_request():
170178 return _decode_jwt (token , secret , algorithm )
171179
172180
181+ def _decode_jwt_from_cookies (type ):
182+ if type == 'access' :
183+ cookie_key = get_access_cookie_name ()
184+ csrf_header_key = get_access_csrf_header_name ()
185+ else :
186+ cookie_key = get_refresh_cookie_name ()
187+ csrf_header_key = get_refresh_csrf_header_name ()
188+
189+ token = request .cookies .get (cookie_key )
190+ if not token :
191+ raise NoAuthorizationError ('Missing cookie "{}"' .format (cookie_key ))
192+ secret = _get_secret_key ()
193+ algorithm = get_algorithm ()
194+ token = _decode_jwt (token , secret , algorithm )
195+
196+ if get_cookie_csrf_protect ():
197+ csrf = request .headers .get (csrf_header_key , None )
198+ if not csrf or csrf != token ['csrf' ]:
199+ raise NoAuthorizationError ("Missing or invalid csrf double submit header" )
200+
201+ return token
202+
203+
204+ def _decode_jwt_from_request (type ):
205+ token_location = get_token_location ()
206+ if token_location == 'headers' :
207+ return _decode_jwt_from_headers ()
208+ else :
209+ return _decode_jwt_from_cookies (type )
210+
211+
173212def _handle_callbacks_on_error (fn ):
174213 """
175214 Helper decorator that will catch any exceptions we expect to encounter
@@ -183,7 +222,7 @@ def wrapper(*args, **kwargs):
183222
184223 try :
185224 return fn (* args , ** kwargs )
186- except NoAuthHeaderError :
225+ except NoAuthorizationError :
187226 return m .unauthorized_callback ()
188227 except jwt .ExpiredSignatureError :
189228 return m .expired_token_callback ()
@@ -211,7 +250,7 @@ def jwt_required(fn):
211250 @wraps (fn )
212251 def wrapper (* args , ** kwargs ):
213252 # Attempt to decode the token
214- jwt_data = _decode_jwt_from_request ()
253+ jwt_data = _decode_jwt_from_request (type = 'access' )
215254
216255 # Verify this is an access token
217256 if jwt_data ['type' ] != 'access' :
@@ -243,7 +282,7 @@ def fresh_jwt_required(fn):
243282 @wraps (fn )
244283 def wrapper (* args , ** kwargs ):
245284 # Attempt to decode the token
246- jwt_data = _decode_jwt_from_request ()
285+ jwt_data = _decode_jwt_from_request (type = 'access' )
247286
248287 # Verify this is an access token
249288 if jwt_data ['type' ] != 'access' :
@@ -276,7 +315,7 @@ def jwt_refresh_token_required(fn):
276315 @wraps (fn )
277316 def wrapper (* args , ** kwargs ):
278317 # Get the JWT
279- jwt_data = _decode_jwt_from_request ()
318+ jwt_data = _decode_jwt_from_request (type = 'refresh' )
280319
281320 # verify this is a refresh token
282321 if jwt_data ['type' ] != 'refresh' :
@@ -329,11 +368,7 @@ def _get_csrf_token(encoded_token):
329368 secret = _get_secret_key ()
330369 algorithm = get_algorithm ()
331370 token = _decode_jwt (encoded_token , secret , algorithm )
332- try :
333- return token ['csrf' ]
334- except KeyError :
335- raise RuntimeError ('JWT does not have csrf token set. Is '
336- 'JWT_COOKIE_CSRF_PROTECT set to True?' )
371+ return token ['csrf' ]
337372
338373
339374def set_access_cookies (response , encoded_access_token ):
0 commit comments