Source code for scipost.totp

__copyright__ = "Copyright © Stichting SciPost (SciPost Foundation)"
__license__ = "AGPL v3"

import time
import pyotp

from .models import TOTPDevice


[docs]class TOTPVerification: number_of_digits = 6 token_validity_period = 30 tolerance = 2 # Gives a 2 minute window to use a code. def __init__(self, user): """ Initiate for a certain user instance. """ self._user = user
[docs] def verify_code(self, code): """ Verify a time-dependent code for a certain User. """ try: # Try to see if input token is convertible to integer. # Do not actually make it an integer, because it'll lose the leading 0s. assert int(code) > 0 except (ValueError, AssertionError): # return False, if token could not be converted to an integer return False else: if not hasattr(self._user, "devices"): # For example non-authenticated users... return False for device in self._user.devices.all(): time_int = int(time.time()) totp = pyotp.TOTP( device.token, interval=self.token_validity_period, digits=self.number_of_digits, ) # 1. Check if the current counter is higher than the value of last verified counter # 2. Check if entered token is correct valid_token = totp.verify( code, for_time=time_int, valid_window=self.tolerance ) if not valid_token: # Token not valid continue elif ( device.last_verified_counter <= 0 or time_int > device.last_verified_counter ): # If the condition is true, set the last verified counter value # to current counter value, and return True TOTPDevice.objects.filter(id=device.id).update( last_verified_counter=time_int ) return True return False
[docs] @classmethod def verify_token(cls, secret_key, code): """ Independently verify a secret_key/code combination at current time. """ try: # Try to see if input token is convertible to integer. # Do not actually make it an integer, because it'll lose the leading 0s. assert int(code) > 0 except (ValueError, AssertionError): # return False, if token could not be converted to an integer return False time_int = int(time.time()) totp = pyotp.TOTP( secret_key, interval=cls.token_validity_period, digits=cls.number_of_digits ) return totp.verify(code, for_time=time_int, valid_window=cls.tolerance)