What is 2FA and why 2FA?🤔
2FA stands for two factor authentication. It adds an extra layer of security other than password. The user must enter a 2FA code along with password in order to sign in. 2FA codes can be generated in two ways, time based codes and counter based codes.
Advantages of 2FA over E-Mail or SMS verification:
- No network required: 2FA codes can be generated offline.
- 🛡️ Better security.
Time based codes vs counter based code.
Time based codes | Counter based codes |
---|---|
Time based codes changes depending on time. 🕖 | Counter based codes change depending on number of successful sign-in(s). ✔️ |
No need of adding counter every time in client side. | After every successful login, counter must be increased by one in server side as well as client side. |
2FA with Python
Requirements
-
onetimepass python package (Can be installed using the command:
pip install onetimepass
). - Your favourite authenticator app (Example: Google authenticator, Microsoft authenticator).
Let's start!👀
For both time based codes and counter based code, a secret string is securely shared with the authenticator app while setting up 2FA. All codes are generated based on this secret string. This string is not case sensitive.
🕖Time based codes
Let us now, write a simple Python script to understand how time based 2FA works!
from onetimepass import valid_totp
from secrets import choice
def generate_secret(): # Function to return a random string with length 16.
secret = ''
while len(secret) < 16:
secret += choice('ABCDEFGHIJKLMNOPQRSTUVWXYZ234567')
return secret
secret = generate_secret()
print('Enter the following secret in your authenticator app: ', secret)
print("""
Instructions for saving this secret it Google Authenticator:
1. Open Google Authenticator.
2. Click plus icon at the right bottom.
3. Click Enter a setup key.
4. Enter an Account name of your choice and enter the secret provided above.
5. Click Add.
""")
while True:
otp = int(input('Please enter the otp generated by your authenticator app: '))
authenticated = valid_totp(otp, secret)
if authenticated:
print('Correct otp, Authenticated!')
elif not authenticated:
print('Wrong otp, please try again.')
✔️ Counter based codes
Here is a complete Python script to understand how counter based 2FA works!
from onetimepass import valid_hotp
from secrets import choice
def generate_secret(): # Function to return a random string with length 16.
secret = ''
while len(secret) < 16:
secret += choice('ABCDEFGHIJKLMNOPQRSTUVWXYZ234567')
return secret
secret = generate_secret()
print('Enter the following secret in your authenticator app: ', secret)
print("""
Instructions for saving this secret it Google Authenticator:
1. Open Google Authenticator.
2. Click plus icon at the right bottom.
3. Click Enter a setup key.
4. Enter an Account name of your choice and enter the secret provided above.
5. Click Add.
""")
while True:
counter = 0
otp = int(input('Please enter the otp generated by your authenticator app: '))
authenticated = valid_hotp(otp, secret)
if authenticated:
print('Correct otp, Authenticated!')
counter += 1
elif not authenticated:
print('Wrong otp, please try again.')
Thank you! Leave a comment and a like if you find this article useful :-)
Original article: https://blog.jothin.tech/2fa-with-python
Edit: Thanks to @amoir18 for reporting a mistake.
Top comments (5)
choice('ABCDEFGHIJKLMNOPQRSTUVWXYZ01234567')
This will give a base32 digit error because 0 & 1 are not included in the base32 alphabet
it should be:
choice('ABCDEFGHIJKLMNOPQRSTUVWXYZ234567')
otherwise this is great!
Thanks for telling, I changed it.
Thanks for writing your article, it provided the motivation for implementing HOTP 2fa for the Roundup issue tracker. The HOTP implementation is on the Roundup wiki. I hope to extend it to TOTP in the near future.
I can't get this to work. The code runs, but when I get to the point to enter the otp, it keeps saying the code is wrong. I have tried re-running with multiple keys, but it doesn't ever take the code that is being generated by the authenticator.
I never expected this to be so easy 😆