How TREK Implements Passkey and WebAuthn Passwordless Authentication

TREK implements WebAuthn passwordless authentication through a three-layer architecture consisting of feature-toggle guards, a dedicated passkey service for challenge management, and HTTP endpoints that mint standard session tokens upon successful credential verification.

TREK is an open-source authentication platform that supports modern passwordless login via WebAuthn. This article examines how the repository handles passkey registration and authentication ceremonies, from challenge generation to session token issuance.

Architecture Overview

TREK’s WebAuthn implementation is organized into distinct layers that separate configuration, business logic, and HTTP transport concerns.

Feature Toggle and RP Resolution

Before any WebAuthn ceremony executes, the PasskeyEnabledGuard located in server/src/nest/auth/passkey-enabled.guard.ts validates that the administrator has enabled the Passkey feature flag. The guard also resolves the Relying Party (RP) ID through server/src/services/webauthnConfig.ts, which derives the identifier from the APP_URL environment variable or an explicit WEBAUTHN_RPID setting. If either the toggle is disabled or the RP domain cannot be determined, the guard returns a 404 Not Found response before any authentication logic executes.

Challenge Management

All WebAuthn challenges are ephemeral and stored in the webauthn_challenges database table. Each challenge has a five-minute time-to-live (TTL) and is single-use only. The implementation uses a DELETE … RETURNING SQL pattern to atomically consume challenges, ensuring that replay attacks using captured challenge data are impossible.

The Passkey Service Layer

The core WebAuthn logic resides in server/src/services/passkeyService.ts, which orchestrates both registration and authentication ceremonies using the @simplewebauthn/server library.

Registration Flow

The registration process involves two distinct phases:

  • passkeyRegisterOptions: Generates cryptographically random registration options by calling generateRegistrationOptions. This method creates a challenge that is persisted in the webauthn_challenges table with the issuing user’s ID.
  • passkeyRegisterVerify: Validates the client’s attestation response using verifyRegistrationResponse. Upon successful verification, the credential’s public key, initial counter, supported transports, device type, and backup eligibility are persisted in the webauthn_credentials table.

Login Flow

Authentication follows a similar two-phase pattern:

  • passkeyLoginOptions: Creates a discoverable-credential authentication challenge via generateAuthenticationOptions. This allows the authenticator to select the appropriate credential without the user providing a username first.
  • passkeyLoginVerify: Processes the assertion using verifyAuthenticationResponse. The service performs critical counter verification against the stored value in webauthn_credentials to detect potential credential cloning. If the counter is valid, the service updates the stored counter and mints a session token by calling generateToken, creating the same JWT-based session cookie used for password and OIDC authentication.

HTTP Controller and Security Controls

The PasskeyController in server/src/nest/auth/passkey.controller.ts exposes the WebAuthn functionality through RESTful endpoints.

Endpoint Structure

  • POST /api/auth/passkey/register/options: Initiates registration. Requires existing authentication and password re-verification to prevent account takeover.
  • POST /api/auth/passkey/register/verify: Completes registration and stores the new credential.
  • POST /api/auth/passkey/login/options: Public endpoint returning authentication options for discoverable credentials.
  • POST /api/auth/passkey/login/verify: Validates the assertion and establishes the session.
  • GET /api/auth/passkey/credentials: Lists the user’s registered passkeys. Users can also rename or delete their own credentials through this resource.

Rate Limiting and Audit

Every endpoint is protected by RateLimitService to prevent brute-force enumeration of credential IDs. Additionally, writeAudit logs all registration and authentication attempts with metadata including IP address and credential ID. To prevent timing side-channel attacks, the login verification endpoint pads response times to a minimum of 350 milliseconds (LOGIN_MIN_LATENCY_MS), matching the latency of password hash verification.

Client Integration Examples

Registration Ceremony

The following TypeScript demonstrates how a client completes passkey registration:

// 1️⃣ Get registration options (authenticated)
await fetch('/api/auth/passkey/register/options', {
  method: 'POST',
  credentials: 'include',
  body: JSON.stringify({ password: 'myCurrentPassword' })
}).then(r => r.json()).then(async data => {
  // 2️⃣ Call the browser API
  const credential = await navigator.credentials.create({
    publicKey: data.options
  });

  // 3️⃣ Send attestation back to the server
  await fetch('/api/auth/passkey/register/verify', {
    method: 'POST',
    credentials: 'include',
    body: JSON.stringify({ attestationResponse: credential, name: 'My Phone' })
  });
});

Login Ceremony

For passwordless login using a discoverable credential:

// 1️⃣ Get login options (no auth)
const opts = await fetch('/api/auth/passkey/login/options')
  .then(r => r.json()).then(r => r.options);

// 2️⃣ Ask the authenticator to sign the challenge
const assertion = await navigator.credentials.get({ publicKey: opts });

// 3️⃣ Verify on the server and obtain a session token
const resp = await fetch('/api/auth/passkey/login/verify', {
  method: 'POST',
  body: JSON.stringify({ assertionResponse: assertion })
}).then(r => r.json());

if (resp.token) {
  // The server set the same auth cookie as a password login
  console.log('Logged in as', resp.user);
}

Summary

  • TREK implements WebAuthn passwordless authentication using @simplewebauthn/server for cryptographic operations.
  • The PasskeyEnabledGuard ensures all passkey routes are disabled when the feature flag is off or the RP ID is unresolvable.
  • Challenges are single-use, time-bound records stored in webauthn_challenges and consumed atomically to prevent replay attacks.
  • The passkeyService.ts file handles registration via passkeyRegisterOptions and passkeyRegisterVerify, and authentication via passkeyLoginOptions and passkeyLoginVerify.
  • Successful WebAuthn verification mints standard JWT session tokens through generateToken, making passkeys interchangeable with password and OIDC flows.
  • The PasskeyController exposes REST endpoints with rate limiting, audit logging, and constant-time response padding to mitigate timing attacks.

Frequently Asked Questions

What WebAuthn library does TREK use?

TREK relies on @simplewebauthn/server for all server-side WebAuthn operations. The library provides the generateRegistrationOptions, verifyRegistrationResponse, generateAuthenticationOptions, and verifyAuthenticationResponse functions used in server/src/services/passkeyService.ts.

How does TREK prevent replay attacks during WebAuthn ceremonies?

The system stores challenges in the webauthn_challenges table with a strict five-minute expiration. When a challenge is consumed during verification, the code executes a DELETE … RETURNING query that atomically removes the record. If the challenge was already used or expired, the verification fails, rendering replayed request data invalid.

Can users manage multiple passkeys in TREK?

Yes. The GET /api/auth/passkey/credentials endpoint allows authenticated users to list all credentials stored in the webauthn_credentials table associated with their account. Users can rename specific passkeys for identification or delete compromised devices without affecting other registered authenticators.

Is passkey login in TREK resistant to phishing?

Yes. The implementation leverages WebAuthn’s origin-binding properties, verifying that the origin in the authentication response matches the configured RP ID in server/src/services/webauthnConfig.ts. Additionally, the use of discoverable credentials and the requirement for genuine user presence during the ceremony ensure that authentication cannot be phished from a malicious domain.

Have a question about this repo?

These articles cover the highlights, but your codebase questions are specific. Give your agent direct access to the source. Share this with your agent to get started:

Share the following with your agent to get started:
curl -s "https://instagit.com/install.md"

Works with
Claude Codex Cursor VS Code OpenClaw Any MCP Client

Maintain an open-source project? Get it listed too →