Token generation

Again, we have three functions that all produce a token, albeit in different formats. Let's see the example:

# secrs/secr_rand.py
print(secrets.token_bytes(16))
print(secrets.token_hex(32))
print(secrets.token_urlsafe(32))

The first one, token_bytes, simply returns a random byte string containing n bytes (16, in this example). The other two do the same, but token_hex returns a token in hexadecimal format, and token_urlsafe returns a token that only contains characters suitable for being included in a URL. Let's see the output (which is a continuation from the previous run):

b'xdax863xebxbb|x8fkx9bxbdx14Qxd4x8dx15}'
9f90fd042229570bf633e91e92505523811b45e1c3a72074e19bbeb2e5111bf7
bl4qz_Av7QNvPEqZtKsLuTOUsNLFmXW3O03pn50leiY

This is all nice, so why don't we have some fun and write a random password generator using these tools?

# secrs/secr_gen.py
import secrets
from string import digits, ascii_letters

def generate_pwd(length=8):
chars = digits + ascii_letters
return ''.join(secrets.choice(chars) for c in range(length))

def generate_secure_pwd(length=16, upper=3, digits=3):
if length < upper + digits + 1:
raise ValueError('Nice try!')
while True:
pwd = generate_pwd(length)
if (any(c.islower() for c in pwd)
and sum(c.isupper() for c in pwd) >= upper
and sum(c.isdigit() for c in pwd) >= digits):
return pwd

print(generate_secure_pwd())
print(generate_secure_pwd(length=3, upper=1, digits=1))

In the previous code, we defined two functions. generate_pwd simply generates a random string of given length by joining together length characters picked at random from a string that contains all the letters of the alphabet (lowercase and uppercase), and the 10 decimal digits.

Then, we define another function, generate_secure_pwd, that simply keeps calling generate_pwd until the random string we get matches the requirements, which are quite simple. The password must have at least one lowercase character, upper uppercase characters, digits digits, and length length.

Before we dive into the while loop, it's worth noting that if we sum together the requirements (uppercase, lowercase, and digits) and that sum is greater than the overall length of the password, there is no way we can ever satisfy the condition within the loop. So, in order to avoid getting stuck in an infinite loop, I have put a check clause in the first line of the body, and I raise a ValueError in case I need it. Could you think of how to write a test for this edge case?

The body of the while loop is straightforward: first we generate the random password, and then we verify the conditions by using any and sumany returns True if any of the items in the iterable it's called with evaluate to True. The use of sum is actually slightly more tricky here, in that it exploits polymorphism. Can you see what I'm talking about before you read on?

Well, it's very simple: True and False in Python are subclasses of integer numbers, therefore when summing on an iterable of True/False values, they will automatically be interpreted like integers by the sum function. That is called polymorphism, and we've briefly talked about it in Chapter 6, OOP, Decorators, and Iterators.

Running the example produces the following result:

$ python secr_gen.py
nsL5voJnCi7Ote3F

J5e

The second password is probably not too secure...

One last example, before we move on to the next module. Let's generate a reset password URL:

# secrs/secr_reset.py
import secrets

def get_reset_pwd_url(token_length=16):
token = secrets.token_urlsafe(token_length)
return f'https://fabdomain.com/reset-pwd/{token}'

print(get_reset_pwd_url())

This function is so easy I will only show you the output:

$ python secr_reset.py
https://fabdomain.com/reset-pwd/m4jb7aKgzTGuyjs9lTIspw
..................Content has been hidden....................

You can't read the all page of ebook, please click here login for view all page.
Reset