Initial commit: YouTube Studio Flow (backend, frontend, infrastructure, docs)

This commit is contained in:
2026-08-11 12:27:44 +02:00
commit d5af006443
304 changed files with 74604 additions and 0 deletions
@@ -0,0 +1,129 @@
# Auth API
Base path: `/api/v1/auth`
All endpoints require `Authorization: Bearer <jwt>` unless noted otherwise.
Related: [[02 - Authentication]], [[09 - Teams API]]
---
## Endpoints
### GET /auth/google
Initiates the Google OAuth flow. Redirects the browser to the Google consent screen.
> No authentication required.
---
### GET /auth/google/callback
OAuth callback handler. Called by Google after the user grants consent. Sets JWT tokens and redirects to the frontend.
> No authentication required. Handled entirely by the backend.
---
### POST /auth/refresh
Refreshes the access token using a valid refresh token.
**Request body:**
```json
{ "refreshToken": "string" }
```
**Response:**
```json
{
"accessToken": "string",
"refreshToken": "string"
}
```
---
### POST /auth/logout
Invalidates the current refresh token. Requires authentication.
**Response:**
```json
{ "message": "Logged out" }
```
---
### GET /auth/me
Returns the currently authenticated user.
**Response:**
```json
{
"id": "cuid",
"email": "user@example.com",
"name": "User Name",
"teamId": "cuid",
"teamRole": "EDITOR"
}
```
**Possible `teamRole` values:** `OWNER`, `ADMIN`, `EDITOR`, `REVIEWER`, `READONLY`
---
### POST /auth/switch-team
Switch the active team context. Returns a new access token scoped to the requested team. The caller must be a member of the target team.
**Request body:**
```json
{ "teamId": "cuid" }
```
**Response:**
```json
{ "accessToken": "string" }
```
Replace the stored access token with the returned one. All subsequent requests will be scoped to the new team. The refresh token is unchanged.
> **Note:** The response does not include `teamRole`. The new role is encoded inside the JWT payload (`teamRole` claim). Clients that need the role without decoding the JWT must call `GET /users/me` after switching teams. See the backlog for a possible improvement.
> **Frontend status:** No UI exists for team switching. This endpoint is only reachable via direct API call. Use `GET /teams/mine` to enumerate the teams available to switch to.
---
### GET /users/me/preferences
Returns the current user's preferences as a freeform JSON object.
**Response:** `Record<string, unknown>`
---
### PATCH /users/me/preferences
Merges new key/value pairs into the user's stored preferences. Existing keys not present in the request body are preserved.
**Request body:** `Record<string, unknown>`
**Response:** Updated preferences object
---
## Notes
- Access tokens are short-lived JWTs signed with `JWT_SECRET`.
- Refresh tokens are stored encrypted and are invalidated on logout.
- YouTube OAuth tokens (for channel connections) are separate from user auth tokens and are AES-256 encrypted in the database using `TOKEN_ENCRYPTION_KEY`. If that key changes, all channel connections must be re-authenticated.
- See [[06 - Authentication]] for the full auth flow and guard usage.