# 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/oauth2/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/oauth2/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)
