- Add `app` export and wrap startup logic behind `if (import.meta.main)` - Extract `hono` middleware into `sdk/hono.ts` for clean separation - Refactor module imports slightly to support in-memory native mocking (`db`, `valkey`, `spire_ffi`, `ratelimit`, `audit`) - Implement comprehensive native Deno mock tests in `server/main.test.ts` - Fix type checking across project files Co-authored-by: mrteye <1945243+mrteye@users.noreply.github.com>
145 lines
6.8 KiB
Markdown
145 lines
6.8 KiB
Markdown
# TASK METADATA
|
|
|
|
- **Target Files:**
|
|
- `server/main.ts`
|
|
- `server/main.test.ts` (new)
|
|
- `server/db.ts`
|
|
- `server/db.test.ts` (new)
|
|
- `server/spire_ffi.test.ts` (new)
|
|
- `server/valkey.ts`
|
|
- `server/valkey.test.ts` (new)
|
|
- `sdk/mod.ts`
|
|
- `sdk/hono.ts` (new)
|
|
- `sdk/mod.test.ts`
|
|
- `sdk/hono.test.ts` (new)
|
|
- **Core Objective:** Design a comprehensive multi-tier automated test harness
|
|
and verification suite for Auth-Yes, validating Tier 1 & 2 (Traefik
|
|
ForwardAuth), Tier 3 (ConnectRPC with SPIFFE/mTLS workload attestation),
|
|
decoupled Default-Deny RBAC authorization, and modular `@auth-yes/sdk` client
|
|
middleware using native Deno mocking.
|
|
- **Dependencies:** Deno standard library `@std/testing/mock`,
|
|
`@std/testing/bdd`, and `hono`.
|
|
- **Additional Important Notes:** Do NOT use Testcontainers or Docker
|
|
dependencies. Mock external services (Postgres, Valkey, SPIFFE API) using
|
|
Deno's native mocking capabilities to ensure hermetic, fast, and deterministic
|
|
test execution. Refactor `server/main.ts` top-level side effects (server
|
|
listener and network probes) behind `if (import.meta.main)` and export `app`
|
|
for in-memory `app.request()` testing.
|
|
|
|
---
|
|
|
|
## Architectural Considerations & Risks
|
|
|
|
### Risks
|
|
|
|
1. **Mocking Accuracy:** Using mocks for PostgreSQL (`db.ts`) and Valkey
|
|
(`valkey.ts`) risks diverging from actual production behavior if the database
|
|
schema or RESP3 client tracking intricacies change. We must ensure the mocked
|
|
responses accurately reflect both success cases and edge cases (e.g.,
|
|
specific Valkey error codes, Postgres constraint violations).
|
|
2. **Top-Level Side Effects during Test Imports:** Currently, importing
|
|
`server/main.ts`, `server/db.ts`, or `server/valkey.ts` executes top-level
|
|
code (e.g., `Deno.serve(...)`, `MetadataService.initialize()`,
|
|
`pingValkey()`, and strict env validation). These must be cleanly
|
|
decoupled/guarded so test runners can import modules in isolation without
|
|
triggering network listeners or unhandled runtime rejections.
|
|
3. **SPIFFE Workload Mismatches:** Relying on explicit Deno stubs for
|
|
`extractSpiffeIdFromCert` and `fetchSpiffeIdentity` must be robust enough to
|
|
simulate corrupted X.509 certificates or unrecognized SPIFFE IDs
|
|
deterministically. If stubs aren't well isolated between tests, state bleed
|
|
could cause flaky test runs.
|
|
4. **SDK Modularity:** Integrating Hono middleware directly into `sdk/mod.ts`
|
|
risks coupling the core SDK to a specific web framework. To preserve Rule 1
|
|
of `AGENTS.md` (zero-dependency SDK), Hono helpers must be housed in a
|
|
dedicated modular submodule (`sdk/hono.ts`) while keeping `sdk/mod.ts`
|
|
completely framework-agnostic.
|
|
|
|
### Alternatives
|
|
|
|
- **Alternative 1: E2E Containers (Rejected):** Spinning up live Postgres,
|
|
Valkey, and SPIRE agent containers using Docker/Testcontainers would provide
|
|
higher fidelity but drastically slow down the test suite and introduce
|
|
environment dependency friction. As requested, we are prioritizing hermetic
|
|
Deno native mocking.
|
|
- **Alternative 2: In-Memory Hono App & Direct RPC Testing (Selected):**
|
|
Leverage Hono's `.request()` helper on the exported `app` instance to test API
|
|
endpoints (Tier 1 & 2) in memory while stubbing the underlying `sql` and
|
|
`valkey` exports. For Tier 3 (ConnectRPC), test both direct service handler
|
|
invocations and RPC routes in memory.
|
|
|
|
---
|
|
|
|
## Proposed Implementation
|
|
|
|
### Phase 1: Test Environment & Server Module Decoupling
|
|
|
|
1. **Entrypoint Refactoring (`server/main.ts`):**
|
|
- Export `app` from `server/main.ts`.
|
|
- Wrap `Deno.serve(...)`, `pingValkey()`, and `MetadataService.initialize()`
|
|
inside `if (import.meta.main) { ... }` so importing `main.ts` in tests does
|
|
not spin up a live server or execute remote network calls.
|
|
- Provide safe fallback defaults for `RP_ID` and `ORIGIN` in test mode or
|
|
allow environment overrides before import.
|
|
2. **Database & Valkey Isolation:**
|
|
- Create reusable mock helpers using `@std/testing/mock` (`stub` and `spy`).
|
|
- Stub the `sql` export in `server/db.ts` to return controlled records
|
|
representing mock users, apps, and grants.
|
|
- Stub `valkey.get` and `valkey.setex` in `server/valkey.ts` to simulate
|
|
session cache hits, misses, and invalidations.
|
|
3. **SPIFFE/mTLS Mocking (Tier 3):**
|
|
- Stub `extractSpiffeIdFromCert` in `server/spire_ffi.ts`.
|
|
- Provide test cases for valid SPIFFE URIs (e.g.,
|
|
`spiffe://system.local/ed-droid-backend`), invalid URIs, missing
|
|
extensions, and unparseable certs.
|
|
|
|
### Phase 2: Tier 1 & 2 (Edge ForwardAuth) Verification
|
|
|
|
1. **Target:** `GET /api/forward-auth` via `app.request()` in
|
|
`server/main.test.ts`.
|
|
2. **Test Cases:**
|
|
- Valid session cookie: Assert HTTP `200 OK` and presence of
|
|
`X-Forwarded-User` and `X-Forwarded-User-Id` headers.
|
|
- Missing session cookie: Assert HTTP `401 Unauthorized`.
|
|
- Invalid/Expired session (simulated via Valkey cache miss): Assert HTTP
|
|
`401 Unauthorized`.
|
|
- Suspended account (simulated via mocked `sql` returning
|
|
`account_status: 'suspended'`): Assert HTTP `403 Forbidden`.
|
|
|
|
### Phase 3: Tier 3 (Zero-Trust Mesh & RBAC) Verification
|
|
|
|
1. **Target:** `AuthService.validateSession` ConnectRPC endpoint & handler.
|
|
2. **Test Cases:**
|
|
- **Default-Deny:** Unassigned user attempting to access a valid SPIFFE ID.
|
|
Assert `valid: false` and authorization rejection.
|
|
- **Valid RBAC Grant:** Authenticated user with explicit grant for the
|
|
calling app's SPIFFE ID. Assert `valid: true`, correct UUID, and correct
|
|
scope (e.g., `viewer`, `admin`).
|
|
- **SPIFFE Attestation Failure:** Missing or invalid `x-peer-cert` header.
|
|
Assert validation failure.
|
|
|
|
### Phase 4: Audit Ledger Verification
|
|
|
|
1. **Target:** `auditLog` function calls in `server/audit.ts`.
|
|
2. **Test Cases:**
|
|
- Spy on `auditLog` in `server/audit.ts`.
|
|
- Verify that successful logins (`login_success`), login failures
|
|
(`login_failed`), and explicit access denials (`session_validation_failed`)
|
|
generate the correct event payload structures and are dispatched to the
|
|
mocked DB.
|
|
|
|
### Phase 5: Modular SDK Ergonomics & Hono Middleware
|
|
|
|
1. **Target:** `sdk/hono.ts` (new) and `sdk/hono.test.ts` (new).
|
|
2. **Implementation Steps:**
|
|
- Create `sdk/hono.ts` exporting `createAuthMiddleware(sdk: AuthSdk)` so the
|
|
core `sdk/mod.ts` remains framework-agnostic.
|
|
- The middleware extracts the `session_id` cookie or `Authorization` header,
|
|
calls `sdk.requireAuth()`, and injects the resulting `userId` into the Hono
|
|
context (`c.set('userId', userId)`).
|
|
- If `requireAuth` throws, the middleware catches it and returns HTTP
|
|
`401 Unauthorized`.
|
|
3. **Verification:**
|
|
- In `sdk/hono.test.ts`, create a test Hono app, attach the middleware, and
|
|
verify that authorized requests pass and unauthorized requests return
|
|
`401 Unauthorized`.
|