Skip to content

Security: providers.* and update.* actions are callable without authentication #61

@pablopunk

Description

@pablopunk

Context

Astro middleware deliberately skips auth for /_actions/ and /api/ (SELF_AUTH_PREFIXES in src/middleware.ts:147), so every action handler must check context.locals.user itself. Most do — but two whole action files don't, and one API route doesn't.

Audited against commit 3531916.

What's exposed

src/actions/providers.ts — all 7 actions, no auth check in any handler (handlers receive only input, never read context.locals.user):

  • connect (sets provider API keys via client.auth.set)
  • disconnect (src/actions/providers.ts:105 — removes provider credentials)
  • startOauth / finishOauth (drive provider OAuth flows)
  • list, getAvailableModelsForUser (cached, leak provider/model config)

src/actions/update.ts — all 3 actions, no auth check:

  • checkForUpdate (src/actions/update.ts:232)
  • pull (src/actions/update.ts:308 — docker-pulls a new image)
  • restart (src/actions/update.ts:344 — restarts the app container)

src/pages/api/system/health.tsGET handler has no requireAuth call (every other API route has one). Leaks queue/project counts and infrastructure health. Minor, but fix in the same pass.

Impact: anyone who can reach the port (e.g. anyone on the same tailnet, or the internet if exposed) can disconnect your AI providers, hijack OAuth flows, or trigger an app update/restart without logging in.

What to do

  1. Add an auth guard to every handler in providers.ts and update.ts. Follow the existing repo patterns — either the ensureAuthenticated(context) helper pattern from src/actions/mcps.ts:10-22, or the inline context.locals.user check from src/actions/chat.ts:53-56. Consider extracting one shared requireUser(context) helper into src/server/auth/ and reusing it, since TODO.md's multi-user plans will need exactly that.
  2. Add requireAuth(cookies) (see src/server/auth/requireAuth.ts, used by e.g. src/pages/api/settings/base-url.ts) to src/pages/api/system/health.ts.
  3. Add a regression test that enumerates all action files / API routes and asserts each handler checks auth (or at minimum, unit tests hitting the fixed handlers unauthenticated and expecting UNAUTHORIZED).

Out of scope

  • src/actions/auth.ts and src/actions/setup.ts are intentionally public (login/first-run setup) — do not touch.
  • The OpenCode permissive permission config and docker.sock mount are by design.
  • Multi-user roles/permissions (separate TODO.md effort).

Acceptance criteria

  • Calling any providers.* or update.* action without a session cookie returns UNAUTHORIZED, and GET /api/system/health returns 401.
  • All flows still work when logged in (connect/disconnect a provider, check for updates from settings UI).
  • pnpm format passes.

Metadata

Metadata

Assignees

No one assigned

    Labels

    bugSomething isn't working

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions