docs(tasks): refine layered-security test harness with modular sdk and entrypoint decoupling

This commit is contained in:
Tyler Gillispie 2026-08-21 16:47:20 -07:00
parent a57e95714e
commit 3ae77473a9
2 changed files with 114 additions and 37 deletions

View File

@ -24,11 +24,12 @@ Management (IAM) fabric and WebAuthn Passkey authority.
server-side (`deleteCookie` across host and wildcard domains). server-side (`deleteCookie` across host and wildcard domains).
3. **Quality Gates:** Every PR must pass `deno fmt`, `deno task lint`, 3. **Quality Gates:** Every PR must pass `deno fmt`, `deno task lint`,
`deno task check`, and `deno test`. `deno task check`, and `deno test`.
4. **Dual Remote & SDK Distribution:** Development is tracked on GitHub (`origin`: 4. **Dual Remote & SDK Distribution:** Development is tracked on GitHub
`git@github.com:mrteye/auth-yes.git`). All commits and SDK changes must be (`origin`: `git@github.com:mrteye/auth-yes.git`). All commits and SDK changes
mirrored to Gitea (`gitea`: `git@git.atyg.org:tylerg/auth-yes.git`) so that must be mirrored to Gitea (`gitea`: `git@git.atyg.org:tylerg/auth-yes.git`)
external agents (e.g. Jules) and unauthenticated local projects can consume so that external agents (e.g. Jules) and unauthenticated local projects can
raw SDK modules via `https://git.atyg.org/tylerg/auth-yes/raw/branch/main/sdk/mod.ts`. consume raw SDK modules via
`https://git.atyg.org/tylerg/auth-yes/raw/branch/main/sdk/mod.ts`.
## 3. History & Context Link ## 3. History & Context Link

View File

@ -1,68 +1,144 @@
# TASK METADATA # TASK METADATA
- **Target Files:** - **Target Files:**
- `server/main.ts`
- `server/main.test.ts` (new) - `server/main.test.ts` (new)
- `server/db.ts`
- `server/db.test.ts` (new) - `server/db.test.ts` (new)
- `server/spire_ffi.test.ts` (new) - `server/spire_ffi.test.ts` (new)
- `server/valkey.ts`
- `server/valkey.test.ts` (new) - `server/valkey.test.ts` (new)
- `sdk/mod.test.ts`
- `sdk/mod.ts` - `sdk/mod.ts`
- **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 `@auth-yes/sdk` client middleware using native Deno mocking. - `sdk/hono.ts` (new)
- **Dependencies:** Deno standard library `@std/testing/mock`, `@std/testing/bdd`, and `hono`. - `sdk/mod.test.ts`
- **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. - `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 ## Architectural Considerations & Risks
### 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. **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. 1. **Mocking Accuracy:** Using mocks for PostgreSQL (`db.ts`) and Valkey
3. **SDK Modularity:** Integrating a Hono-specific middleware into `@auth-yes/sdk` risks violating the generic nature of the SDK. The `AuthSdk` core must remain strictly agnostic, with the Hono helper imported optionally or cleanly segregated from the core `validateSession` logic. (`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 ### 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: Full Integration Test (Hono App Mocking):** Rather than unit testing individual modules in isolation, we can heavily leverage Hono's `.request()` helper on the root app instance to test API endpoints (Tier 1 & 2) in memory while stubbing the underlying `sql` and `valkey` exports. This is generally preferred for testing Hono apps and is cleaner than spinning up a real HTTP server. - **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 ## Proposed Implementation
### Phase 1: Test Environment Mocking Architecture ### Phase 1: Test Environment & Server Module Decoupling
1. **Database & Valkey Isolation:**
- Create generic mock setups using `@std/testing/mock` (`stub` and `spy`). 1. **Entrypoint Refactoring (`server/main.ts`):**
- Stub the `sql` export in `server/db.ts` to return controlled JSON objects representing mock users, apps, and grants. - Export `app` from `server/main.ts`.
- Stub `valkey.get` and `valkey.setex` in `server/valkey.ts` to simulate session cache hits and misses. - Wrap `Deno.serve(...)`, `pingValkey()`, and `MetadataService.initialize()`
2. **SPIFFE/mTLS Mocking (Tier 3):** 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`. - 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. - 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 ### Phase 2: Tier 1 & 2 (Edge ForwardAuth) Verification
1. **Target:** `GET /api/forward-auth`
1. **Target:** `GET /api/forward-auth` via `app.request()` in
`server/main.test.ts`.
2. **Test Cases:** 2. **Test Cases:**
- Valid session cookie: Assert HTTP `200 OK` and presence of `X-Forwarded-User` and `X-Forwarded-User-Id` headers. - 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`. - Missing session cookie: Assert HTTP `401 Unauthorized`.
- Invalid/Expired session (simulated via Valkey cache miss): Assert HTTP `401 Unauthorized`. - Invalid/Expired session (simulated via Valkey cache miss): Assert HTTP
- Suspended account (simulated via mocked `sql` returning `account_status: 'suspended'`): Assert HTTP `403 Forbidden`. `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 ### Phase 3: Tier 3 (Zero-Trust Mesh & RBAC) Verification
1. **Target:** `AuthService.validateSession` ConnectRPC endpoint (`POST /auth.v1.AuthService/ValidateSession`).
1. **Target:** `AuthService.validateSession` ConnectRPC endpoint & handler.
2. **Test Cases:** 2. **Test Cases:**
- **Default-Deny:** Unassigned user attempting to access a valid SPIFFE ID. Assert `valid: false` and HTTP `403` or ConnectRPC validation failure. - **Default-Deny:** Unassigned user attempting to access a valid SPIFFE ID.
- **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`). Assert `valid: false` and authorization rejection.
- **SPIFFE Attestation Failure:** Missing or invalid `x-peer-cert` header. Assert validation failure. - **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 ### Phase 4: Audit Ledger Verification
1. **Target:** `auditLog` function calls across endpoints.
1. **Target:** `auditLog` function calls in `server/audit.ts`.
2. **Test Cases:** 2. **Test Cases:**
- Spy on `auditLog` in `server/audit.ts`. - Spy on `auditLog` in `server/audit.ts`.
- Verify that successful logins (`login_success`), login failures (`login_failed`), and explicit access denials generate the correct event payload structures and are dispatched to the mocked DB. - 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: SDK Ergonomics & Hono Middleware ### Phase 5: Modular SDK Ergonomics & Hono Middleware
1. **Target:** `sdk/mod.ts`
1. **Target:** `sdk/hono.ts` (new) and `sdk/hono.test.ts` (new).
2. **Implementation Steps:** 2. **Implementation Steps:**
- Extend `sdk/mod.ts` (or create an adjunct file like `sdk/hono.ts`) to export a `createAuthMiddleware(sdk: AuthSdk)` function. - Create `sdk/hono.ts` exporting `createAuthMiddleware(sdk: AuthSdk)` so the
- The middleware will extract the `session_id` cookie or `Authorization` header, call `sdk.requireAuth()`, and inject the resulting `userId` into the Hono context (`c.set('userId', userId)`). core `sdk/mod.ts` remains framework-agnostic.
- If `requireAuth` throws, the middleware catches it and returns HTTP `401 Unauthorized`. - 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:** 3. **Verification:**
- In `sdk/mod.test.ts`, create a mock Hono app, apply the middleware, and assert that authorized requests pass and unauthorized requests are halted cleanly. - 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`.