python: API token generation with itsdangerous python: API token generation with itsdangerous flask flask

python: API token generation with itsdangerous


If you need a token that is time sensitive, use the TimedSerializer class instead.

Not only does it used a timestamp to form the signature (thus producing a new signature each time you use it), but you can also limit the token lifetime using that timestamp:

>>> from itsdangerous import TimedSerializer>>> s = TimedSerializer('sekrit')>>> token = s.dumps({'id': 'foobar'})>>> token'{"id": "foobar"}.COWWsA.dect1vZLaDdgFQUA1G_iTpPY3Hg'>>> s.loads(token, max_age=3600){'id': 'foobar'}>>> s.loads(token, max_age=0)Traceback (most recent call last):  File "<stdin>", line 1, in <module>  File "/Users/mpieters/Development/venvs/stackoverflow-2.7/lib/python2.7/site-packages/itsdangerous.py", line 643, in loads    .unsign(s, max_age, return_timestamp=True)  File "/Users/mpieters/Development/venvs/stackoverflow-2.7/lib/python2.7/site-packages/itsdangerous.py", line 463, in unsign    date_signed=self.timestamp_to_datetime(timestamp))itsdangerous.SignatureExpired: Signature age 18 > 0 seconds

Note that the Serializer class doesn't actually support a expires_in keyword argument, so I suspect the code you are quoting actually uses the (undocumented) TimedJSONWebSignatureSerializer class instead (imported with an alias), which does take that keyword argument, and which includes a timestamp as well:

>>> from itsdangerous import TimedJSONWebSignatureSerializer as Serializer>>> s = Serializer('sekrit', expires_in=3600)>>> token = s.dumps({'id': 'foobar'})>>> token'eyJhbGciOiJIUzI1NiIsImV4cCI6MTQ0MzEwODAyMywiaWF0IjoxNDQzMTA0NDIzfQ.eyJpZCI6ImZvb2JhciJ9.eCD3zKK1lYT8cZ9w8g0YVpaF-1rR-k6UNCYq9dHmvGo'>>> s.loads(token){'id': 'foobar'}>>> s = Serializer('sekrit', expires_in=0)>>> token = s.dumps({'id': 'foobar'})>>> token'eyJhbGciOiJIUzI1NiIsImV4cCI6MTQ0MzEwNDUwMCwiaWF0IjoxNDQzMTA0NTAwfQ.eyJpZCI6ImZvb2JhciJ9.Eiw3Eco7p61X-ikMxXS5dDVmjYmRSThcsMCxMyuA-r0'>>> s.loads(token)Traceback (most recent call last):  File "<stdin>", line 1, in <module>  File "/Users/mpieters/Development/venvs/stackoverflow-2.7/lib/python2.7/site-packages/itsdangerous.py", line 807, in loads    date_signed=self.get_issue_date(header))itsdangerous.SignatureExpired: Signature expired

Because the timestamp is included, the generated token is not static:

>>> s = Serializer('sekrit', expires_in=3600)>>> token1 = s.dumps({'id': 'foobar'})>>> token2 = s.dumps({'id': 'foobar'})>>> from difflib import ndiff>>> print '\n'.join(ndiff(token1.split('.'), token2.split('.')))- eyJhbGciOiJIUzI1NiIsImV4cCI6MTQ0MzEwODIwMywiaWF0IjoxNDQzMTA0NjAzfQ?                                        ^ ^                    ^^+ eyJhbGciOiJIUzI1NiIsImV4cCI6MTQ0MzEwODIxMSwiaWF0IjoxNDQzMTA0NjExfQ?                                        ^ ^                    ^^  eyJpZCI6ImZvb2JhciJ9- YmrKQTvZEWw4_JOOPn5uEk9QlZNla4o3Gvo09H1MXfM+ ApeLrwT_R60pkvCYe4ihzJFPG55tGiJK6VSi6BKxAXM

These two tokens differ materially even though they were produced by the same serializer.