13

I'm following the "Flask Web Development" book to implement token based authentication. Basically, user authenticate his/herself with HTTP basic auth and a token is generated for it:

s = Serializer(app.config['SECRET_KEY'], expires_in = 3600)
token = s.dumps({ 'id': user.id })

But it looks like this doesn't change as long as the id and SECRET_KEY stay the same. I understand that the transaction will be through HTTPS but still think a dynamic token would be better. How to achieve a dynamic token?

0

1 Answer 1

25

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.

Sign up to request clarification or add additional context in comments.

1 Comment

Spot on. It was an alias. Thanks!

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.