Designing A Login Register and User Authentication System in Python
Introduction
Password security is a critical aspect of cybersecurity. Passwords are essential for protecting our online identities and confidential data. To ensure password security, it is essential to use a robust password policy and take appropriate measures to safeguard them.
In this blog post, we will look at a Python script that implements a basic password management system that can help to understand how to approach creating secure user passwords.
The Python script provides a basic user interface that allows users to register and log in to the system. The system generates a random password that meets specific requirements for new users during the registration process.
Additionally, the system stores the user’s login credentials in a file after hashing the password using the SHA-256 algorithm. Let’s examine each section of the Python script in more detail.
Coding: Login Register and User Authentication Script
Importing Modules and Placing Constants
First, we import the necessary modules in the script.
import secrets
import string
import hashlib
from getpass import getpass
The secrets
module is used to generate random passwords. The string
module provides a list of ASCII letters and digits that are used to generate the random password.
The hashlib
module is used to hash the password before saving it to the user details file. Finally, the getpass
module is used to hide the password input from the terminal.
USER_DETAILS_FILEPATH = "users.txt"
PUNCTUATIONS = "@#$%&"
DEFAULT_PASSWORD_LENGTH = 12
INVALID_LENGTH_MESSAGE = f'''
Password length must be between 8 and 16.
Password length must be a number.
Generating password with default length of {DEFAULT_PASSWORD_LENGTH} characters.
'''
The USER_DETAILS_FILEPATH
constant holds the path to the file that stores the user details. The PUNCTUATIONS
constant holds the selected punctuation that can be used in the randomly generated password.
The DEFAULT_PASSWORD_LENGTH
constant holds the default password length if the user doesn't provide one. The INVALID_LENGTH_MESSAGE
constant holds the error message to display to the user if the user inputs a password length that is not in the range (8-16) and not an integer.
Generate Password
def generate_password(length=12):
characters = string.ascii_letters + string.digits + PUNCTUATIONS
pwd = ''.join(secrets.choice(characters) for _ in range(length))
return pwd
The generate_password
function generates a random password of the required length (default length is 12). The password consists of ASCII letters, digits, and selected punctuation characters.
In this script, instead of using all the punctuation from string.punctuation
I have decided to use only 5 commonly used punctuations stored in PUNCTUATIONS
variable. Of course, you can use all or modify them as per your requirements.
Hashing the Generated Password
def hash_password(pwd):
"""Hash a password using SHA-256 algorithm"""
pwd_bytes = pwd.encode('utf-8')
hashed_pwd = hashlib.sha256(pwd_bytes).hexdigest()
return hashed_pwd
The hash_password
function takes a password as input, encodes it to bytes, and hashes it using the SHA-256 algorithm. The hashed password is returned as a hexadecimal string.
Saving the User Details in a File
def save_user(username, hashed_pwd):
"""Save user-details to the users detail file"""
with open(USER_DETAILS_FILEPATH, "a") as f:
f.write(f"{username} {hashed_pwd}\n")
The save_user
function saves the user's details to a file specified by USER_DETAILS_FILEPATH
variable. The username and hashed password are written to the file as a space-separated string.
Important Note: The user details are saved in a plain text file (users.txt) without any encryption or other security measures. This can be a security risk if the file is accessed by an unauthorized user or if the file is stored on an insecure system.
Check If The User Exists
def user_exists(username):
try:
with open(USER_DETAILS_FILEPATH, "r") as f:
for line in f:
parts = line.split()
if parts[0] == username:
return True
except FileNotFoundError as fl_err:
print(f"{fl_err.args[-1]}: {USER_DETAILS_FILEPATH}")
print(f"System will create file: {USER_DETAILS_FILEPATH}")
return False
The user_exists
function checks if a user with the specified username already exists in the user details file. If the script is run first time it will catch the file not find error and give the user same message. The users.txt
file to save user details will be created after user password is created with save_user()
function.
It returns True if the user exists, otherwise False.
Authenticating User
def authenticate_user(username, password):
with open(USER_DETAILS_FILEPATH, "r") as f:
for line in f:
parts = line.split()
if parts[0] == username:
hashed_password = parts[1]
if hashed_password == hash_password(password):
return True
else:
return False
return False
The authenticate_user
function checks if the specified password matches the hashed password stored for the user with the specified username in the user details file. It returns True if the password is correct, otherwise False.
Important Note: The authenticate_user()
function reads the user details from the USER_DETAILS_FILEPATH file and checks if the password matches the hashed password in the file. While this approach is simple and straightforward, it may not be secure enough for a production system. For example, it does not use salting to further protect against attacks like dictionary attacks and rainbow table attacks.
Here is a basic example to demonstrate the basic use of adding salt to hash a password to make it more secure.
import hashlib
password = "password123"
salt = "randomsaltvalue"
hashed_password = hashlib.sha256(password.encode('utf-8') + salt.encode('utf-8')).hexdigest()
Validate input function
def validate_input(password_length):
try:
password_length = int(password_length)
if password_length < 8 or password_length > 16:
raise ValueError("Password length must be between 8 and 16")
return password_length
except ValueError:
print(INVALID_LENGTH_MESSAGE)
return DEFAULT_PASSWORD_LENGTH
The validate_input
function is used to validate user input for password length. It ensures that the password length is an integer value between 8 and 16 or that any other error like the input is not an integer.
If the user input is not within the specified range, we are raising a single exception here and that is ValueError
exception with a message stored in INVALID_LENGTH_MESSAGE
variable. If the input is valid, it returns the password length as an integer.
Register function
def register():
username = input("Enter username: ")
if user_exists(username):
print("User already exists.")
return
length = input("Enter Auto Generated Password Length (Number 8-16): ")
length = validate_input(length)
password = generate_password(length)
hashed_password = hash_password(password)
save_user(username, hashed_password)
print("User created successfully.")
print("Your password is:", password)
This function prompts the user to enter a username and password length. If the user already exists, the function returns without registering the user. Otherwise, it generates a password and saves the user’s details to the user details file. The generated password is printed on the console.
Login function
def login():
username = input("Enter username: ")
if not user_exists(username):
print("User does not exist.")
return
password = getpass("Password: ")
if not authenticate_user(username, password):
print("Incorrect password.")
return
print("Login successful.")
The login
function prompts the user to enter a username and password. It checks if the user exists using the user_exists
function.
If the user exists, it checks if the provided password matches the hashed password using the authenticate_user
function. If the authentication is successful, it prints "Login successful" to the console. If the authentication fails, it prints "Incorrect password" to the console.
Main function
def main():
while True:
print("1. Register\n2. Login\n3. Exit")
choice = input("Enter your choice: ")
if choice == "1":
register()
elif choice == "2":
login()
elif choice == "3":
break
else:
print("Invalid choice.")
if __name__ == "__main__":
main()
The main
function runs a while loop that repeatedly prompts the user to choose between registering, logging in, or exiting. If the user chooses to register, it calls the register
function. If the user chooses to log in, it calls the login
function. If the user chooses to exit, the while loop breaks and the program terminates.
if __name__ == “__main__”: construct checks if the current script is being run as the main program. If it is, it calls the main()
function.
Complete Code
Running the program
To run the program, open a terminal or command prompt, navigate to the directory where the script is saved, and type python authentication_system.py
in the terminal. The program will start running, and the user will be prompted to choose between registering, logging in, or exiting.
Note: Although I have put effort into designing this Secure Password Authentication script to be Pythonic and used secrets, getpass and hashlib modules yet this script can not be used directly in a production environment, mainly due to the reasons explained in Important Notes.
However, this is an import script, designed to understand the basics of a login, register and authentication system. I hope you enjoyed reading this.
Conclusion
In this tutorial, we have learned how to implement a simple authentication system using Python. We used the ‘secrets’ module to generate random passwords, the ‘hashlib’ module to hash passwords, and the ‘getpass’ module to securely get the user’s password input. We also learned how to read from and write to a file using Python’s built-in file-handling functions.
Hey there👋! If you found this tutorial helpful, please show your appreciation by clapping for it! Remember, you can clap multiple times if you like it.
