A Young Man Presenting His ID to a Club Bouncer

Authentication: A Conceptual Guide for Software Engineers

AuthenticationAuthorization
Published

What prompted this blog?

Authentication is something I set up on every system I work on. I typically use Better Auth for this because of the smooth developer experience and comprehensive documentation. However, I noticed that I find myself just copy-pasting the code provided by the documentation without wondering what happens under the hood.

On one hand, I find this to be a benefit. The framework provides an abstraction that takes care of the low-level details, allowing me to focus on building the application.

Better Auth Meme

On the other hand, a lack of understanding these details makes it hard to debug issues if they arise in development, or worse, production. If a user is unable to log in, is it because I integrated the library wrong or did my application code mishandle something downstream?

So, I decided to take a peek behind the veil of what these authentication libraries/frameworks do to better develop my mental model for authentication.

What is authentication?

To put it succinctly, authentication seeks to answer the question "Who are you?", which comes in to stages:

  1. Identification - Claiming who you are (e.g. enter an email address)
  2. Verification - Proving your claim (e.g. enter the correct password for that email address)

Why care about this distinction? Because different attacks target different stages and, consequently, have different defenses! For example:

  • Credential stuffing, where leaked credentials are used by attackers, targets identification as the attacker knows the claim and proof (email and password).
  • Brute force attacks, where an attacker tries many passwords against a known account, targets verification as the attacker only knows the claim but not the proof, so they try to guess it.

How does this compare to authorization?

While authentication lets you know who a user is, authorization lets you know what that user is allowed to do. Authentication comes first since you can't decide what a user is allowed to do without first identifying who they are. However, it's important to note that authentication and authorization can fail independently!

For example, a system can have completely functional authentication but broken authorization. What would this look like? A valid user can log in correctly, while attackers can't, but they can access the data of another user. In other words, the login works fine but access control is failing.

The opposite is also possible. You could have airtight role checks, but if your session tokens are easy to steal or leak, an attacker can impersonate a legitimate user and perform destructive actions

What do we mean by multi-factor authentication?

Every authentication mechanism falls into one of the following categories, which we call factors:

Factor

What it means

Examples

Knowledge

Something you know

Password, PIN, Security Questions

Possession

Something you have

Email, Phone Number, Hardware Key

Inherence

Something you are

Fingerprint, Face Scan

The reason we care about this distinction is because each one identifies it's vulnerable to:

  • Knowledge - Phishing or leaked in a database breach
  • Possession - Can be stolen or swapped
  • Inherence - Can't be rotated if compromised (you can't change your fingerprint!)

Multi-factor authentication simply refers to an authentication system that employs at least two mechanisms from different factors. Think of Google, which asks for a password (knowledge) and then a code sent to your email (possession) or a bank mobile application that requires a PIN (knowledge) and an OTP (possession).

By employing multiple factors of authentication, we improve security because compromising an account requires compromising multiple authentication mechanisms. Say that your bank application's database got compromised, leaking your PIN. Even with this information, attackers won't be able to get into your account because they'd still need the OTP sent to your phone. Although this isn't impossible, it makes it much more difficult to pull off since they need to carry out multiple types of attacks (leak database for password, steal phone for OTP).

Does two passwords count as MFA?

You may think that an authentication system that employs two passwords or a password and a security question falls under multi-factor authentication (this is what I thought at least). However, this is not the case!

Although multiple authentication mechanisms are used (i.e. password + password, password + PIN), they fall under the same factor (knowledge). Multi-factor authentication requires the mechanisms to be from different factors.

What happens if an authentication system functions like this? An attacker can compromise both mechanisms simultaneously with a single attack! A phishing attack, or a database leak, can reveal your two passwords or password and security question.

How do I think about the authentication stack?

While learning more about authentication, I started to develop this mental model around it. It splits authentication into four distinct layers:

Layer

Function

Example

L4: Access Control

Allow/deny operations based on business rules

RBAC, ABAC

L3: Session Management

Maintain authentication state across requests

Cookies, JWT's

L2: Credential Verification

Proving identity claims

Password Hashing

L1: Transport Security

Securing data in transit via encryption

HTTPS, TLS

In this stack, a layer can only be functional if the layers below it are functional as well:

  • L2 without L1: Passwords are on the server, but they travel in plaintext over HTTP. An attacker on the network can intercept your request and read your password before it reaches your server for hashing.
  • L3 without L2: Your system can issue sessions without a problem, but it never verifies the credentials of the user requesting a session. Anyone who claims to be [email protected]` gets a valid session.
  • L4 without L3: Your role checks are airtight and properly implement business rules, but sessions can be easily stolen via attacks like XSS. To your system, the attacker is an admin.

On top of helping me consolidate authentication conceptually, I find this model practically useful when integrating auth libraries.

Layer

Responsibility

Handler

L4: Access Control

Route protection, role checks, permission checks for specific resources

Application code

L3: Session Management

Session creation, cookie management, issuance and refreshing of tokens

Auth library

L2: Credential Verification

Password hashing, OAuth token exchange

Auth library

L1: Transport Security

HTTPS, TLS certificates

Hosting platform/infrastructure (e.g. Coolify, Vercel, Cloudflare)

I used to think that an auth library took care of everything. How I think about it now is that these auth libraries just handle layers 2 and 3. Layer 1 is handled by your infrastructure and layer 4 is handled by your business logic, implemented via your application code.

For example, a library might expose session.user.role via its API, but it doesn't enforce what routes that role can access or what operations that role can perform because it's your business logic that gives these roles meaning:

middleware.ts
1// The library gives you the identity (L2-L3)
2const session = await auth.getSession(request);
3
4// Enforcement is YOUR code (L4)
5if (session.user.role != "admin") {
6 return new Response("Forbidden", { status: 403 });
7}

How to debug authentication issues with this mental model?

I mentioned earlier that one of the reasons why I wanted to dive deeper into authentication was to figure out where to look when things go wrong. The mental model helps with this! Here are some examples:

What's the problem?

What layer failed?

How to fix?

User A accessed User B's data

Access control (L4).

Authentication worked as User A was on a valid session, but the system failed to check what he was trying to access.

Enforce role/permission checks in the application code.

An attacker logged in as our system administrator

Credential verification (L2).

The attacker input valid credentials, which may have been obtained via phishing or a leak.

Employ MFA by adding a second factor (e.g. face scan or OTP via SMS).

User sessions are being hijacked without any login activity

Session management (L3).
Since no login activity was recorded, the attacker must have stolen a valid user session cookie.

Enforce stronger cookie configurations.

Prior to this consolidation, each diagnosis would lead me down a rabbit whole trying to figure out where the issues were coming from. Now, each diagnosis maps to a layer, which points me in the direction of the fix.

What's the tldr?

Authentication is the process of identifying a user based on the claims they put forward and the proof behind them. This is distinct from authorization, which is the process of determining what kind of access an identified user has.

Authentication mechanisms come in three factors: knowledge (something you know), possession (something you have), and inherence (something you are). Multi-factor authentication systems employ authentication mechanisms from multiple distinct factors to prevent any single attack from compromising a user's account.

The mental model for the authentication stack consists of four layers: transport security, credential verification, session management, and access control. Auth libraries like Better Auth or Clerk handle layers two and three while layers one and four are to be handled by the hosting platform and the application code, respectively. Thinking of authentication with this model has helped me quickly identify potential fixes for diagnosed issues.


Last updated

Share


Like