Properties
status: current platform: robot.wtf (VPS)
Extracted from the original wikibot.io design. AWS-specific content archived at Archive/AWS_Design/Auth.
See also: Design/VPS_Architecture, Design/Data_Model.
Auth Overview
ATProto OAuth login — users authenticate via their ATProto identity (Bluesky handle/DID). The platform runs a self-hosted OAuth 2.1 Authorization Server for MCP connections.
Three auth paths, all converging on the same identity resolution and ACL check:
- Browser session: ATProto OAuth → platform issues a session JWT (signed with our RS256 key) → middleware validates JWT on each request → resolves user → checks ACL → sets Otterwiki headers.
- MCP OAuth (Claude.ai): Self-hosted OAuth 2.1 AS handles DCR, PKCE, token issuance, JWKS. Claude.ai discovers
/.well-known/oauth-protected-resource, authenticates, presents access token. Per-wiki authorization in our middleware. - Bearer token (Claude Code, API clients): Token in
Authorizationheader → middleware hashes token, looks up in DB → resolves to user + wiki → checks ACL.
The platform middleware is the single authentication boundary. Everything downstream trusts it.
MCP Auth
OAuth 2.1 (Claude.ai): Self-hosted AS (authlib-based) provides DCR, PKCE, AS metadata, token endpoint, and JWKS. The MCP endpoint serves /.well-known/oauth-protected-resource pointing to the local AS. Per-wiki authorization happens in middleware — the AS identifies the user, middleware checks wiki access. See Design/VPS_Architecture for implementation details.
Bearer token (Claude Code / API): Each wiki gets a unique MCP bearer token, stored as a bcrypt hash in the platform DB. The user sees the token once (at creation) and can regenerate it from the dashboard. Usage: claude mcp add --transport http.
ACL Model
Simple role-based model:
| Role | Read | Write | Delete | Manage ACL | Delete wiki |
|---|---|---|---|---|---|
| viewer | yes | no | no | no | no |
| editor | yes | yes | yes | no | no |
| owner | yes | yes | yes | yes | yes |
Wiki creator is always owner. Owners can grant viewer/editor access to other registered users.
Authorization Flow
Layer 1 — Platform middleware (before Otterwiki sees the request):
- Resolve user identity from JWT or bearer token
- Resolve wiki from request routing
- Look up ACL: does this user have a grant on this wiki?
- If no grant and wiki is not public → 403
- Map ACL role to Otterwiki permission headers:
| ACL role | x-otterwiki-permissions header |
|---|---|
| viewer | READ |
| editor | READ,WRITE,UPLOAD |
| owner | READ,WRITE,UPLOAD,ADMIN |
| anonymous (public wiki) | Synthetic user with READ only |
- Set headers:
x-otterwiki-email,x-otterwiki-name,x-otterwiki-permissions - Forward to Otterwiki
Layer 2 — Otterwiki (AUTH_METHOD=PROXY_HEADER):
- Reads headers, creates ephemeral user object per request
- No local user database — all identity comes from headers
- Enforces READ/WRITE/UPLOAD/ADMIN based on the permissions header
For MCP and API paths, Otterwiki is not involved in auth — the handlers read the git repo directly. Authorization happens entirely in Layer 1.
Public wiki access
The platform middleware injects a synthetic anonymous user with READ permission for public wikis. Otterwiki config stays identical for all wikis:
AUTH_METHOD = "PROXY_HEADER" READ_ACCESS = "APPROVED" # always — public access handled by middleware WRITE_ACCESS = "APPROVED" ATTACHMENT_ACCESS = "APPROVED" DISABLE_REGISTRATION = True # no Otterwiki-level registration
Otterwiki Admin Panel — Section Disposition
The wiki owner (ACL role owner) gets ADMIN permission → access to /-/admin/*.
| Admin section | Disposition | Reason |
|---|---|---|
| Application Preferences | Keep | Wiki branding: site name, description, logo, favicon, language |
| Sidebar Preferences | Keep | UI layout: sidebar shortcuts, custom menu items |
| Content and Editing | Keep | Git workflow: commit message mode/template, page name casing, WikiLink style |
| Repository Management | Disable | Conflicts with platform Git management |
| Permissions and Registration | Disable | Conflicts with platform auth (middleware-managed) |
| User Management | Disable | No local user database in ProxyHeaderAuth mode |
| Mail Preferences | Disable for MVP | SMTP notifications — revisit later |
Implementation: Override admin navigation template to hide disabled sections. Return 404 from disabled routes in middleware (defense in depth).
