News Froggy
newsfroggy
HomeTechReviewProgrammingGamesHow ToAboutContacts
newsfroggy

Your daily source for the latest technology news, startup insights, and innovation trends.

More

  • About Us
  • Contact
  • Privacy Policy
  • Terms of Service

Categories

  • Tech
  • Review
  • Programming
  • Games
  • How To

© 2026 News Froggy. All rights reserved.

TwitterFacebook
Programming

Building Passkey Authentication with WebAuthn, Next.js, and NestJS

Password-based authentication has long been the default for digital identity, yet it presents a myriad of challenges for both users and developers. Users grapple with remembering complex passwords, often resorting to

PublishedOctober 28, 2026
Reading Time9 min
Building Passkey Authentication with WebAuthn, Next.js, and NestJS

Password-based authentication has long been the default for digital identity, yet it presents a myriad of challenges for both users and developers. Users grapple with remembering complex passwords, often resorting to reuse, which significantly increases the risk of credential stuffing and phishing attacks. Developers, on the other hand, bear the burden of securely storing hashed and salted passwords, implementing rate limiting, and building forgotten password flows that are rarely user-friendly.

Fortunately, passkeys emerge as a robust solution to these longstanding issues. Instead of relying on a shared secret, passkeys enable users to verify their identity using device-native authenticators like fingerprints, facial recognition, or a PIN. The core principle is that a cryptographic key, unique to the device, signs a challenge and is never transmitted to the service provider. This approach addresses many of the inherent vulnerabilities of traditional passwords, offering a more secure and frictionless user experience.

This guide will walk you through the foundational steps of implementing passkey authentication using WebAuthn, a NestJS backend, and a Next.js frontend. We'll leverage the SimpleWebAuthn TypeScript library to streamline the cryptographic complexities, focusing on the server-side setup for generating passkey registration options.

The Fundamental Flaws of Password-Based Authentication

Before diving into passkeys, it's crucial to understand the limitations they aim to overcome:

  1. Phishing Attacks: Passwords are vulnerable to phishing because they work wherever they are entered. A convincing fake login page can trick users into revealing their credentials, which then function identically on the legitimate site.
  2. Password Reuse: The sheer number of online accounts forces users to reuse passwords. A data breach on one site can lead to credential stuffing attacks across many others, compromising unrelated accounts.
  3. Data Breaches: Even with best practices like salting and hashing, server-side storage of password derivatives remains a target. Weak password hashes can still be cracked offline, making any database leak a critical event for user security.
  4. Lack of Privacy: Users share a secret with every service, relying on each to protect it. Breaches can reveal patterns of user behavior across services, impacting privacy.
  5. Credential Fatigue: The constant demand for unique, strong passwords, coupled with forced resets and verification codes, leads to user frustration. This often results in insecure behaviors and increased support costs for businesses.

How Passkeys Revolutionize Authentication

Passkeys eliminate the shared secret entirely, offering a superior alternative:

  • Phishing Resistance: Passkeys are cryptographically bound to the specific domain (Relying Party ID) that created them. The browser and operating system will refuse to use a passkey on any other domain, regardless of how authentic a phishing site appears.
  • Breach Resistance: Only a public key is stored on the server. If a database is compromised, attackers gain nothing that can be used to sign in.
  • No Password Reuse: Each passkey generates a new, unique key pair for a single site, making password reuse impossible.
  • Enhanced Privacy: No secret is shared with the site, and individual keys differ across services. Sensitive biometric data (fingerprints, face scans) never leaves the user's device.
  • Eliminates Fatigue: There's nothing to create or remember. Users authenticate with familiar device unlock methods like biometrics or a PIN, simplifying the login process.

This paradigm shift moves the burden of security enforcement from the user to the browser and operating system, allowing users to simply confirm their identity.

Understanding Passkeys and WebAuthn

A passkey is a credential built upon public key cryptography, standardized by the Web Authentication API (WebAuthn). When a passkey is created, your device generates a unique key pair: a private key that remains securely on the device, protected by biometrics or a PIN, and a public key sent to the website for storage alongside your account.

The term "passkey" is an industry-friendly name for WebAuthn credentials that can synchronize across devices. It's important to note that your application does not select the verification method (fingerprint, face, or PIN); the operating system determines this based on device capabilities and user settings. Key concepts in WebAuthn include:

  • Relying Party ID (RP ID): This is the domain to which your passkeys belong (e.g., example.com). In development, it's typically localhost. This binding is fundamental to passkeys' phishing resistance.
  • User Verification (UV) Flag: A crucial bit within the signed response, indicating that the user genuinely performed a fingerprint, face, or screen lock check. Your server validates this flag to ensure legitimate user interaction.

The Passkey Authentication Flow

Both the sign-up (registration) and sign-in (authentication) processes follow a consistent three-step pattern:

  1. The server generates a cryptographic challenge and sends it, along with relevant options, to the browser.
  2. The browser then prompts the user's device to verify their identity and cryptographically sign the challenge using their private key.
  3. The browser returns the signed response to the server, which then validates it using the public key it holds.

During registration, the device generates a new key pair, and the server stores the public key. During authentication, the device signs a fresh challenge with an existing private key, and the server verifies this signature with the previously stored public key.

Setting Up the NestJS Backend for Passkey Registration

Our project structure will consist of a client/ directory for the Next.js frontend and a server/ directory for the NestJS API. We'll focus on the NestJS backend.

Prerequisites and Initial Setup

To get started, you'll need Node.js (v22+), PostgreSQL, and a device capable of supporting passkeys (e.g., Mac with Touch ID, Windows with Hello, or a modern smartphone). Familiarity with TypeScript and basic NestJS concepts will be beneficial.

Begin by creating a new NestJS project: bash npx @nestjs/cli new server cd server

Next, install the necessary dependencies for WebAuthn, database interaction, configuration, and JWTs: bash npm install @simplewebauthn/server @nestjs/typeorm typeorm pg @nestjs/config @nestjs/jwt @nestjs/passport passport passport-jwt

Database Configuration and Connection

First, create an empty PostgreSQL database named auth. You can do this from the terminal: bash createdb -h localhost -U postgres auth

Define your environment variables in a .env file within the server directory: bash JWT_SECRET=replace-with-a-long-random-string WEBAUTHN_RP_ID=localhost WEBAUTHN_ORIGIN=http://localhost:3000 DB_HOST=localhost DB_PORT=5432 DB_USERNAME=postgres DB_PASSWORD=your-database-password DB_NAME=auth

WEBAUTHN_RP_ID is crucial for phishing resistance, specifying the domain without protocol or port, while WEBAUTHN_ORIGIN is the exact frontend URL from which requests are expected. Browsers only permit WebAuthn on HTTPS or localhost.

Configure your database and JWT settings in src/auth/config/database.config.ts and src/auth/config/jwt.config.ts, respectively. Then, integrate them into src/app.module.ts, ensuring TypeORM uses synchronize for development convenience (though migrations are recommended for production). Finally, enable CORS in src/main.ts to allow your frontend on port 3000 to communicate with the API on port 4000. For production, restrict origin: true to your frontend's specific URL.

Defining Database Entities for Users and Passkeys

We need two primary entities: User to represent the account and Authenticator to store individual passkeys. One user can be associated with multiple authenticators. Create src/auth/entities/auth.entity.ts:

The User entity will include id (UUID), email (unique), webauthnId (a unique, random identifier for WebAuthn, not PII, stored as base64url), and currentChallenge (a temporary field to store the challenge awaiting verification). The Authenticator entity will store credentialId (the passkey's unique ID), credentialPublicKey (the public key received from the device), webauthnUserId, counter (for detecting cloned devices), deviceType, backedUp, and transports (how the authenticator is accessed).

Notably, webauthnId is a random, base64url-encoded string derived from 32 random bytes, adhering to the WebAuthn specification's recommendation against using personal information as the userID. The currentChallenge field is vital for security; it holds the server-generated challenge that the device must sign, preventing replay attacks. It's set before sending options to the client and cleared after successful verification.

Generating Passkey Registration Options on the Server

Within src/auth/auth.service.ts, we'll implement the logic to generate registration options using SimpleWebAuthn. The AuthService leverages injected repositories for User and Authenticator entities, along with JwtService and ConfigService for managing environment variables and tokens.

The generateRegistrationOptions method takes a user's email. It first attempts to find an existing user. If a user doesn't exist, a new User record is created, assigning a unique webauthnId generated from random bytes. If the user already has authenticators registered, a ConflictException is thrown, indicating that sign-up with an existing email should lead to sign-in.

Crucially, this method calls SimpleWebAuthn's generateRegistrationOptions function. This function takes several parameters:

  • rpName: A human-readable name for your application, displayed to the user.
  • rpID: The Relying Party ID, typically your domain.
  • userID: The unique, non-PII webauthnId of the user.
  • userName: The user's email, for display purposes.
  • attestationType: Set to 'none' for common passkey implementations.
  • authenticatorSelection: Configured to residentKey: 'required' (ensuring the passkey is discoverable without an explicit username) and userVerification: 'required' (mandating user interaction like biometrics).
  • excludeCredentials: A list of existing credential IDs to prevent users from registering the same passkey multiple times.

After generating these options, the method saves the resulting options.challenge to the user's currentChallenge field in the database. This step is critical because the server needs to verify this exact challenge when the signed response comes back from the client. The generated options are then returned to the frontend.

This setup provides the essential server-side foundation for initiating the passkey registration process, ensuring adherence to WebAuthn standards and incorporating initial security considerations.

FAQ

Q: Why do passkeys address phishing better than passwords?

A: Passkeys are cryptographically bound to the Relying Party ID (RP ID), which is your legitimate domain. The browser and operating system only permit a passkey to be used on the exact domain it was registered with. This means even a pixel-perfect phishing site on a different domain cannot trick the browser into using the passkey, making phishing ineffective.

Q: What is the purpose of currentChallenge in the User entity?

A: The currentChallenge stores a cryptographically secure, random string generated by the server for each passkey registration or authentication attempt. This challenge is then signed by the user's device and sent back. The server verifies that the received challenge matches the stored currentChallenge, preventing replay attacks where an attacker might try to reuse a previously signed authentication response. It's cleared after a successful verification.

Q: Why should the webauthnId be a random string instead of the user's email address?

A: The WebAuthn specification advises against using personally identifiable information (PII) like an email address for the userID (which corresponds to webauthnId on the server). Using a random, unique identifier enhances user privacy by decoupling the identifier stored with the passkey on the device from their real-world identity, even if the credential details were exposed in a client-side leak.

#programming#freeCodeCamp#authentication#passwordless authentication#passkeys#nestjsMore

Related articles

Java's Enduring Foundations: Interconnectedness, Reliability, and
Programming
Stack Overflow BlogOct 28

Java's Enduring Foundations: Interconnectedness, Reliability, and

Java's origins, driven by creator James Gosling at Sun Microsystems, addressed critical challenges in early computing. The language tackled significant 'networking friction' to enable seamless interconnectedness across diverse systems. Simultaneously, the 'stringent reliability requirements' of consumer electronics inspired the robust design of the Java Virtual Machine (JVM) and its evolving garbage collectors, crucial for 'large-scale concurrent memory management.'

Under the Hood: How Real-Time Noise Cancellation Works
Programming
freeCodeCampOct 26

Under the Hood: How Real-Time Noise Cancellation Works

As software developers, we often find ourselves in environments far from ideal for focused work – be it a bustling open-plan office, a noisy commute, or a long-haul flight. In these situations, noise-cancelling

Microsoft Comms Chief Frank Shaw Reflects on Nearly 3 Decades
Tech
GeekWireOct 25

Microsoft Comms Chief Frank Shaw Reflects on Nearly 3 Decades

Frank X. Shaw, Microsoft’s longtime communications chief, is departing at year-end after nearly three decades. In an exit interview, he reflected on working with Gates, Ballmer, and Nadella, admitted mistakes like the Kin phone launch, and shared lessons on dealing with reporters. Shaw also discussed the evolution of communications in the AI era and his plans for time off before his next move.

Programming
Stack Overflow BlogOct 23

Uncovering Undocumented Behavior: The Power of Characterization Tests

Here's a common, often uncomfortable truth about software that's been in production for a few years: your existing regression tests likely describe what the system should do, based on someone's initial intentions. They

Life Is Strange Creator's High Praise for Amazon TV Show: A Hella
Games
PolygonOct 21

Life Is Strange Creator's High Praise for Amazon TV Show: A Hella

Michel Koch, co-creator of the original Life is Strange game, has given his strong approval for the upcoming Amazon Prime TV adaptation after visiting the set. He praised the cast, including Tatum Grace Hopkins (Max) and Maisy Stella (Chloe), calling the experience a career highlight. Despite Don't Nod's lack of official involvement, the set visit signals respect for the game's origins, building fan anticipation for the series.

AI Model Lock-in? It's Not the Model, It's Everything Else
Programming
Stack Overflow BlogOct 21

AI Model Lock-in? It's Not the Model, It's Everything Else

A recent survey reveals that while swapping AI models is becoming common, the real lock-in risk for enterprises lies in the surrounding infrastructure: integrations, agent workflows, and guardrails. Despite concerns about vendor lock-in, it often ranks lower than immediate priorities like security and cost in AI purchasing decisions. However, multi-model adopters are proactively building open, flexible architectures, demonstrating a path to mitigate future migration headaches.

Back to Newsroom

Stay ahead of the curve

Get the latest technology insights delivered to your inbox every morning.