Implements FED-010: Agent Spawn via Federation feature that enables spawning and managing Claude agents on remote federated Mosaic Stack instances via COMMAND message type. Features: - Federation agent command types (spawn, status, kill) - FederationAgentService for handling agent operations - Integration with orchestrator's agent spawner/lifecycle services - API endpoints for spawning, querying status, and killing agents - Full command routing through federation COMMAND infrastructure - Comprehensive test coverage (12/12 tests passing) Architecture: - Hub → Spoke: Spawn agents on remote instances - Command flow: FederationController → FederationAgentService → CommandService → Remote Orchestrator - Response handling: Remote orchestrator returns agent status/results - Security: Connection validation, signature verification Files created: - apps/api/src/federation/types/federation-agent.types.ts - apps/api/src/federation/federation-agent.service.ts - apps/api/src/federation/federation-agent.service.spec.ts Files modified: - apps/api/src/federation/command.service.ts (agent command routing) - apps/api/src/federation/federation.controller.ts (agent endpoints) - apps/api/src/federation/federation.module.ts (service registration) - apps/orchestrator/src/api/agents/agents.controller.ts (status endpoint) - apps/orchestrator/src/api/agents/agents.module.ts (lifecycle integration) Testing: - 12/12 tests passing for FederationAgentService - All command service tests passing - TypeScript compilation successful - Linting passed Refs #93 Co-Authored-By: Claude Sonnet 4.5 <noreply@anthropic.com>
415 lines
6.8 KiB
Markdown
415 lines
6.8 KiB
Markdown
# Authentication Endpoints
|
|
|
|
Complete reference for authentication API endpoints powered by BetterAuth.
|
|
|
|
## Base URL
|
|
|
|
```
|
|
http://localhost:3001/auth
|
|
```
|
|
|
|
All authentication endpoints are prefixed with `/auth`.
|
|
|
|
## Endpoints
|
|
|
|
### Sign Up
|
|
|
|
Create a new user account with email and password.
|
|
|
|
```http
|
|
POST /auth/sign-up
|
|
```
|
|
|
|
**Request Body:**
|
|
|
|
```json
|
|
{
|
|
"email": "user@example.com",
|
|
"password": "SecurePass123!",
|
|
"name": "John Doe"
|
|
}
|
|
```
|
|
|
|
**Response (201):**
|
|
|
|
```json
|
|
{
|
|
"user": {
|
|
"id": "user-uuid",
|
|
"email": "user@example.com",
|
|
"name": "John Doe",
|
|
"emailVerified": false
|
|
},
|
|
"session": {
|
|
"id": "session-uuid",
|
|
"token": "eyJhbGciOiJIUzI1NiIs...",
|
|
"expiresAt": "2026-01-29T12:00:00.000Z"
|
|
}
|
|
}
|
|
```
|
|
|
|
**Errors:**
|
|
|
|
- `409 Conflict` — Email already exists
|
|
- `422 Validation Error` — Invalid input
|
|
|
|
---
|
|
|
|
### Sign In
|
|
|
|
Authenticate with email and password.
|
|
|
|
```http
|
|
POST /auth/sign-in
|
|
```
|
|
|
|
**Request Body:**
|
|
|
|
```json
|
|
{
|
|
"email": "user@example.com",
|
|
"password": "SecurePass123!"
|
|
}
|
|
```
|
|
|
|
**Response (200):**
|
|
|
|
```json
|
|
{
|
|
"user": {
|
|
"id": "user-uuid",
|
|
"email": "user@example.com",
|
|
"name": "John Doe"
|
|
},
|
|
"session": {
|
|
"id": "session-uuid",
|
|
"token": "eyJhbGciOiJIUzI1NiIs...",
|
|
"expiresAt": "2026-01-29T12:00:00.000Z"
|
|
}
|
|
}
|
|
```
|
|
|
|
**Errors:**
|
|
|
|
- `401 Unauthorized` — Invalid credentials
|
|
|
|
---
|
|
|
|
### Sign Out
|
|
|
|
Invalidate current session.
|
|
|
|
```http
|
|
POST /auth/sign-out
|
|
```
|
|
|
|
**Headers:**
|
|
|
|
```http
|
|
Authorization: Bearer {session_token}
|
|
```
|
|
|
|
**Response (200):**
|
|
|
|
```json
|
|
{
|
|
"success": true
|
|
}
|
|
```
|
|
|
|
---
|
|
|
|
### Get Session
|
|
|
|
Retrieve current session information.
|
|
|
|
```http
|
|
GET /auth/session
|
|
```
|
|
|
|
**Headers:**
|
|
|
|
```http
|
|
Authorization: Bearer {session_token}
|
|
```
|
|
|
|
**Response (200):**
|
|
|
|
```json
|
|
{
|
|
"user": {
|
|
"id": "user-uuid",
|
|
"email": "user@example.com",
|
|
"name": "John Doe"
|
|
},
|
|
"session": {
|
|
"id": "session-uuid",
|
|
"expiresAt": "2026-01-29T12:00:00.000Z"
|
|
}
|
|
}
|
|
```
|
|
|
|
**Errors:**
|
|
|
|
- `401 Unauthorized` — Invalid or expired session
|
|
|
|
---
|
|
|
|
### Get Profile
|
|
|
|
Get authenticated user's profile (custom endpoint).
|
|
|
|
```http
|
|
GET /auth/profile
|
|
```
|
|
|
|
**Headers:**
|
|
|
|
```http
|
|
Authorization: Bearer {session_token}
|
|
```
|
|
|
|
**Response (200):**
|
|
|
|
```json
|
|
{
|
|
"id": "user-uuid",
|
|
"email": "user@example.com",
|
|
"name": "John Doe",
|
|
"emailVerified": false
|
|
}
|
|
```
|
|
|
|
**Errors:**
|
|
|
|
- `401 Unauthorized` — Not authenticated
|
|
|
|
---
|
|
|
|
### OIDC Callback
|
|
|
|
OAuth callback handler for Authentik (and other OIDC providers).
|
|
|
|
```http
|
|
GET /auth/callback/authentik
|
|
```
|
|
|
|
**Query Parameters:**
|
|
|
|
- `code` — Authorization code from provider
|
|
- `state` — CSRF protection token
|
|
|
|
This endpoint is called by the OIDC provider after successful authentication.
|
|
|
|
**Response:**
|
|
|
|
- Redirects to frontend with session token
|
|
|
|
---
|
|
|
|
## Authentication Flow
|
|
|
|
### Email/Password Flow
|
|
|
|
```
|
|
1. User submits credentials → POST /auth/sign-in
|
|
2. Server validates credentials
|
|
3. Server creates session
|
|
4. Server returns session token
|
|
5. Client stores token
|
|
6. Client includes token in subsequent requests
|
|
```
|
|
|
|
### OIDC Flow
|
|
|
|
```
|
|
1. User clicks "Sign in with Authentik"
|
|
2. Frontend redirects to Authentik
|
|
3. User authenticates with Authentik
|
|
4. Authentik redirects to /auth/callback/authentik
|
|
5. Server exchanges code for tokens
|
|
6. Server creates/updates user
|
|
7. Server creates session
|
|
8. Server redirects to frontend with session token
|
|
```
|
|
|
|
## Using Session Tokens
|
|
|
|
Include the session token in the `Authorization` header for all authenticated requests:
|
|
|
|
```http
|
|
GET /api/tasks
|
|
Authorization: Bearer eyJhbGciOiJIUzI1NiIs...
|
|
```
|
|
|
|
### Token Storage
|
|
|
|
**Frontend (Browser):**
|
|
|
|
- Store in `httpOnly` cookie (most secure)
|
|
- Or `localStorage` (less secure, XSS vulnerable)
|
|
|
|
**Mobile/Desktop:**
|
|
|
|
- Secure storage (Keychain on iOS, KeyStore on Android)
|
|
|
|
### Token Expiration
|
|
|
|
Tokens expire after 24 hours (configurable via `JWT_EXPIRATION`).
|
|
|
|
**Check expiration:**
|
|
|
|
```typescript
|
|
import { AuthSession } from "@mosaic/shared";
|
|
|
|
const isExpired = (session: AuthSession) => {
|
|
return new Date(session.session.expiresAt) < new Date();
|
|
};
|
|
```
|
|
|
|
**Refresh flow** (future implementation):
|
|
|
|
```http
|
|
POST /auth/refresh
|
|
```
|
|
|
|
## Error Responses
|
|
|
|
### 401 Unauthorized
|
|
|
|
```json
|
|
{
|
|
"error": {
|
|
"code": "UNAUTHORIZED",
|
|
"message": "Invalid or expired session token"
|
|
}
|
|
}
|
|
```
|
|
|
|
### 422 Validation Error
|
|
|
|
```json
|
|
{
|
|
"error": {
|
|
"code": "VALIDATION_ERROR",
|
|
"message": "Input validation failed",
|
|
"details": {
|
|
"email": "Invalid email format",
|
|
"password": "Must be at least 8 characters"
|
|
}
|
|
}
|
|
}
|
|
```
|
|
|
|
## Examples
|
|
|
|
### Sign Up
|
|
|
|
```bash
|
|
curl -X POST http://localhost:3001/auth/sign-up \
|
|
-H "Content-Type: application/json" \
|
|
-d '{
|
|
"email": "jane@example.com",
|
|
"password": "SecurePass123!",
|
|
"name": "Jane Doe"
|
|
}'
|
|
```
|
|
|
|
### Sign In
|
|
|
|
```bash
|
|
curl -X POST http://localhost:3001/auth/sign-in \
|
|
-H "Content-Type: application/json" \
|
|
-d '{
|
|
"email": "jane@example.com",
|
|
"password": "SecurePass123!"
|
|
}'
|
|
```
|
|
|
|
**Save the token from response:**
|
|
|
|
```bash
|
|
TOKEN=$(curl -X POST http://localhost:3001/auth/sign-in \
|
|
-H "Content-Type: application/json" \
|
|
-d '{"email":"jane@example.com","password":"SecurePass123!"}' \
|
|
| jq -r '.session.token')
|
|
```
|
|
|
|
### Get Profile
|
|
|
|
```bash
|
|
curl http://localhost:3001/auth/profile \
|
|
-H "Authorization: Bearer $TOKEN"
|
|
```
|
|
|
|
### Sign Out
|
|
|
|
```bash
|
|
curl -X POST http://localhost:3001/auth/sign-out \
|
|
-H "Authorization: Bearer $TOKEN"
|
|
```
|
|
|
|
## Security Considerations
|
|
|
|
### Password Requirements
|
|
|
|
- Minimum 8 characters
|
|
- At least one uppercase letter
|
|
- At least one lowercase letter
|
|
- At least one number
|
|
|
|
Configure in `apps/api/src/auth/auth.config.ts`.
|
|
|
|
### Rate Limiting
|
|
|
|
- Sign-up: 5 requests per hour per IP
|
|
- Sign-in: 10 requests per hour per IP (prevents brute force)
|
|
|
|
### CSRF Protection
|
|
|
|
OIDC flow includes `state` parameter for CSRF protection.
|
|
|
|
### Token Security
|
|
|
|
- Tokens are signed with `JWT_SECRET`
|
|
- Use strong secret (min 32 characters)
|
|
- Rotate secret regularly in production
|
|
- Never expose tokens in logs
|
|
|
|
## TypeScript Types
|
|
|
|
All authentication types are available from `@mosaic/shared`:
|
|
|
|
```typescript
|
|
import type {
|
|
AuthUser,
|
|
AuthSession,
|
|
LoginRequest,
|
|
LoginResponse,
|
|
} from '@mosaic/shared';
|
|
|
|
// Use in frontend
|
|
const login = async (req: LoginRequest): Promise<AuthSession> => {
|
|
const res = await fetch('/auth/sign-in', {
|
|
method: 'POST',
|
|
headers: { 'Content-Type': 'application/json' },
|
|
body: JSON.stringify(req),
|
|
});
|
|
return res.json();
|
|
};
|
|
|
|
// Use in backend
|
|
@Post('sign-in')
|
|
async signIn(@Body() request: LoginRequest): Promise<LoginResponse> {
|
|
// ...
|
|
}
|
|
```
|
|
|
|
See [API Types](2-types.md) for complete type definitions.
|
|
|
|
## Next Steps
|
|
|
|
- **Configure OIDC** — [Configuration → Authentik](../../1-getting-started/3-configuration/2-authentik.md)
|
|
- **Review Types** — [Authentication Types](2-types.md)
|
|
- **Understand Architecture** — [Architecture → Authentication](../../3-architecture/2-authentication/1-betterauth.md)
|