Circuits Of Imagination

An Architecture for Passwordless Authentication

31 Aug 2026

Background and Scope: Who is This For

This post describes an architecture for passwordless user authentication for consumer-facing web applications. To break that down a little more:

Passwordless: The goal is to allow the user to log into a web site without using or having to remember a password.

User: We are focused on human users as opposed to service accounts.

Authentication: This architecture is focused on Authenticating, i.e. verifying, a user’s identity. This is opposed to Authorization, i.e. defining what a user is and isn’t allowed to do.

Consumer-facing: This is your run of the mill website where, today, users register with a username, email, and password. Users login by providing a username and password and are able to reset their password by requesting a magic link sent to their inbox. MFA might or might not be a requirement.

Web application: The user is authenticating to a web application hosted on the internet, as opposed to a locally running app or logging into a personal device like your phone or computer.

This architecture is indented to describe a framework for authenticating users to a web application. This architecture doesn’t describe other facets of user management such as authorization or user roles. Neither does this architecture describe a full security architecture such as rate limiting, credential expiration or access and refresh token management. Although this architecture has all the components for MFA, we will focus on single factor authentication and will consider MFA out of scope.

Current State: Passwords Have Eaten the Web

Logging in with a username and password is a universally well understood and familiar UX pattern for users Passwords are portable, if a user gets a new device and needs to log into a service all they have to do is type in their username and password no matter what device they are using. Passwords have also been one of the weakest links in user security over the past 10+ years.

The Problems With Passwords

Password resets: Users regularly forget their passwords. The most common password reset mechanism is by sending a Magic Link to the email associated with the account. In effect, this pattern makes access to the email account a user credential. This is an Out-Of-Band Authenticator in NIST parlance. See: NIST SP 800-63 B Section 5.1.3.1.

The password is sent to the server: Sending the password to server means the server, and anyone who can control the server, has access to your raw password.

If I can try a bunch of guesses with the server letting me know if the password is correct or not, I might be able to guess your password.

If I can control the login page, I can get your password because you have to type it into the login page.

If I control the login action on the server, I can get your password.

If I can get on the path between you and the server, I can get your password because it is sent to the server. Less of an issue today thanks to TLS, but still feasible in some cases.

The server stores the hashed password, along with the passwords for all the other users. This means if I can get a copy of that data, I have a fair chance at cracking the password of at least a few users.

Passwords are hard to remember: As a result people tend to reuse their passwords or an easy to recognize password pattern. This means for a given user if their password on service A is compromised, there is a good chance you will be able guess their password on service B.

The two issues of services having direct access to a user’s password, and users reusing their passwords, combine to make passwords particularly easy to compromise. As a result the Information Security world has been looking for an alternative to passwords for over a decade, or at the very least augment password with things like Multi-Factor Authentication (MFA) and WebAuthN.

WebAuthN, Passkeys and Why They Suck

First for some definitions:

  • Public Key Credential: A public key that is associated with an account. A user authenticates by proving access to the private key associated with the public key credential.
  • WebAuthN: Is a browser API for creating and utilizing a public key credential.
  • Passkey: A public key credential that is compatible with WebAuthN.

The Problems with Passkeys

  • Passkeys are tied to a device, if you lose your device you lose your credentials
  • Passkeys can’t be transferred across devices. If you need to login with a new device, you might need to access to your old device.
  • Passkeys registration and authentication is a little more complex, from a technical perspective, than password based authentication.
  • Users are used to passwords, and people like what is familiar.

The Architecture

One User Multiple Credentials

When designing an authentication system focused on single user password based logins, the most straight forward approach is to have a users table. This users table will generally have one column for the password hash and another column for the user’s email address. The main purpose of the email field is to sometimes serve as the username, and mainly to store the email to send a password resent link to when the user forgets their password. This is a relatively simplistic and naive implementation, but is in fact how many web authentication systems are designed.

A more nimble, but more complex, approach is to have multiple credentials of different types associated with a user. Password, MFA tokens and passkeys all become credentials associated with a user. Once we have different credentials of different types associated to a user, we can ditch the password and let the user authenticate with any of the valid credentials.

The Entities

A User

This is the typical “user” entity found in most web applications. In this context a user is an entity that can be authenticated.

classDiagram
    class User {
        +id ID
    }

A Credential

A credential is an authentication mechanism and associated data. For example a passkey credential and the associated public key, or a password credential and the hashed password. A user’s credentials should be unique, as in a single user should not have two credentials of the same type and authentication data. For example a user shouldn’t have two passkey credentials with the same public key.

For our use case we are going to define two types of credentials:

  1. A Passkey Credential: A passkey credential contains an ID and a public key. Since passkey public keys aren’t made for human readability, we will also attach a description to the Passkey credential to help user track with Passkey is associated with which device.
  2. An Email Credential: An email credentials contains an ID and a verified email address.
classDiagram
    class Credential {
        +id ID
        +User user
        +boolean valid
    }
    class PasskeyCredential {
        +PublicKey publicKey
        +String description
    }
    class EmailCredential {
        +String emailAddress
    }
    Credential <|-- EmailCredential
    Credential <|-- PasskeyCredential

An Authentication Challenge

Whenever a user wants to authenticate to the app, the user needs to provide the correct response to a given challenge. For example when authenticating using a Passkey the user needs to respond to the challenge by siging the challenge with the private key associated with the passkey proving access to the private key. When attempting to authenticate via email, the user needs to provide the secret code sent to the email proving access to the email address. In this second case the secret code is part of the challenge.

classDiagram
    class AuthenticationChallenge {
        +id ID
        +Timestamp expiration
        +Credential credential
    }
    class MagicLinkChallenge {
        +String nonce
    }
    class PasskeyChallenge {
        +String nonce
    }
    AuthenticationChallenge <|-- MagicLinkChallenge
    AuthenticationChallenge <|-- PasskeyChallenge

The Flows

Registration

The registration flow is very similar to the typical user registration flow for any password-based authentication system. The main difference is instead of submitting an email and password, the user submits and email and a passkey public key credential.

During registration the user submits the information defining the user, including the initial credential data. For this design we will assume two credentials are provided on registration:

  • An email credential
  • A passkey credential

In our registration flow, we will not consider registration complete until the user has validated the email address the submitted.

sequenceDiagram
    actor User
    participant Server
    participant DB@{ "type": "database", "alias": "User Database"}
    participant EmailServer@{ "alias": "Email Server"}
    User->>Server: Registration data including public key and email address
    activate Server
    Server->>DB: Store user data and credentials
    Server->>EmailServer: Send email address verification
    Server->>User: Registration confirmation
    Note right of Server: All credentials are invalid since registration is not yet complete
    deactivate Server
    User->>Server: Confirms email address
    activate Server
    Server->>User: Registration complete
    Note right of Server: Credentials are valid and user can login
    deactivate Server

Email Verification

The email verification flow is used to validate that the user has access to the email address provided. This flow is used during registration to establish the initial Email Credential and any time the user wants to modify, or add, an Email Credential.

sequenceDiagram
    actor User
    participant Server
    participant DB@{ "type": "database", "alias": "Database"}
    participant EmailServer@{ "alias": "Email Server"}
    User->>Server: New Email Credential data
    activate Server
    Server->>DB: Store email credential data. Credential is invalid.
    Note right of Server: New Email Credential is still invalid util user verifies email. 
    Server->>EmailServer: Send email address verification
    Server->>User: Success message
    deactivate Server
    User->>Server: Confirms email address by clicking on the link
    activate Server
    Server->>DB: Update Email Credential and mark it as valid.
    Server->>User: Email verified
    deactivate Server

Passkey Authentication

This would be the standard WebAuthN Authentication Flow.

sequenceDiagram
    actor User
    participant Server
    participant DB@{ "type": "database", "alias": "User Database"}
    User->>Server: Requests login with Passkey
    activate Server
    Server->>DB: Create Passkey Challenge
    DB->>Server: Challenge created
    Server->>User: Challenge nonce
    User->>Server: Signed challenge response
    Server->>DB: Get associated Passkey Challenge
    alt Challenge and Signature Valid
        Server->>DB: Set challenge as invalid since it was already used
    	Server->>User: Login Successful
    else Challenge or signature invalid
    	Server->>User: Login Unsuccessful
    end
    deactivate Server

Allows the user to login using the registered email as a credential. When sending the link to the user the link should contain the nonce, and the server will lookup the challenge using the nonce. If the challenge is found and valid, the user can be authenticated. If the nonce is not found, or the challenge associated with the nonce is no longer valid, authentication is denied and the user is provided an error message.

User enumeration is an important consideration here, especially when responding to the user after the Magic Link is requested. Most applications will respond to that request letting the requester know to either check their email address, or that the email address was invalid. This type of message easily allows an attacker to determine if a given email address has an account on the app or not. Instead apps should respond with a generic message letting the user know that if their email address was valid a magic link will be sent, and providing the same response when requesting a magic link regardless of whether the address was valid or not.

sequenceDiagram
    actor User
    participant Server
    participant DB@{ "type": "database", "alias": "User Database"}
    participant EmailServer@{ "alias": "Email Server"}
    User->>Server: Requests login with Magic Link
    activate Server
    Server->>DB: Create Magic Link Challenge
    DB->>Server: Challenge created
    Server->>EmailServer: Send magic link with nonce
    Server->>User: Notify user to check email
    deactivate Server
    User->>Server: Clicks Link
    activate Server
    Server->>DB: Retrieve Magic Link Challenge
    DB->>Server: Provide Magic Link Challenge
    alt Link valid
        Server->>DB: Set challenge as invalid since it was already used
    	Server->>User: Login Successful
    else Link invalid
    	Server->>User: Login Unsuccessful
    end
    deactivate Server

Add New Passkey Credential

Users need to edit their credentials. For passkeys there are two main operations: Delete a credential, which is a simple operation we won’t explicitly spell out; And adding a new credential which is outline here. This is essentially the WebAuthN User-Verifying Authenticator process.

sequenceDiagram
    actor User
    participant Server
    participant DB@{ "type": "database", "alias": "User Database"}
    User->>Server: Requests adding a new Passkey Credential
    activate Server
    Note right of Server: Validate that the user is already authenticated
    Server->>DB: Create Passkey Challenge
    DB->>Server: Challenge created
    Server->>User: Challenge nonce + Creation options
    User->>Server: Signed challenge response + Public Key
    Server->>DB: Get associated Passkey Challenge
    alt Challenge and Signature Valid
        Server->>DB: Set challenge as invalid since it was already used
        Server->>DB: Store new validated Passkey Credential
    	Server->>User: New credential added
    else Challenge or signature invalid
    	Server->>User: Error adding credential
    end
    deactivate Server

User Journeys

Since we have the basic entities and flows defined, lets take a look and see how they fit together from a user’s perspective.

New User Registration

Registering as a new user follows the registration flow above. Part of the user registration process will be following the WebAuthN User-Verifying Authenticator registration flow to create the appropriate Public Key credential.

After the user submits the registration data to the server, the server will send an email to the user for them to verify their email address. Registration is not considered complete until a user has verified their email address because a valid email address credential is required when logging in from a new device.

One major issue to consider is how the system should react if a user loses their email address verification message. The system will need a way for the user to recover from that case. One option is to delete the incomplete users data if their email address is not verified within a given time frame. This will allow the user to re-attempt registration after a given period if they lose their verification message.

Another option is to allow a user to re-register using the same email address if the email address hasn’t been verified yet, including send a new email verification message. However this approach opens up the door for a timing attack where a user registers with an email address and a given passkey, and then an attacker immediately registers again with the same email address but using a passkey under the attackers control before the user has confirmed their email address. An unaware user might complete the registration process not knowing they just added an attacker-controlled credential to their account.

User Login

After a user has completed the registration process, the next thing they would want to do is login. In this case we are going to assume the user is going to login with the Passkey credential they created during registration. In this case the user would simply follow the passkey authentication flow above.

User Login from New Device

Now the user wants to login using a new device, or in other words they want to login without access to any valid passkey credentials. This is where the magic link authentication flow comes into play. The working assumption here is that the user has access to their email from the device they want to login from. The user would use the magic link authentication flow to login, and then use add new passkey flow to add a new Passkey Credential that is accessible from their new device.

Account Recovery

Account recovery follows the same process as logging in from a new device. If a user losses access to all their Passkey devices, they can still log in using the magic link authentication flow and add a new credential for their new device. If the user loses access to all of their devices and their email account, the user would loose access to their account. This would be no different from a user forgetting their password and losing access to their email account.

Removing a Passkey Credential

This is a simple operation that needs to be provided to the user. When a user no longer controls a device they will want to remove the passkey associated with that device. This is a simple operation that doesn’t need a separate flow diagram.

Why All This

Passwords continue to be a weak link in the web security framework. Breach after breach dumps millions of users passwords every couple of months. The architecture described above isn’t just about using WebAuthN and Passkeys, it’s about creating a versatile framework to support different authentication mechanisms allowing us to move beyond passwords to more secure login options. Removing passwords from your authentication flow removes whole classes of vulnerabilities that you will no longer have to deal with. Passkeys and WebAuthN have their own set of security and usability issues, but they have proven to be much more manageable compared to passwords.

To dive into the details of Passkeys and WebAuthN check out webauthn.io for language specific libraries and tutorials, and the W3C Web Authentication Standard for the full specifications.

Glossary

Passkey
A Private and Public key pair used for authenticating a user to a service.
WebAuthN
A standard and API for accessing and authenticating using Passkeys.
MFA
Multi-Factor Authentication - a security process that requires users to provide two or more distinct forms of verification to confirm their identity.
NIST
National Institute of Standards and Technology.
OOB
Out Of Bounds - Utilizing a process that is outside the scope of the current operation.