diff --git a/ONBOARDING.md b/ONBOARDING.md index 06d4ca4..5f7809c 100644 --- a/ONBOARDING.md +++ b/ONBOARDING.md @@ -1,85 +1,239 @@ -# Auth-Yes — Client Application Onboarding & Integration Guide +# Auth-Yes — Application Integration & Architecture Guide -This guide provides end-to-end instructions for connecting subsidiary -applications (such as **`ed-droid`**) to the **Auth-Yes** Zero-Trust Identity & -Access Management (IAM) fabric. +Auth-Yes is a standalone, zero-trust Identity and Access Management (IAM) fabric +and WebAuthn Passkey authority. It provides microsecond session validation, +cryptographic workload identity, and decoupled application-level Role-Based +Access Control (RBAC). + +This guide provides a universal, technology-agnostic architectural reference for +developers and system administrators integrating services into the Auth-Yes +ecosystem. --- -## 1. System Architecture & Authentication Flow +## 1. System Architecture & Defense-in-Depth Model -Auth-Yes provides ultra-low-friction, passkey-first authentication with a -**3-tier defense-in-depth model**: +Auth-Yes implements a **three-tier defense model**, allowing applications to +choose the integration depth that best fits their architecture: ``` - [ Browser / Public Internet ] - │ - 1. WebAuthn Passkey Login - 2. Scoped Cookie: *.atyg.org - │ - ▼ - ═════════════════════════════════════ - Traefik Ingress Proxy (traefik-net) - ═════════════════════════════════════ - │ - ┌────────────────────┴────────────────────┐ - ▼ ▼ -[ Tier 2: ForwardAuth ] [ Tier 3: Native App ] - (Portainer / Web UIs) (ed-droid) - │ │ - │ GET /api/forward-auth │ 1. Extract session_id - │ (Valkey Cache Lookup) │ 2. SDK ConnectRPC - │ ▼ - │ ┌─────────────────────────────────┐ - │ │ SPIRE Agent (spire-socket) │ - │ │ - Provides Client X509 SVID │ - │ │ - spiffe://system.local/ed-droid│ - │ └────────────────┬────────────────┘ - │ │ - │ │ ConnectRPC over HTTP/2 - ▼ ▼ - ═════════════════════════════════════════════════════════════════════ - Auth-Yes Central Gateway (auth-internal-net: auth-api:8000) - - Microsecond L1/L2 Valkey Session Verification (RESP3 Tracking) - - Cryptographic SPIFFE Workload Validation (spire_ffi) - - Default-Deny PostgreSQL Application RBAC Grants (grants table) - ═════════════════════════════════════════════════════════════════════ + [ Public Internet / User Browser ] + │ + 1. WebAuthn Passkey Login + 2. Wildcard Cookie: *.yourdomain.org + │ + ▼ + ═════════════════════════════════════════ + Ingress Reverse Proxy (Traefik, Nginx, etc.) + ═════════════════════════════════════════ + │ + ┌────────────────────┴────────────────────┐ + ▼ ▼ +[ Tier 1 & 2: Edge ForwardAuth ] [ Tier 3: Native Zero-Trust ] +(Off-the-shelf UIs / 3rd-Party) (Microservices & APIs) + │ │ + │ GET /api/forward-auth │ 1. Extract session token + │ (Valkey Cache Lookup) │ 2. Query Workload API Socket + │ ▼ + │ ┌─────────────────────────────────┐ + │ │ SPIRE Agent (Workload API) │ + │ │ - Issues X.509 SVID │ + │ │ - spiffe:/// │ + │ └────────────────┬────────────────┘ + │ │ + │ │ ConnectRPC over HTTP/2 + ▼ ▼ +═════════════════════════════════════════════════════════════════════ +Auth-Yes Central Gateway (auth-api:8000) +- Microsecond Session Validation (Valkey L1/L2 with RESP3 Tracking) +- Cryptographic Workload SVID Attestation (spire_ffi) +- Default-Deny PostgreSQL Application RBAC Grants (grants table) +═════════════════════════════════════════════════════════════════════ +``` + +### The Three Integration Tiers: + +| Tier | Target Use Case | Integration Mechanism | Protocol / Transport | Code Required | +| :--------------------------- | :---------------------------------------------------- | :------------------------------------------ | :------------------------------------------ | :------------------------- | +| **Tier 1: Global Edge** | Pre-release & unmapped internal services | Universal proxy perimeter fallback | HTTP 302 Redirect to `/login` | None | +| **Tier 2: Edge ForwardAuth** | Off-the-shelf apps, admin dashboards, legacy web UIs | Reverse proxy ForwardAuth route | HTTP `GET /api/forward-auth` | None | +| **Tier 3: Zero-Trust RPC** | Native microservices, custom APIs, streaming backends | Application SDK or direct ConnectRPC client | ConnectRPC / gRPC over HTTP/2 + SPIFFE mTLS | Minimal (Middleware / RPC) | + +--- + +## 2. Core Identity & Authorization Primitives + +### 2.1. User Session Identifiers + +- **Browser Clients:** Auth-Yes issues an HTTP-only, secure session cookie + (`session_id`) scoped to the parent domain (e.g., `.yourdomain.org`). Browsers + automatically transmit this cookie across all subdomains. +- **API / Programmatic Clients:** Applications accept standard Bearer + authorization headers: `Authorization: Bearer `. + +### 2.2. Workload Identity (SPIFFE / SPIRE) + +- Microservices obtain cryptographic identities (X.509 SVIDs) by connecting to + the local SPIRE Workload API UNIX domain socket at + `/var/run/spire/agent.sock`. +- SVIDs follow the standard SPIFFE ID URI format: + `spiffe:///` (e.g., + `spiffe://system.local/payment-service`). +- When an application calls Auth-Yes, Auth-Yes extracts and attests the caller's + SPIFFE ID to verify application authenticity. + +### 2.3. Decoupled Default-Deny RBAC + +Auth-Yes decouples **Authentication** (who the user is) from **Authorization** +(what application they are permitted to access): + +1. **Default-Deny:** A valid user session has **zero application permissions by + default**. +2. **Grant Evaluation:** When a service validates a session, Auth-Yes evaluates + the internal grant matrix: + ```sql + SELECT role FROM grants WHERE user_id = $1 AND app_id = $2; + ``` +3. **Scope Payloads:** If a grant exists, Auth-Yes returns + `{ valid: true, uuid: "...", scopes: [""] }`. If no grant exists, it + returns `{ valid: false, error: "Validation failed" }` resulting in an + immediate `403 Forbidden`. + +--- + +## 3. Integration Patterns + +### Pattern A: Edge ForwardAuth (Zero-Code / Reverse Proxy) + +For off-the-shelf web applications (e.g., Grafana, Portainer, PgAdmin), +configure your ingress reverse proxy to query Auth-Yes before routing traffic to +the upstream container. + +#### Traefik Example: + +```yaml +# In Traefik dynamic configuration or container labels: +labels: + - "traefik.http.routers.myapp.middlewares=auth-forward@docker" + - "traefik.http.middlewares.auth-forward.forwardauth.address=http://auth-api:8000/api/forward-auth" + - "traefik.http.middlewares.auth-forward.forwardauth.trustForwardHeader=true" + - "traefik.http.middlewares.auth-forward.forwardauth.authResponseHeaders=X-Forwarded-User,X-Forwarded-User-Id" +``` + +- **Behavior:** + - If valid: Auth-Yes returns HTTP `200 OK` and injects upstream identity + headers (`X-Forwarded-User: `, `X-Forwarded-User-Id: `). + - If missing / invalid: Auth-Yes returns HTTP `401 Unauthorized` or redirects + unauthenticated browsers to `/login`. + +--- + +### Pattern B: Native ConnectRPC / gRPC (Language-Agnostic) + +For services written in Go, Rust, Python, Java, or C#, applications can +communicate directly with the Auth-Yes ConnectRPC endpoint over internal HTTP/2 +multiplexing. + +#### Protobuf Contract (`auth.proto`): + +```protobuf +syntax = "proto3"; +package auth.v1; + +service AuthService { + rpc ValidateSession (ValidateSessionRequest) returns (ValidateSessionResponse); +} + +message ValidateSessionRequest { + string token = 1; +} + +message ValidateSessionResponse { + bool valid = 1; + string uuid = 2; + repeated string scopes = 3; + string error = 4; +} +``` + +- **Endpoint:** `http://auth-api:8000/auth.v1.AuthService/ValidateSession` (or + internal mesh DNS). +- **Workload Identity:** Pass client TLS certificates fetched from the SPIRE + Workload API socket to enable mTLS attestation. + +--- + +### Pattern C: TypeScript & Deno Applications (`@auth-yes/sdk`) + +For TypeScript/JavaScript runtimes, `@auth-yes/sdk` provides a zero-dependency +client with built-in **RESP3 client-side cache tracking** (L1 memory cache +invalidated in real time via Valkey broadcast events). + +#### 1. Import Reference: + +```json +{ + "imports": { + "@auth-yes/sdk": "https://git.atyg.org/tylerg/auth-yes/raw/branch/main/sdk/mod.ts", + "@auth-yes/sdk/hono": "https://git.atyg.org/tylerg/auth-yes/raw/branch/main/sdk/hono.ts" + } +} +``` + +#### 2. Service Integration: + +```typescript +import { createAuthSdk } from "@auth-yes/sdk"; + +// Initialize SDK with internal mesh endpoints +const authSdk = createAuthSdk({ + authApiUrl: Deno.env.get("AUTH_API_URL") || "http://auth-api:8000", + valkeyUrl: Deno.env.get("VALKEY_URL"), // Optional: Enables real-time L1 cache tracking +}); + +// Validate any session token (cookie or header) +const result = await authSdk.validateSession(sessionToken); + +if (result.valid) { + console.log(`User Authenticated: ${result.uuid}, Roles:`, result.scopes); +} else { + console.warn(`Access Denied: ${result.error}`); +} ``` --- -## 2. Docker / Podman Compose Configuration +## 4. Container & Infrastructure Topology -To connect a subsidiary service to Auth-Yes and SPIRE, update your application's -`compose.yml`: +To connect any application container to Auth-Yes and SPIRE, ensure two +infrastructure prerequisites: + +1. **Network Mesh:** Attach the container to the internal bridge network + (`auth-internal-net`) and ingress proxy network (`traefik-net`). +2. **Workload Socket:** Mount the pre-existing named volume `spire-socket` as + read-only (`:ro`). + +### Generic Compose Template: ```yaml version: "3.8" services: - ed-droid: - image: ${REG}/library/ed-droid:latest - container_name: ed-droid + my-service: + image: ${REG}/library/my-service:latest + container_name: my-service environment: - AUTH_API_URL=http://auth-api:8000 - VALKEY_URL=redis://auth-valkey:6379 - SPIFFE_ENDPOINT_SOCKET=/var/run/spire/agent.sock networks: - - default # Internal service mesh - - traefik-net # Ingress routing + - default # auth-internal-net + - traefik-net # Ingress proxy volumes: - # Mount zero-trust SPIRE Workload API socket (read-only) + # Read-only SPIRE Workload API UNIX domain socket - spire-socket:/var/run/spire:ro - labels: - - "traefik.enable=true" - - "traefik.docker.network=traefik-net" - - "traefik.http.routers.ed-droid.rule=Host(`ed-droid.atyg.org`)" - - "traefik.http.routers.ed-droid.entrypoints=websecure" - - "traefik.http.routers.ed-droid.tls=true" - - "traefik.http.services.ed-droid.loadbalancer.server.port=3000" volumes: - # Reference the pre-existing SPIRE socket volume spire-socket: external: true @@ -93,120 +247,52 @@ networks: --- -## 3. SDK Integration (Deno / TypeScript / Hono) +## 5. Administrative Lifecycle & Access Provisioning -The `@auth-yes/sdk` is zero-dependency on backend databases and uses -**ConnectRPC** with **RESP3 client-side caching**. +Administrative operations are managed via the **Auth-Yes Admin Console** +(`https://auth./admin`): -### 3.1. Importing the SDK +### 5.1. Application Registration (`/admin/apps`) -In `deno.json` / `package.json`: +Every internal service connecting to Auth-Yes must be registered: -```json -{ - "imports": { - "@auth-yes/sdk": "https://git.atyg.org/tylerg/auth-yes/raw/branch/main/sdk/mod.ts", - "@auth-yes/sdk/hono": "https://git.atyg.org/tylerg/auth-yes/raw/branch/main/sdk/hono.ts" - } -} -``` +- **Application Name:** Human-readable label (e.g., `Analytics Engine`). +- **SPIFFE ID:** Cryptographic workload URI matching the service's container + attestation (e.g., `spiffe://system.local/analytics-engine`). +- **Description:** Functional description of the application. -### 3.2. Initializing the SDK & Protecting Routes +### 5.2. User Onboarding & Invite Token Taxonomy (`/admin/invites`) -```typescript -import { Hono } from "jsr:@hono/hono@4"; -import { createAuthSdk } from "@auth-yes/sdk"; -import { createAuthMiddleware } from "@auth-yes/sdk/hono"; +Auth-Yes uses a structured invite token model for user registration: -// 1. Initialize SDK with internal mesh endpoints -export const authSdk = createAuthSdk({ - authApiUrl: Deno.env.get("AUTH_API_URL") || "http://auth-api:8000", - valkeyUrl: Deno.env.get("VALKEY_URL") || "redis://auth-valkey:6379", -}); +1. **Site-Scoped Token (Recommended for subsidiary applications):** + - Pre-binds an invite code to a specific application and role (`user`, + `operator`, `admin`). + - When a user registers their passkey using this token, their account is + automatically activated and granted immediate access to the designated + application. +2. **Global Admin Token:** + - Grants system-wide administrative access across Auth-Yes management views. +3. **Open / Pending Token:** + - Creates an account in `pending` state requiring explicit administrator + review and role assignment before login is permitted. -const app = new Hono(); +### 5.3. Dynamic Role Assignment & Instant Revocation (`/admin/users/:id`) -// 2. Public Routes (No Auth Required) -app.get("/health", (c) => c.text("OK")); - -// 3. Protected API Routes (Protected by Auth-Yes Middleware) -const authMiddleware = createAuthMiddleware(authSdk); - -app.use("/api/*", authMiddleware); - -app.get("/api/me", (c) => { - const userId = c.get("userId"); // Injected UUID - return c.json({ userId, status: "authenticated" }); -}); - -// 4. Granular Scope / Role Validation -app.get("/api/admin/system", async (c) => { - const token = c.req.header("Authorization")?.replace("Bearer ", "") || - c.req.raw.headers.get("cookie")?.match(/session_id=([^;]+)/)?.[1]; - - const session = await authSdk.validateSession(token!); - - if (!session.scopes?.includes("admin")) { - return c.json( - { error: "Forbidden: Requires application admin scope" }, - 403, - ); - } - - return c.json({ message: "Admin console unlocked" }); -}); - -export default app; -``` +- **Grant Management:** Administrators can attach or adjust application roles + directly in the user profile matrix. +- **Instant Session Revocation:** Terminating a session or revoking an + application grant purges active tokens from Valkey in $< 1$ millisecond, + immediately blocking subsequent requests across all microservices. --- -## 4. Administrative Setup in Auth-Yes Console +## 6. Verification & Troubleshooting -Before users can access `ed-droid`, complete these steps in -`https://auth.atyg.org`: - -### Step 1: Register Application (`/admin/apps`) - -1. Log in to **Auth-Yes Admin Console** $\rightarrow$ **Applications**. -2. Click **+ Register Application**. -3. Fill in: - - **Name:** `ed-droid` - - **SPIFFE ID:** `spiffe://system.local/ed-droid` - - **Description:** - `Elite Dangerous streaming hub and telemetry module system` -4. Click **Save Application**. - -### Step 2: Onboard Users via Invite Tokens (`/admin/invites`) - -1. Go to **Invite Tokens** $\rightarrow$ **Generate Invite**. -2. Select Token Type: - - **Site-Scoped Invite:** Choose `ed-droid` and role (`user`, `operator`, or - `admin`). - - _Effect:_ When the user registers their passkey, they are instantly - granted access to `ed-droid` without admin intervention. - - **Global Admin Invite:** For core infrastructure administrators. -3. Share the generated registration link: - `https://auth.atyg.org/register?code=`. - -### Step 3: Manage Existing User Grants (`/admin/users/:id`) - -1. Navigate to **User Directory** $\rightarrow$ Click on a user. -2. In the **Application Access Grants** matrix: - - Select `ed-droid` from the dropdown. - - Assign role (`viewer`, `operator`, `admin`). - - Click **Grant Access**. -3. **Revocation:** Click **Revoke Access** at any time to instantly terminate - access (invalidated across Valkey in $< 1$ millisecond). - ---- - -## 5. Verification Checklist - -| Step | Verification Command / Check | Expected Result | -| :------------------------------ | :------------------------------------------------------------- | :---------------------------------------------- | -| **1. SPIRE Socket Mounted** | `podman exec -it ed-droid ls -la /var/run/spire/agent.sock` | Socket file is present (`srw-rw-rw-`) | -| **2. Auth Gateway Reachable** | `podman exec -it ed-droid curl -I http://auth-api:8000/health` | Returns HTTP `200 OK` | -| **3. Valkey Caching Active** | Check app logs during `validateSession()` | L1 cache hits log 0ms response times | -| **4. Default-Deny Active** | Request without grant for `ed-droid` | Returns HTTP `403 Forbidden: Validation failed` | -| **5. Valid Session Authorized** | Passkey authenticated user with grant | Returns HTTP `200 OK` with user UUID and scopes | +| Check | Verification Command / Procedure | Expected Diagnostic | +| :---------------------------------- | :------------------------------------------------------------- | :--------------------------------------------------------------------- | +| **1. Workload Socket Availability** | `ls -la /var/run/spire/agent.sock` inside container | File exists with type `s` (UNIX domain socket). | +| **2. Auth Mesh Connectivity** | `curl -I http://auth-api:8000/health` inside container | Returns HTTP `200 OK`. | +| **3. Unregistered Service Check** | Calling `ValidateSession` from an unassigned application | Returns HTTP `403 Forbidden: Validation failed`. | +| **4. Grant Authorization Check** | Calling `ValidateSession` with an active user grant | Returns `{ valid: true, uuid: "...", scopes: ["..."] }`. | +| **5. Revocation Propagation** | Revoke session in Admin Console $\rightarrow$ Re-issue request | Instant transition to HTTP `401 Unauthorized` without server restarts. |