Imagine the following simple user registration flow:
- User registers by setting an e-mail and a password.
- The server sends an e-mail containing an activation link to the user.
- The user clicks on the activation link and activates their account.
Let's focus on step 2. How do we generate this activation link?
One way to implement this is to generate single use tokens and save them in a database table. Our url then looks like
http://sitename.com/activate?token=mytoken. That solution has the benefit of being able to track that a token has been used and enforce single use. The drawback is that you need a new database table, which I wanted to avoid.
How else can we generate these tokens?
My first hunch was to reuse Django's PasswordResetTokenGenerator. However, notice that the
check_token function requires a user. This means that the activation link needs to include information about who the user is. So our
/activate link will need to include an url parameter that's either the username, user id, or something else that uniquely identifies a user. I'm not happy with that. This is a link that's supposed to be private and I don't want the user's first impression of my site to be that I'm including their user id in an activation link, when most other sites don't do that.
The solution that I settled on was encrypting the user id on the server. I'm generating a link of the form
http://sitename.com/activate?token=mytoken. I generate the token using the Python cryptography package. The token is created by encrypting the username with a salt.
So why is this not what Django is doing? I suppose the answer is that Python doesn't have a symmetric encryption implementation library coming with it.
In addition, I also wanted the tokens to expire. This is simple, we can just include a timestamp in the token and compare it against the current time upon decryption.
Here's a sample class that implements all of this: