Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
47 changes: 36 additions & 11 deletions apps/docs/mkdocs.yml
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@ site_name: SwampHacks Documentation
repo_url: https://github.com/swamphacks/core
repo_name: SwampHacks Core
docs_dir: src

theme:
name: material
icon:
Expand All @@ -15,10 +16,12 @@ theme:
- navigation.instant.progress
- content.code.copy
- content.action.edit

plugins:
- search

copyright: >
Copyright © 2025 SwampHacks
Copyright © 2026 SwampHacks

markdown_extensions:
- pymdownx.highlight:
Expand All @@ -30,33 +33,55 @@ markdown_extensions:
- pymdownx.superfences
- admonition
- pymdownx.details
- pymdownx.superfences
- attr_list
- pymdownx.tabbed:
alternate_style: true
- pymdownx.emoji:
emoji_index: !!python/name:material.extensions.emoji.twemoji
emoji_generator: !!python/name:material.extensions.emoji.to_svg

extra:
generator: false

nav:
- Home:
- Introduction: index.md
- System Architecture: architecture.md
- Repository Structure: repo-structure.md
- Getting Started: getting-started.md
- Development Workflow: workflow.md
- Web:
- Overview: web/index.md
- Installation: web/installation.md
- Features: web/features.md
- Installation & Setup: web/installation.md
- Architecture & State: web/architecture.md
- Routing & Auth: web/routing-auth.md
- API Integration: web/api-integration.md
- Styling & UI: web/styling.md
- API:
- Overview: api/index.md
- Installation: api/installation.md
- OpenAPI: 'https://core.apidocumentation.com/guide/swamphacks-core-api'
- Database testing: 'api/db_testing.md'
- Installation & Setup: api/installation.md
- Project Structure: api/structure.md
- Authentication & Roles: api/auth.md
- Database Schema (Neon): api/database.md
- Migrations: api/migrations.md
- Database Testing: api/db_testing.md
- OpenAPI: api/openapi.md
- Discord Bot:
- Overview: discord-bot/index.md
- Installation: discord-bot/installation.md
- Commands: discord-bot/commands.md
- Architecture: discord-bot/architecture.md
- Commands & Events: discord-bot/commands.md
- Infrastructure:
- Overview: infrastructure/index.md
- Docker Containers: infrastructure/docker.md
- Secrets Management (Infisical): infrastructure/secrets.md
- Deployment (DigitalOcean): infrastructure/digitalocean.md
- CI/CD Pipeline: infrastructure/cicd.md
- Operations:
- Third-Party Services: operations/services.md
- Troubleshooting: operations/troubleshooting.md
- Maintenance & Handoff: operations/handoff.md
- Docs:
- Overview: docs/index.md
- Installation: docs/installation.md

extra:
generator: false
- Writing Guide: docs/writing-guide.md
108 changes: 108 additions & 0 deletions apps/docs/src/api/auth.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,108 @@
# Authentication & Roles

## Overview

The API uses **Discord OAuth2** for authentication. On success, the server issues a session cookie. All protected routes validate this cookie on every request.

A separate key-based scheme exists for the mobile check-in app.

---

## OAuth2 Flow

```
Client API Discord
│ │ │
│── GET /auth/callback?code ──▶│ │
│ │── exchange code ────────────▶│
│ │◀─ access token ─────────────│
│ │── GET /users/@me ───────────▶│
│ │◀─ Discord user info ─────────│
│ │ │
│ │ (upsert user + session) │
│ │ │
│◀─ Set-Cookie: sh_session_id ─│ │
│◀─ 302 → CLIENT_URL ──────────│ │
```

1. The frontend initiates the OAuth2 flow by redirecting the user to Discord with a `state` nonce stored in the `sh_auth_nonce` cookie.
2. Discord redirects back to `/auth/callback` with a `code` and `state`.
3. The API validates the nonce, exchanges the code for a Discord access token, and fetches the user's Discord profile.
4. If the user is new, an `auth.users` record and `auth.accounts` record are created in a transaction. Otherwise, a new session is created for the existing user.
5. The session ID is set as the `sh_session_id` cookie and the user is redirected to the frontend.

---

## Session Validation

Every request to a protected route goes through `RequireAuth` middleware:

1. Reads the `sh_session_id` cookie.
2. Looks up the session in `auth.sessions` (must not be expired).
3. Fetches the associated user record.
4. Attaches a `UserContext` to the request context.

**Rolling expiration:** If the session has not been used in the past 24 hours, its expiration is extended by 30 days and the cookie is refreshed.

### UserContext fields

| Field | Type | Description |
|---|---|---|
| `UserID` | UUID | Unique user identifier |
| `Email` | `*string` | Primary email from Discord |
| `PreferredEmail` | `*string` | User-set preferred email |
| `Name` | string | Display name |
| `Onboarded` | bool | Whether onboarding is complete |
| `Image` | `*string` | Profile image URL |
| `Role` | `AuthUserRole` | Platform role (`user` or `superuser`) |
| `EmailConsent` | bool | Whether the user opted into emails |

---

## Platform Roles

Two platform-level roles are defined in `auth_user_role`:

| Role | Description |
|---|---|
| `user` | Default role for all registered users |
| `superuser` | Full access; bypasses all role checks |

Platform roles are enforced by `RequirePlatformRole(roles)` middleware. Superusers bypass this check unconditionally.

---

## Event Roles

Users can have a role within a specific event, stored in `event_roles`:

| Role | Description |
|---|---|
| `admin` | Full event management (create/delete/assign roles, release decisions) |
| `staff` | Event operations (check-in, review applications, manage redeemables) |
| `attendee` | Accepted attendee |
| `applicant` | Has submitted an application |

Event roles are enforced by `RequireEventRole(roles)` middleware, which fetches the user's role for the event from the URL path. Superusers bypass event role checks.

---

## Mobile Authentication

The mobile check-in app uses a static key instead of session cookies:

```
Authorization: Key <MOBILE_AUTH_KEY>
```

Routes under `/mobile` require this header. The key is configured via the `MOBILE_AUTH_KEY` environment variable (not in `.env.dev.example` — request it from the team).

---

## Endpoints

| Method | Path | Auth | Description |
|---|---|---|---|
| `GET` | `/auth/callback` | None | OAuth2 callback |
| `GET` | `/auth/me` | Session | Get current user |
| `POST` | `/auth/logout` | Session | Invalidate session |
Loading