Skip to content

Commit efa38a1

Browse files
committed
Merge branch 'master' into user_loader
2 parents 3638368 + 3f90d1f commit efa38a1

File tree

4 files changed

+73
-5
lines changed

4 files changed

+73
-5
lines changed

docs/changing_default_behavior.rst

+25
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,9 @@
11
Changing Default Behaviors
22
==========================
33

4+
Changing callback functions
5+
~~~~~~~~~~~~~~~~~~~~~~~~~~~
6+
47
We provide what we think are sensible behaviors when attempting to access a
58
protected endpoint. If the access token is not valid for any reason (missing,
69
expired, tampered with, etc) we will return json in the format of {'msg': 'why
@@ -40,3 +43,25 @@ Possible loader functions are:
4043
* - **user_loader_error_loader**
4144
- Function that is called when the user_loader callback function returns **None**
4245
- Takes one argument - The identity of the user who failed to load
46+
47+
Dynamic token expires time
48+
~~~~~~~~~~~~~~~~~~~~~~~~~~
49+
50+
You can also change the expires time for a token via the **expires_delta** kwarg
51+
in the **create_refresh_token** and **create_access_token** functions. This takes
52+
a **datetime.timedelta** and overrides the **JWT_REFRESH_TOKEN_EXPIRES** and
53+
**JWT_ACCESS_TOKEN_EXPIRES** options. This can be useful if you have different
54+
use cases for different tokens. An example of this might be you use short lived
55+
access tokens used in your web application, but you allow the creation of long
56+
lived access tokens that other developers can generate and use to interact with
57+
your api in their programs.
58+
59+
.. code-block:: python
60+
61+
@app.route('/create-dev-token', methods=[POST])
62+
@jwt_required
63+
def create_dev_token():
64+
username = get_jwt_identity()
65+
expires = datatime.timedelta(days=365)
66+
token = create_access_token(username, expires_delta=expires)
67+
return jsonify({'token': token}), 201

flask_jwt_extended/jwt_manager.py

+17-4
Original file line numberDiff line numberDiff line change
@@ -297,7 +297,7 @@ def user_loader(self, identity):
297297
"""
298298
return self._user_loader_callback(identity)
299299

300-
def create_refresh_token(self, identity):
300+
def create_refresh_token(self, identity, expires_delta=None):
301301
"""
302302
Creates a new refresh token
303303
@@ -309,13 +309,19 @@ def create_refresh_token(self, identity):
309309
query disk twice, once for initially finding the identity
310310
in your login endpoint, and once for setting addition data
311311
in the JWT via the user_claims_loader
312+
:param expires_delta: A datetime.timedelta for how long this token should
313+
last before it expires. If this is None, it will
314+
use the 'JWT_REFRESH_TOKEN_EXPIRES` config value
312315
:return: A new refresh token
313316
"""
317+
if expires_delta is None:
318+
expires_delta = config.refresh_expires
319+
314320
refresh_token = encode_refresh_token(
315321
identity=self._user_identity_callback(identity),
316322
secret=config.encode_key,
317323
algorithm=config.algorithm,
318-
expires_delta=config.refresh_expires,
324+
expires_delta=expires_delta,
319325
csrf=config.csrf_protect
320326
)
321327

@@ -326,7 +332,7 @@ def create_refresh_token(self, identity):
326332
store_token(decoded_token, revoked=False)
327333
return refresh_token
328334

329-
def create_access_token(self, identity, fresh=False):
335+
def create_access_token(self, identity, fresh=False, expires_delta=None):
330336
"""
331337
Creates a new access token
332338
@@ -340,13 +346,19 @@ def create_access_token(self, identity, fresh=False):
340346
in the JWT via the user_claims_loader
341347
:param fresh: If this token should be marked as fresh, and can thus access
342348
fresh_jwt_required protected endpoints. Defaults to False
349+
:param expires_delta: A datetime.timedelta for how long this token should
350+
last before it expires. If this is None, it will
351+
use the 'JWT_ACCESS_TOKEN_EXPIRES` config value
343352
:return: A new access token
344353
"""
354+
if expires_delta is None:
355+
expires_delta = config.access_expires
356+
345357
access_token = encode_access_token(
346358
identity=self._user_identity_callback(identity),
347359
secret=config.encode_key,
348360
algorithm=config.algorithm,
349-
expires_delta=config.access_expires,
361+
expires_delta=expires_delta,
350362
fresh=fresh,
351363
user_claims=self._user_claims_callback(identity),
352364
csrf=config.csrf_protect
@@ -356,3 +368,4 @@ def create_access_token(self, identity, fresh=False):
356368
config.algorithm, csrf=config.csrf_protect)
357369
store_token(decoded_token, revoked=False)
358370
return access_token
371+

setup.py

+1-1
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,7 @@
66
from setuptools import setup
77

88
setup(name='Flask-JWT-Extended',
9-
version='2.2.0',
9+
version='2.3.0',
1010
url='https://github.com/vimalloc/flask-jwt-extended',
1111
license='MIT',
1212
author='Landon Gilbert-Bland',

tests/test_protected_endpoints.py

+30
Original file line numberDiff line numberDiff line change
@@ -33,6 +33,16 @@ def login():
3333
}
3434
return jsonify(ret), 200
3535

36+
@self.app.route('/auth/login2', methods=['POST'])
37+
def login2():
38+
expires = timedelta(minutes=5)
39+
ret = {
40+
'access_token': create_access_token('test', fresh=True,
41+
expires_delta=expires),
42+
'refresh_token': create_refresh_token('test', expires_delta=expires),
43+
}
44+
return jsonify(ret), 200
45+
3646
@self.app.route('/auth/refresh', methods=['POST'])
3747
@jwt_refresh_token_required
3848
def refresh():
@@ -341,6 +351,26 @@ def test_bad_tokens(self):
341351
self.assertEqual(status_code, 422)
342352
self.assertIn('msg', data)
343353

354+
def test_expires_time_override(self):
355+
# Test access token
356+
response = self.client.post('/auth/login2')
357+
data = json.loads(response.get_data(as_text=True))
358+
access_token = data['access_token']
359+
time.sleep(2)
360+
status_code, data = self._jwt_get('/partially-protected', access_token)
361+
self.assertEqual(status_code, 200)
362+
self.assertEqual(data, {'msg': 'protected hello world'})
363+
364+
# Test refresh token
365+
response = self.client.post('/auth/login2')
366+
data = json.loads(response.get_data(as_text=True))
367+
refresh_token = data['refresh_token']
368+
time.sleep(2)
369+
status_code, data = self._jwt_post('/auth/refresh', refresh_token)
370+
self.assertEqual(status_code, 200)
371+
self.assertIn('access_token', data)
372+
self.assertNotIn('msg', data)
373+
344374
def test_optional_jwt_bad_tokens(self):
345375
# Test expired access token
346376
response = self.client.post('/auth/login')

0 commit comments

Comments
 (0)