172 lines
2.6 KiB
Markdown
172 lines
2.6 KiB
Markdown
# 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 <accessToken>
|
|
```
|
|
|
|
### Refresh
|
|
|
|
```http
|
|
POST /auth/refresh
|
|
Content-Type: application/json
|
|
|
|
{
|
|
"refreshToken": "<refreshToken>"
|
|
}
|
|
```
|
|
|
|
Refresh tokens are rotated on every use.
|
|
|
|
### Logout
|
|
|
|
```http
|
|
POST /auth/logout
|
|
Content-Type: application/json
|
|
|
|
{
|
|
"refreshToken": "<refreshToken>"
|
|
}
|
|
```
|
|
|
|
### Create App Handoff Token
|
|
|
|
Launcher calls this before launching ADS IDE or another app.
|
|
|
|
```http
|
|
POST /auth/handoff
|
|
Authorization: Bearer <launcherAccessToken>
|
|
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.
|