# How TREK Implements Passkey WebAuthn Authentication: A Complete Technical Guide

> Discover how TREK implements passkey WebAuthn authentication using a three-layer architecture for secure JWT sessions. Learn the technical details.

- Repository: [Maurice/TREK](https://github.com/mauriceboe/TREK)
- Tags: deep-dive
- Published: 2026-07-04

---

**TREK implements passkey WebAuthn authentication through a three-layer architecture that combines a feature-toggle guard, a stateful service managing single-use cryptographic challenges, and REST controllers that generate standard JWT sessions upon successful verification.**

The open-source TREK repository provides a production-ready implementation of WebAuthn (passkey) authentication that integrates seamlessly with existing session-based security. Unlike bolt-on solutions that treat WebAuthn as a separate identity provider, TREK embeds the protocol directly into its native authentication stack, using the same session cookies and audit trails as password or OIDC logins.

## Feature Toggle and RP Guard

Before any WebAuthn ceremony executes, TREK validates that the functionality is enabled and properly configured.

### The PasskeyEnabledGuard

The `PasskeyEnabledGuard` in [`server/src/nest/auth/passkey-enabled.guard.ts`](https://github.com/mauriceboe/TREK/blob/main/server/src/nest/auth/passkey-enabled.guard.ts) acts as a mandatory prerequisite for all passkey endpoints. This guard checks two conditions:

- **Admin Configuration**: Verifies that the *Passkey* feature flag is enabled in application settings.
- **Relying Party (RP) Resolution**: Ensures a valid WebAuthn **Relying Party ID** (RP ID) can be resolved from the environment.

If either check fails, the guard returns a **404 Not Found** response before any authentication logic executes, effectively hiding the endpoints rather than exposing disabled functionality.

### RP ID Resolution

The RP configuration is centralized in [`server/src/services/webauthnConfig.ts`](https://github.com/mauriceboe/TREK/blob/main/server/src/services/webauthnConfig.ts). TREK dynamically determines the RP ID by examining the `APP_URL` environment variable or falling back to an explicit `WEBAUTHN_RPID` setting. This configuration also defines the allowed origins, ensuring that credentials are scoped strictly to the deployed domain and preventing phishing attacks from unauthorized origins.

## Cryptographic Ceremony Management

All WebAuthn protocol logic resides in [`server/src/services/passkeyService.ts`](https://github.com/mauriceboe/TREK/blob/main/server/src/services/passkeyService.ts), which orchestrates registration and authentication ceremonies using the `@simplewebauthn/server` library.

### Challenge Lifecycle and Replay Protection

TREK stores cryptographic challenges in a dedicated `webauthn_challenges` table defined in [`server/src/db/migrations.ts`](https://github.com/mauriceboe/TREK/blob/main/server/src/db/migrations.ts). The implementation enforces strict security controls:

- **Time-bound**: Challenges expire after **5 minutes**.
- **Single-use**: Upon verification, the service executes a `DELETE … RETURNING` query to atomically remove the challenge, making replay attacks impossible.
- **User-scoped**: Challenges are associated with specific user IDs to prevent cross-user replay.

### Registration Ceremonies

The registration flow consists of two distinct phases:

1. **Options Generation**: The `passkeyRegisterOptions` method calls `generateRegistrationOptions` to create a challenge scoped to the user's ID. This includes the RP ID, user details, and cryptographic preference settings (e.g., resident key requirements for discoverable credentials).

2. **Attestation Verification**: The `passkeyRegisterVerify` method processes the client's attestation response using `verifyRegistrationResponse`. Upon successful validation, the service extracts the credential's public key, credential ID, authenticator type, transports, and initial counter value, persisting them to the `webauthn_credentials` table.

### Authentication and Counter Verification

The login flow supports **discoverable credentials** (passkeys) for passwordless authentication:

1. **Challenge Creation**: `passkeyLoginOptions` invokes `generateAuthenticationOptions` to create a challenge that allows the browser to select any credential registered for the current RP ID.

2. **Assertion Verification**: `passkeyLoginVerify` uses `verifyAuthenticationResponse` to validate the cryptographic signature. Critically, TREK implements **counter verification** to detect cloned authenticators: the service compares the stored counter value against the assertion's counter and rejects the request if the new value is less than or equal to the stored value.

3. **Counter Update**: On successful verification, the stored counter is atomically updated to prevent future replay attempts using the same assertion.

## HTTP Controller and Security Hardening

The `PasskeyController` in [`server/src/nest/auth/passkey.controller.ts`](https://github.com/mauriceboe/TREK/blob/main/server/src/nest/auth/passkey.controller.ts) exposes the WebAuthn functionality through REST endpoints, applying additional security layers.

### Endpoint Structure

The controller routes requests to the service layer while enforcing authentication boundaries:

- `/api/auth/passkey/register/options` – Requires existing session authentication and password re-confirmation to prevent account takeover during registration.
- `/api/auth/passkey/register/verify` – Accepts the attestation response and credential name (e.g., "My Phone").
- `/api/auth/passkey/login/options` – Public endpoint serving authentication challenges for passwordless login.
- `/api/auth/passkey/login/verify` – Public endpoint verifying assertions and issuing sessions.
- `/api/auth/passkey/credentials` – Authenticated endpoints for listing, renaming, or deleting a user's own passkeys.

### Rate Limiting and Audit Logging

Every controller method is protected by `RateLimitService` to prevent brute-force attempts against challenge endpoints. Additionally, all successful and failed attempts are logged via `writeAudit`, creating a forensic trail of passkey registrations and logins alongside traditional authentication events.

### Timing Attack Protection

To prevent timing analysis that could reveal whether a user has passkeys registered, the login endpoints implement constant-time padding. The `LOGIN_MIN_LATENCY_MS` constant (set to **350 ms**) ensures that responses take at least this duration regardless of whether the credential lookup succeeds or fails, masking the difference between "user not found" and "invalid credential."

## Session Integration

Upon successful WebAuthn verification, TREK does not create a separate token type. Instead, `passkeyLoginVerify` calls the existing `generateToken` function and [`server/src/services/cookie.ts`](https://github.com/mauriceboe/TREK/blob/main/server/src/services/cookie.ts) to issue the **same JWT session cookie** used for password and OIDC authentications. This architectural decision ensures that:

- Session management remains centralized and consistent.
- Passkeys are treated as a first-class authentication method, not a secondary factor requiring additional steps.
- Existing authorization logic (role checks, session expiry) functions identically regardless of authentication type.

## Summary

- **Guarded Activation**: The `PasskeyEnabledGuard` and [`webauthnConfig.ts`](https://github.com/mauriceboe/TREK/blob/main/webauthnConfig.ts) ensure WebAuthn endpoints are only exposed when properly configured, preventing security misconfigurations.
- **Stateless Challenges**: Single-use, expiring challenges in `webauthn_challenges` eliminate replay attack vectors.
- **Ceremony Orchestration**: [`passkeyService.ts`](https://github.com/mauriceboe/TREK/blob/main/passkeyService.ts) handles registration (`generateRegistrationOptions`/`verifyRegistrationResponse`) and login (`generateAuthenticationOptions`/`verifyAuthenticationResponse`) with mandatory counter verification to detect cloned devices.
- **Unified Sessions**: Successful passkey authentication immediately mints standard JWT sessions via `generateToken`, integrating seamlessly with TREK's existing security model.
- **Hardened Delivery**: Rate limiting, audit logging, and timing attack mitigation (350 ms padding) protect the endpoints from enumeration and brute-force attacks.

## Frequently Asked Questions

### How does TREK prevent replay attacks during WebAuthn ceremonies?

TREK stores challenges in a `webauthn_challenges` table with a 5-minute expiration. When a client submits a response, the service executes a `DELETE … RETURNING` SQL statement to atomically remove the challenge. This single-use design ensures that once a challenge is consumed for verification, it cannot be reused, effectively neutralizing replay attacks.

### What session mechanism does TREK use after a successful passkey login?

After `verifyAuthenticationResponse` succeeds in [`passkeyService.ts`](https://github.com/mauriceboe/TREK/blob/main/passkeyService.ts), the service calls `generateToken` and sets the session cookie using the same [`cookie.ts`](https://github.com/mauriceboe/TREK/blob/main/cookie.ts) utilities employed by password authentication. This means passkey users receive identical JWT session tokens to other authentication methods, maintaining a unified security boundary.

### How does TREK detect cloned or compromised authenticators?

During `passkeyLoginVerify`, the service validates the authenticator's counter value against the stored counter in `webauthn_credentials`. If the assertion's counter is less than or equal to the stored value, TREK rejects the authentication attempt, as this indicates the credential may have been extracted and cloned onto another device.

### Can administrators disable WebAuthn passkeys in TREK?

Yes. The `PasskeyEnabledGuard` in [`server/src/nest/auth/passkey-enabled.guard.ts`](https://github.com/mauriceboe/TREK/blob/main/server/src/nest/auth/passkey-enabled.guard.ts) checks an administrative feature flag before allowing access to any passkey endpoint. If the feature is disabled or the RP ID cannot be resolved from [`webauthnConfig.ts`](https://github.com/mauriceboe/TREK/blob/main/webauthnConfig.ts), the application returns a 404 response, effectively hiding the functionality without exposing error details.