How does Django create and verify tokens to reset password?

5

Given a template to reset password already implemented by Django, in the following views:

  • django.contrib.auth.password_reset
  • django.contrib.auth.password_reset_done
  • django.contrib.auth.password_reset_confirm
  • django.contrib.auth.password_reset_complete

In the view " django.contrib.auth.password_reset " is generated a token passed as url parameter for the django.contrib.auth.password_reset_confirm view.

On this token, I did not find anything about it in the Django documentation and I also could not understand the source code, however I have the following questions:

  • How is it generated and verified?
  • Is the token persisted in the database and bound to the user, or is it only generated a hash of the id?
  • Using this method can be generated tokens for other utilities, such as activating an account?
  • asked by anonymous 07.06.2016 / 13:42

    1 answer

    4

    Excellent question man!

    Come on, as per the Django documentation :

    There are 4 views for password reset:

    # - password_reset envia o email
    # - password_reset_done mostra uma mensagem de sucesso para o envio do email
    # - password_reset_confirm checa a url e pergunta por uma nova senha
    # - password_reset_complete mostra uma mensagem de sucesso para todo o processo
    

    The views password_reset and password_reset_confirm use the same class to generate and check the token. django.contrib.auth.tokens.default_token_generator .

    1. How is it generated and verified?

    Looking at the documentation link you can see that django uses the following class to generate the token from django.contrib.auth.tokens.default_token_generator .

    Searching for this class in the github project you can check that the token is generated according to the user and the timestamp.

    Removing comments from the code if it has:

    def make_token(self, user):
        return self._make_token_with_timestamp(user, self._num_days(self._today()))
    
    def _make_token_with_timestamp(self, user, timestamp):
        ts_b36 = int_to_base36(timestamp)
        hash = salted_hmac(
            self.key_salt,
            self._make_hash_value(user, timestamp),
        ).hexdigest()[::2]
        return "%s-%s" % (ts_b36, hash)
    
    def _make_hash_value(self, user, timestamp):
        login_timestamp = '' if user.last_login is None else user.last_login.replace(microsecond=0, tzinfo=None)
        return (
            six.text_type(user.pk) + user.password +
            six.text_type(login_timestamp) + six.text_type(timestamp)
        )
    

    Checking this code and what is in github (I will not include everything because I believe it is unnecessary) we can conclude that the token formed is generated from timestamp+"-"+hash

    Where:

    #timestamp = conversao numero de dias desde 01/01/2001 para a base 36.
    #hash = user.pk + user.password + user.last_login + timestamp
    

    The fact that the code uses six.text_type is due to the fact that a str pattern was set for Python3 and unicode for other versions, as you can see in the code itself for the six class.

    if PY3:
        string_types = str,
        integer_types = int,
        class_types = type,
        text_type = str
        binary_type = bytes
    
        MAXSIZE = sys.maxsize
    else:
        string_types = basestring,
        integer_types = (int, long)
        class_types = (type, types.ClassType)
        text_type = unicode
        binary_type = str
    

    2. Is the token persisted in the database and bound to the user, or is it only generated a hash of the id?

    As you can see in the project code, the token is not persisted but is generated and acknowledged at the time of access. As demonstrated in the first question.

    3. Using this method can be generated tokens for other utilities, such as activating an account?

    Yes, you can use the django.contrib.auth.tokens.default_token_generator class to do token generation and validation, but you will need to implement your own views.

        
    07.06.2016 / 14:01