Introduction
Passwords are a thing of the past. They are insecure, hard to remember, and a pain to manage. Fortunately, there’s a better way: passwordless authentication with WebAuthn. In this article, I’ll show you how to build a simple, secure, and passwordless authentication system using WebAuthn and Node.js.
What is WebAuthn?
WebAuthn is a web standard for secure authentication that uses public-key cryptography to protect users from phishing attacks. Instead of a password, users can authenticate with a fingerprint, a security key, or other authenticators.
The Tech Stack: Node.js and Express
For this project, I used the following technologies:
- Node.js: A popular and powerful JavaScript runtime.
- Express: A minimal and flexible Node.js web application framework.
Architecture and Implementation
The core of the WebAuthn authentication system is a server that can:
- Register a user’s public key.
- Verify a user’s signature against their public key.
sequenceDiagram
participant User
participant Browser
participant Server
participant Authenticator
Note over User,Authenticator: Registration Flow
User->>Browser: Click "Register"
Browser->>Server: Request challenge
Server-->>Browser: Challenge + User ID
Browser->>Authenticator: Create credential
Authenticator->>User: Prompt (fingerprint/PIN)
User-->>Authenticator: Authenticate
Authenticator-->>Browser: Public key + Signature
Browser->>Server: POST /register
Server->>Server: Store public key
Server-->>Browser: Success
Note over User,Authenticator: Authentication Flow
User->>Browser: Click "Login"
Browser->>Server: Request challenge
Server-->>Browser: Challenge
Browser->>Authenticator: Sign challenge
Authenticator->>User: Prompt (fingerprint/PIN)
User-->>Authenticator: Authenticate
Authenticator-->>Browser: Signature
Browser->>Server: POST /authenticate
Server->>Server: Verify with public key
Server-->>Browser: Authentication successful
Server Implementation
The server is a simple Express application with two endpoints: /register and /authenticate.
The /register Endpoint
The /register endpoint is responsible for storing the user’s public key. When a user registers, they send their user ID and public key to the server. The server then stores the public key in a database (in this example, we’re using an in-memory object for simplicity).
app.post('/register', (req, res) => {
const { userId, publicKey } = req.body;
const publicKeyPem = `-----BEGIN PUBLIC KEY-----\n${publicKey}\n-----END PUBLIC KEY-----`;
userPublicKeys[userId] = publicKeyPem;
res.send('Registration successful');
});
The /authenticate Endpoint
The /authenticate endpoint is responsible for verifying a user’s signature. When a user wants to authenticate, they sign a piece of data with their private key and send the signature to the server. The server then uses the user’s public key to verify the signature.
app.post('/authenticate', (request, response) => {
const { userId, signature, originalData } = request.body;
const publicKey = userPublicKeys[userId];
const originalDataBuffer = Buffer.from(originalData, 'base64');
const verifier = crypto.createVerify('SHA256');
verifier.update(originalDataBuffer);
const signatureBuffer = Buffer.from(signature, 'base64');
const isValid = verifier.verify(
{
key: publicKey,
padding: crypto.constants.RSA_PKCS1_PSS_PADDING,
saltLength: 32,
},
signatureBuffer
);
if (isValid) {
response.send('Authentication successful');
} else {
response.status(401).send('Authentication failed');
}
});
Conclusion
WebAuthn is a powerful and secure alternative to passwords. By using WebAuthn, you can provide your users with a seamless and secure authentication experience. This project demonstrates how easy it is to implement a simple WebAuthn server using Node.js and Express.
Tags