Skip to content

Commit 3669ffa

Browse files
Adds option to use token multiple times
1 parent 1932809 commit 3669ffa

File tree

2 files changed

+24
-4
lines changed

2 files changed

+24
-4
lines changed

mailauth/signing.py

Lines changed: 9 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
from django.contrib.auth import get_user_model
22
from django.core import signing
3+
from django.core.signing import SignatureExpired
34
from django.utils import baseconv
45

56
__all__ = (
@@ -52,13 +53,14 @@ def _make_hash_value(self, user):
5253
user_pk = baseconv.base62.encode(user.pk)
5354
return self.sep.join((user_pk, last_login))
5455

55-
def unsign(self, value, max_age=None):
56+
def unsign(self, value, max_age=None, allow_multi_use=False):
5657
"""
5758
Verify access token and return user, if the token is valid.
5859
5960
Args:
6061
value (str): URL safe base64 encoded access token.
6162
max_age (datetime.timedelta): Maximum age an access token to be valid.
63+
allow_multi_use: If True allows the token to be used more than once
6264
6365
Returns:
6466
django.contrib.user.models.BaseUser: Return user object for given
@@ -85,7 +87,10 @@ def unsign(self, value, max_age=None):
8587
raise UserDoesNotExist("User with pk=%s does not exist" % user_pk) from e
8688
else:
8789
if last_login != '' and self.to_timestamp(user.last_login) != last_login:
88-
raise signing.SignatureExpired(
89-
"The access token for %r seems used" % user
90-
)
90+
if allow_multi_use:
91+
return user
92+
else:
93+
raise SignatureExpired(
94+
"The access token for %r seems used" % user
95+
)
9196
return user

tests/test_signing.py

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -39,6 +39,21 @@ def test_unsign__last_login(self, db, signer, signature):
3939
):
4040
signer.unsign(signature)
4141

42+
def test_unsing__allow_multiple_use(self, db, signer, signature):
43+
user = get_user_model().objects.create_user(
44+
pk=1337,
45+
46+
# later date, that does not match the signature (token was used)
47+
last_login=timezone.datetime(2012, 7, 3, tzinfo=timezone.utc),
48+
)
49+
assert user == signer.unsign(signature, allow_multi_use=True)
50+
assert user == signer.unsign(signature, allow_multi_use=True)
51+
with pytest.raises(
52+
SignatureExpired,
53+
match="The access token for <EmailUser: [email protected]> seems used"
54+
):
55+
signer.unsign(signature, allow_multi_use=False)
56+
4257
def test_to_timestamp(self):
4358
value = timezone.datetime(2002, 5, 3, tzinfo=timezone.utc)
4459
base62_value = signing.UserSigner.to_timestamp(value=value)

0 commit comments

Comments
 (0)