- Serve multiple domains (RP IDs) from one instance: host-based dispatch, per-domain credentials and sessions, domains managed at runtime in the admin UI — previously one RP per instance - Cross-domain sign-in via Related Origin Requests: per-domain related-origins list with a served .well-known/webauthn document - Explicit per-domain origin lists with shell-glob wildcards (**. for apex + any subdomain depth, *. for one level), editable in the admin UI with validation and self-lockout guards - Per-domain auth hosts: the account/admin UI can live on a different host per domain, no longer confined to subdomains of a single RP - CLI: 'paskia init <rp-id [rp-name]' initializes or adds a domain to an existing database; 'paskia migrate' converts legacy databases BREAKING CHANGES (v2.0): - Database schema: config is now per-domain and credentials/sessions carry an rp_id — existing databases must be converted with 'paskia migrate' - Origins are now explicit: main implicitly allowed every subdomain of the RP; configure '**.' origins to reproduce that behavior - CLI: the flat '--rp-id/--rp-name/--origin/--auth/--save' flags are replaced by the 'init' and 'migrate' subcommandsReviewed-on: #4
179 lines
4.9 KiB
Markdown
179 lines
4.9 KiB
Markdown
# Paskia E2E Tests
|
|
|
|
End-to-end tests for Paskia using [Playwright](https://playwright.dev/) with Chrome's **Virtual Authenticator**.
|
|
|
|
## Overview
|
|
|
|
These tests exercise the complete WebAuthn/passkey authentication flow without requiring physical hardware. Chrome's DevTools Protocol provides a virtual authenticator that can:
|
|
|
|
- Generate passkey credentials
|
|
- Sign authentication challenges
|
|
- Store resident keys (discoverable credentials)
|
|
- Simulate user verification (biometrics/PIN)
|
|
|
|
## Prerequisites
|
|
|
|
- Node.js 18+
|
|
- Python with `uv` (for running the backend server)
|
|
|
|
## Setup
|
|
|
|
```bash
|
|
cd e2e
|
|
npm install
|
|
npm run install:browsers
|
|
```
|
|
|
|
## Running Tests
|
|
|
|
### Basic Test Run
|
|
|
|
```bash
|
|
npm test
|
|
```
|
|
|
|
This will:
|
|
1. Start a fresh Paskia server with a test database
|
|
2. Run all E2E tests against it
|
|
3. Clean up the server when done
|
|
|
|
### With Coverage
|
|
|
|
```bash
|
|
npm run test:coverage
|
|
```
|
|
|
|
Runs tests and collects coverage for both:
|
|
- **Python backend** (via `coverage.py`) - HTML report in `coverage-html/`
|
|
- **Frontend JavaScript** (via Chrome V8 coverage) - JSON data in `e2e/coverage-frontend/`
|
|
|
|
### Interactive Mode
|
|
|
|
```bash
|
|
npm run test:ui
|
|
```
|
|
|
|
Opens Playwright's UI mode for interactive test debugging.
|
|
|
|
### Headed Mode
|
|
|
|
```bash
|
|
npm run test:headed
|
|
```
|
|
|
|
Runs tests with a visible browser window.
|
|
|
|
### Debug Mode
|
|
|
|
```bash
|
|
npm run test:debug
|
|
```
|
|
|
|
Runs tests with Playwright Inspector for step-by-step debugging.
|
|
|
|
## Test Structure
|
|
|
|
```
|
|
e2e/
|
|
├── playwright.config.js # Playwright configuration
|
|
├── package.json
|
|
├── tsconfig.json
|
|
├── test-data/ # Test database (created at runtime)
|
|
│ └── paskia.kantadb
|
|
└── tests/
|
|
├── global-setup.ts # Creates fresh DB (localhost + test.localhost domains), captures reset token
|
|
├── global-teardown.ts # Cleanup
|
|
├── 10-passkey.spec.ts # Registration, authentication, session tests
|
|
├── 20-api-auth.spec.ts # API-mode iframe flows (401/403/reauth)
|
|
├── 50-multidomain.spec.ts# Multi-domain dispatch, related origins, auth hosts, remote login
|
|
├── 99-logout.spec.ts # Logout (runs last)
|
|
└── fixtures/
|
|
├── virtual-authenticator.ts # Virtual authenticator setup
|
|
├── passkey-helpers.ts # WebSocket helpers
|
|
└── remote-auth.ts # Pairing-code remote auth helpers
|
|
```
|
|
|
|
## What's Tested
|
|
|
|
### Registration Flow
|
|
- Bootstrap admin user registration via reset token
|
|
- WebSocket challenge-response with virtual authenticator
|
|
- Session token creation and validation
|
|
|
|
### Authentication Flow
|
|
- Passkey authentication via WebSocket
|
|
- Credential verification
|
|
- Session management
|
|
|
|
### Session Management
|
|
- Token validation (`/auth/api/validate`)
|
|
- User info retrieval (`/auth/api/user-info`)
|
|
- Logout (`/auth/api/logout`)
|
|
- Invalid/missing token rejection
|
|
|
|
### Multi-Domain
|
|
- Host-based domain dispatch (`localhost` vs `test.localhost`, 421 for unknown hosts)
|
|
- Related Origin Requests well-known endpoint and admin domain API
|
|
- Per-domain auth hosts (UI at the site root)
|
|
- WebSocket cross-domain rules
|
|
- Cross-domain remote login via pairing code
|
|
|
|
## How Virtual Authenticator Works
|
|
|
|
The tests use Chrome DevTools Protocol (CDP) to create a virtual authenticator:
|
|
|
|
```typescript
|
|
const cdpSession = await page.context().newCDPSession(page)
|
|
await cdpSession.send('WebAuthn.enable')
|
|
await cdpSession.send('WebAuthn.addVirtualAuthenticator', {
|
|
options: {
|
|
protocol: 'ctap2',
|
|
transport: 'internal',
|
|
hasResidentKey: true,
|
|
hasUserVerification: true,
|
|
isUserVerified: true,
|
|
automaticPresenceSimulation: true,
|
|
},
|
|
})
|
|
```
|
|
|
|
This creates an in-browser authenticator that:
|
|
- Automatically responds to WebAuthn prompts
|
|
- Stores credentials persistently during the test session
|
|
- Simulates user verification without actual biometric input
|
|
|
|
## Environment Variables
|
|
|
|
| Variable | Description | Default |
|
|
|----------|-------------|---------|
|
|
| `BASE_URL` | Server URL | `http://localhost:4404` |
|
|
| `CI` | CI environment flag | - |
|
|
| `CLEANUP_TEST_DB` | Remove test DB after run | `false` |
|
|
|
|
## Limitations
|
|
|
|
1. **Chromium only**: Virtual authenticator is a Chrome DevTools feature
|
|
2. **Multi-domain via `*.localhost`**: Chrome resolves any `*.localhost` hostname to loopback, which the tests use for cross-domain scenarios; non-localhost domains are exercised only via explicit Host headers (Node-side requests)
|
|
3. **Single user per run**: Bootstrap creates one admin user; additional users need admin API
|
|
|
|
## Debugging Tips
|
|
|
|
1. **Check test database**: `e2e/test-data/paskia.kantadb` is removed during teardown; comment out the cleanup in `global-teardown.ts` to inspect it after a run
|
|
2. **View server output**: Global setup echoes server bootstrap to console
|
|
3. **Use trace viewer**: `npx playwright show-trace` on failure traces
|
|
|
|
## CI Integration
|
|
|
|
The tests are designed for CI environments:
|
|
|
|
```yaml
|
|
- name: Run E2E Tests
|
|
run: |
|
|
cd e2e
|
|
npm ci
|
|
npm run install:browsers
|
|
npm test
|
|
env:
|
|
CI: true
|
|
```
|