# ATNMY STDO Auth Central JWT auth server for Autonomy Studio and launched apps. ## Stack - Bun - Hono - PostgreSQL - Redis - Prisma - JWT access tokens - Opaque refresh tokens stored in Redis - One-time handoff tokens for app SSO stored in Redis ## Local Run ```bash cp .env.example .env docker compose up -d postgres redis bun install bun run db:generate bun run db:migrate bun run db:seed bun run dev ``` Default server: ```text http://localhost:4100 ``` Default backing services: ```text PostgreSQL: localhost:5434 Redis: localhost:6380 ``` Default seeded admin: ```text admin@futuredrive.net / admin1234 ``` Change this before using the service outside local development. ## API ### Login ```http POST /auth/login Content-Type: application/json { "email": "admin@futuredrive.net", "password": "admin1234" } ``` Returns: ```json { "accessToken": "...", "accessTokenExpiresIn": 43200, "refreshToken": "...", "refreshTokenExpiresIn": 2592000, "user": { "id": "...", "email": "admin@futuredrive.net", "name": "ATNMY Studio Admin", "role": "ADMIN", "roles": ["ADMIN"] } } ``` ### Current User ```http GET /auth/me Authorization: Bearer ``` ### Refresh ```http POST /auth/refresh Content-Type: application/json { "refreshToken": "" } ``` Refresh tokens are rotated on every use. ### Logout ```http POST /auth/logout Content-Type: application/json { "refreshToken": "" } ``` ### Create App Handoff Token Launcher calls this before launching ADS IDE or another app. ```http POST /auth/handoff Authorization: Bearer Content-Type: application/json { "audienceApp": "ADS IDE" } ``` Returns a short-lived one-time token: ```json { "handoffToken": "...", "expiresIn": 60, "audienceApp": "ADS IDE" } ``` ### Exchange App Handoff Token The launched app calls this on startup. ```http POST /auth/handoff/exchange Content-Type: application/json { "handoffToken": "...", "audienceApp": "ADS IDE" } ``` Returns the launched app's own access/refresh token pair. ## Launcher Integration Sketch ```ts const handoff = await fetch(`${AUTH_URL}/auth/handoff`, { method: 'POST', headers: { authorization: `Bearer ${accessToken}`, 'content-type': 'application/json' }, body: JSON.stringify({ audienceApp: 'ADS IDE' }) }).then((r) => r.json()) spawn(execBin, [ `--url=${encodeURIComponent(url)}`, `--handoff-token=${encodeURIComponent(handoff.handoffToken)}`, `--auth-url=${encodeURIComponent(AUTH_URL)}` ]) ``` ADS IDE then calls `/auth/handoff/exchange` and stores the returned session.