Skip to content

Commit e45c0d1

Browse files
authored
Merge pull request #55 from vimalloc/expires_overwrite
Add abillity to change token expires time at a non-global level
2 parents 950a333 + 2bc81aa commit e45c0d1

File tree

3 files changed

+71
-4
lines changed

3 files changed

+71
-4
lines changed

docs/changing_default_behavior.rst

Lines changed: 25 additions & 0 deletions
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
@@ -34,3 +37,25 @@ Possible loader functions are:
3437
* - **revoked_token_loader**
3538
- Function to call when a revoked token accesses a protected endpoint
3639
- None
40+
41+
Dynamic token expires time
42+
~~~~~~~~~~~~~~~~~~~~~~~~~~
43+
44+
You can also change the expires time for a token via the **expires_delta** kwarg
45+
in the **create_refresh_token** and **create_access_token** functions. This takes
46+
a **datetime.timedelta** and overrides the **JWT_REFRESH_TOKEN_EXPIRES** and
47+
**JWT_ACCESS_TOKEN_EXPIRES** options. This can be useful if you have different
48+
use cases for different tokens. An example of this might be you use short lived
49+
access tokens used in your web application, but you allow the creation of long
50+
lived access tokens that other developers can generate and use to interact with
51+
your api in their programs.
52+
53+
.. code-block:: python
54+
55+
@app.route('/create-dev-token', methods=[POST])
56+
@jwt_required
57+
def create_dev_token():
58+
username = get_jwt_identity()
59+
expires = datatime.timedelta(days=365)
60+
token = create_access_token(username, expires_delta=expires)
61+
return jsonify({'token': token}), 201

flask_jwt_extended/jwt_manager.py

Lines changed: 16 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -244,7 +244,7 @@ def revoked_token_loader(self, callback):
244244
self._revoked_token_callback = callback
245245
return callback
246246

247-
def create_refresh_token(self, identity):
247+
def create_refresh_token(self, identity, expires_delta=None):
248248
"""
249249
Creates a new refresh token
250250
@@ -256,13 +256,19 @@ def create_refresh_token(self, identity):
256256
query disk twice, once for initially finding the identity
257257
in your login endpoint, and once for setting addition data
258258
in the JWT via the user_claims_loader
259+
:param expires_delta: A datetime.timedelta for how long this token should
260+
last before it expires. If this is None, it will
261+
use the 'JWT_REFRESH_TOKEN_EXPIRES` config value
259262
:return: A new refresh token
260263
"""
264+
if expires_delta is None:
265+
expires_delta = config.refresh_expires
266+
261267
refresh_token = encode_refresh_token(
262268
identity=self._user_identity_callback(identity),
263269
secret=config.encode_key,
264270
algorithm=config.algorithm,
265-
expires_delta=config.refresh_expires,
271+
expires_delta=expires_delta,
266272
csrf=config.csrf_protect
267273
)
268274

@@ -273,7 +279,7 @@ def create_refresh_token(self, identity):
273279
store_token(decoded_token, revoked=False)
274280
return refresh_token
275281

276-
def create_access_token(self, identity, fresh=False):
282+
def create_access_token(self, identity, fresh=False, expires_delta=None):
277283
"""
278284
Creates a new access token
279285
@@ -287,13 +293,19 @@ def create_access_token(self, identity, fresh=False):
287293
in the JWT via the user_claims_loader
288294
:param fresh: If this token should be marked as fresh, and can thus access
289295
fresh_jwt_required protected endpoints. Defaults to False
296+
:param expires_delta: A datetime.timedelta for how long this token should
297+
last before it expires. If this is None, it will
298+
use the 'JWT_ACCESS_TOKEN_EXPIRES` config value
290299
:return: A new access token
291300
"""
301+
if expires_delta is None:
302+
expires_delta = config.access_expires
303+
292304
access_token = encode_access_token(
293305
identity=self._user_identity_callback(identity),
294306
secret=config.encode_key,
295307
algorithm=config.algorithm,
296-
expires_delta=config.access_expires,
308+
expires_delta=expires_delta,
297309
fresh=fresh,
298310
user_claims=self._user_claims_callback(identity),
299311
csrf=config.csrf_protect

tests/test_protected_endpoints.py

Lines changed: 30 additions & 0 deletions
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():
@@ -342,6 +352,26 @@ def test_bad_tokens(self):
342352
self.assertEqual(status_code, 422)
343353
self.assertIn('msg', data)
344354

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

0 commit comments

Comments
 (0)