Designing a Passwordless Authentication System
Building a Passwordless Authentication System in FastAPI: A Journey from Concept to Implementation
If you're completely new to building a passwordless authentication system, how would you approach it? This is a great system design question, and the answer depends on your level of expertise. As someone who enjoys solving problems, here's how I tackled it.
Breaking Down the Problem
Usually, you draw down what you think you need for the system to work. The more technical term is deriving the functional and non-functional requirements. Too fancy for me, I'd say. Three boxes: an interface to receive information in the first box, another box to make sense of the information and process to produce the last box, which should result in something that would essentially mean we have achieved what we set out to do.
Applying this to a passwordless authentication system makes things clearer. Let's dive into each box.
The First Box: Receiving Information
Take this into the context of building an authenticator system, things become a bit easier. If it's the first box I have to consider, what kind of information do I really need there? Something that doesn't include the punching in of passwords would be the first. Clearly stating what I wanted to do in the first interface helps, and I drew inspiration from current solutions even though I was attempting this as if I never understood the inner workings of such a system. Either go with emails or phone numbers. The latter seemed too far off because I would have to consider how I would reach out to phone numbers from users. Not a chance. But emails are the best. A simple SMTP server, and you're basically good to go.
The Second Box: Processing the Information
Now I have to think about what to send to users after taking their very souls from them (emails). Should I send them a message that would help me verify who they are? Are there novice ways to implement? What is the simplest way to approach this without making the solution over-engineered, verbose, or even highly abstracted to look simple? The goal is to verify the user's identity without relying on passwords.
Generating a One-Time Password (OTP)
After collecting the user's email, the system generates a short-lived 6-digit code and sends it to the user's email. The OTP is valid for a limited time (e.g., 5-10 minutes) and can only be used once.
Having this in simple terms means you can have your existing model representation for the user, and you would hypothetically add these new fields: is_verified
, verification_code
, and verification_code_expires_at
to track whether the user was able to verify their account before the timedelta.
class User:
id = Column(UUID, ...)
email = Column(EmailString, ...)
is_verified = Column(Boolean, ...)
verification_code = Column(Integer, ...)
verification_code_expires_at = Column(TimeStamp, ...)
How OTP Works
When a user enters their email, the system generates an OTP, stores it securely (e.g., in a database with a timestamp), and sends it to the user's email. The user then enters the OTP into the application, and the system verifies it against the stored value and timestamp.
Example Flow:

Security Considerations
- OTP Expiry: OTPs should expire after a short period to minimize the risk of misuse.
- Rate Limiting: Prevent abuse by limiting the number of OTP requests a user can make within a specific time frame.
- Secure Storage: OTPs should be hashed before storing them in the database to protect against data breaches. Teams don't go for this option because it is not needed most of the time.
What about Logging In? We just update the User Model again. This is how the flow looks like.

The Third Box: The Result
Upon each successful registration, we just set the verification fields to None
to compensate and set is_verified
to True
. As long as the user is verified in the system database, the user is granted a token to access routes and services within the system, and everyone gets to live happily ever after. Seems very simple to reason about.
The result is a seamless, passwordless authentication experience that is both secure and user-friendly.
But bruh... instead of having one field for passwords, we have extra fields just to circumvent this issue. And I say this to it: I do not know the day someone is going to break your encryption algorithm you use to hash your password (if you even do that, you nasty programmer), but I'm certain when that happens, I won't be in a bad position as you would be when that time comes.
Final Thoughts
Building a passwordless authentication system was somehow rewarding. By breaking down the problem into smaller, manageable parts, I was able to create a solution that is secure, user-friendly, and easy to understand. The current solution sits in my FastAPI Server Template Repo, and the simplicity and flexibility of the framework made things super easier to reinvent.
FastAPI Server Template Github
Read More HereIf you're interested in implementing something similar, I encourage you to start small, experiment, and iterate. And remember, the key to a great system is not just how it works but how well it solves the problem for the end user.