Authentication Deep Dive Part 2

Types of Authentication

Adebayo Taiwo
7 min readMay 4, 2023

Welcome back! In our previous article, we dove deep into the fascinating world of authentication. We explored the different methods that are commonly used to preserve authentication state in web applications and also provided practical examples on how to implement them using Python.

Now, get ready to take your understanding of authentication to the next level as we delve into the different types of authentication. In this article, we’ll cover the most popular forms of authentication, their pros and cons, and when to use them.

Whether you’re a seasoned developer or a newcomer to the world of web development, this article is a must-read. So, buckle up and let’s get started!

In this article we’ll continue from where we stopped and proceed to learn about the various types of authentication, their pros and cons and how to implement them in code.

Password-based authentication

Password-based authentication is the most commonly used type of authentication. When a user registers for an application, they are required to provide personal information and and also provide a secret such as a password or pin. The provided information is associated with a unique identifier such as email address or phone number.

The secret is encrypted using a one-way hash function and stored on the server. This is done to ensure that even if the server is compromised, the secret cannot be easily accessed by an attacker. Whenever a user wants to authenticate, they provide their unique identifier and the secret.

On the server, the provided secret is verified by comparing it to the one stored for the corresponding unique identifier. If the secret matches, the user is authenticated and granted access to the requested resource. If the secret does not match, the server returns an Unauthorized Response, denying access to the user.

Let’s dive into how it’s done

For this demo, we’ll be building a dummy financial app where users can receive free money. We’ll also be adding password-based authentication to the app. Here’s a sneak peek of our homepage

Starting with our register route

Our register route can serve two methods “GET” and “POST”, if the client send a “GET” request we return the register page

Note the parameters being set for render_template allow me to reuse the same html file for both login and register page.

If the request is a post request, we extract the submitted email and password, we then store the information in a dictionary, the provided email is used as the user’s id.

Note: No validation is being done here, in production, the submitted data will be validated to check if they meet specific requirements such as the password being strong enough, the user information will also be stored in a databse.

We call the login_user function which set cookie for the user’s session and return a redirect response to redirect the user to the home page(we’ve used this before in part 1).

def login_user():
id = "".join(random.sample(letters, 10))
resp = make_response(redirect(url_for("home")))
resp.set_cookie("auth_cookie", id)
return resp, id

In our index route we retrieve the “auth_cookie” cookie and call the retrieve_user function to get the user to which this session belongs to.

Home route for our application
def retrieve_user(cookie):
user_email = cookies.get(cookie, "")
user_details = database.get(user_email, {})
return user_email, user_details

If no user is found, we redirect the client to the login page.

Now that we’re done with the main authentication flow, let’s talk about our app.

Our app have two major functionalities, give user money and also allow the user to spend the money.

app main function

The transact function carry out the business logic of our application, clients can only make a post request to this endpoint, sending along the action they want to perform which can be either “send” or “add”.

The focus here will be on the first four lines, we first retrieve the value of the “auth_token” cookie, we then pass that to the retrieve user endpoint, which retrieve the user whose session is associated with the given cookie, if no user is found, we return an Unauthorized response.

We can isolate the first four line as a standalone function and add it to protected endpoints in order to get and track the user hitting the endpoints.

Javascript function that communicate with our app. Note the credentials : “include” parameter which instruct fetch to send cookies along with the request.

Passwordless Authentication

Password-based authentication requires users to provide a secret during registration, but this method has significant drawbacks. Users frequently forget their passwords, and storing them insecurely can put them at risk of security breaches. As a result, many applications are turning to alternative methods, such as passwordless authentication, to enhance security and improve user experience.

What is passwordless authentication?

Passwordless authentication is a method of verifying a user’s identity without requiring a password. It is an alternative to traditional password based authentication method, which can be vulnerable to security threats such as hacking, phishing, and brute force attacks. Instead of passwords, it relies on alternative methods of verifying a user’s identity, such as biometric authentication (e.g., fingerprint or facial recognition), cryptographic keys, or one-time passwords (OTPs) sent via email or SMS.

Passwordless authentication flow with OTP.

1. User provides their email address during the registration process.
2. Server generates a unique token and sends it to the provided email address.
3. User receives the token via email and clicks on the verification link to be redirected to a verification page.
4. User enters the code sent to their email address on the verification page.
5. Server verifies the code entered by the user and grants access if the code is correct.
6. On subsequent logins, the user only needs to provide their email address to log in without a password.

We’ll modify the auth flow for our financial app to use passwordless auth.

@app.route("/register", methods=["POST", "GET"])
def register():
if request.method == "GET":
return render_template(
"login.html",
page="Registration Form",
action="Register",
alt="Already have an account",
alt_text="Login",
alt_route="/login",
)

email = request.form.get("email")
resp = send_otp(mail, email)
return resp

Here’s the modified register endpoint that retrieves the email address submitted by the client and calls the sent_otp function to generate and send a unique one-time password (OTP) to the provided email address:

def send_otp(mail, email):
token = "".join(random.sample(letters, 5))
msg = Message("Login Token", sender=email_address, recipients=[email])
msg.body = f"Your login token is {token}"
print(msg.body)

tokens[token] = email
mail.send(msg)
resp = make_response(redirect(url_for("verify_otp")))
return resp

The send_otp function generates a random token for the user, saves the token, and sends the generated token to the user’s email address. We also redirect the user to the verify_otp page so that they can input their OTP.

@app.route("/verify_otp", methods=["POST", "GET"])
def verify_otp():
if request.method == "GET":
return render_template(
"login.html",
page="Verify OTP",
action="Verify",
alt="Don't have an account",
alt_text="Register",
alt_route="/register",
)
otp = request.form.get("otp")

if not otp:
flash("OTP not provided")
return redirect(url_for("verify_otp"))

email = tokens.get(otp)

if not email:
flash("Invalid OTP provided")
return redirect(url_for("verify_otp"))

resp, id = login_user()
cookies[id] = email

if not database.get(email):
database[email] = {"balance": 400}
return resp

This is the core of our passwordless authentication flow. When a user sends a POST request to this endpoint, we extract the OTP they provide, verify that it exists and is valid. We then retrieve the associated email address, log in the user, and if the user does not already exist, we create their account.

Authentication is a crucial aspect of building any web application. In this write-up, we explored the basic authentication flows for both password-based and passwordless authentication.

For password-based authentication, we started with a user providing their email and password. For authentication we check if the provided password matched the one stored in the database. If it did, we logged the user in; otherwise, we returned an error message.

For passwordless authentication, we registered a user with their email address and sent them a unique one-time password (OTP) to their email. The user then entered the OTP on a verification page to gain access. On subsequent logins, the user only needed to provide their email address, and they could log in without a password.

While the examples provided here are straightforward, building a production-grade authentication system requires additional measures. We must validate user input thoroughly, collect more information about the user, store the collected information in a secure database, and provide more security checks such as Cross-Site Request Forgery (CSRF) protection for passwordless authentication.

In the upcoming article, get ready to dive deeper into the fascinating world of authentication as we explore how to implement Multi-factor Authentication and delve into the workings of Single Sign On (SSO). We’ll not only learn how it works but also discover how to implement our very own SSO with docker.

Don’t miss my next articles! Follow my Medium account to stay up-to-date on my latest content.

--

--

Adebayo Taiwo

Developer and writer, linux and python evangelist. Knowledge seeker, enjoys learning and implementing cool stuffs with code.