Skip to content

fix(auth): resolve 401 on all /api/* routes after Better Auth + Kysely integration #62

@Delqhi

Description

@Delqhi

Problem

After merging #58 (commit 30de716) with Better Auth + Kysely + Postgres, all /api/* routes return 401 Unauthorized from inside the Docker container — including the health endpoint that has no explicit auth guard.

Observed

$ docker exec -it sin-code-web-ui-v2-webui-1 curl -s http://localhost:3000/api/health
{"error":"Not authenticated"}   # status 401

But:

  • pnpm tsc --noEmit returns 0 errors
  • .next/standalone includes the kysely adapter chunks
  • Env vars are correctly set inside the container (DATABASE_URL=postgresql://app:...@postgres:5432/sin_webui)
  • Postgres is healthy and reachable from the webui container
  • Middleware manifest is empty (no middleware intercepting routes)

Root cause hypotheses

  1. Next.js 16 + kysely-adapter — the adapter may be triggering Next.js's built-in auth interception at build time. The build output had no middleware, but there's some Next.js magic that auto-detects auth configs.
  2. BETTER_AUTH_URL mismatch — set to http://localhost:3100 in docker-compose, but the container serves on http://localhost:3000. The middleware may check baseURL matches the request URL and reject otherwise.
  3. The kysely-adapter getDb() initialisationlib/db.ts uses a global memoised Kysely. On first request, the Kysely instance may be initialised in a server-component context that conflicts with Edge runtime.

Acceptance Criteria

  • curl http://localhost:3100/api/health returns {"ok":true,"db":"pg"} (not 401)
  • Login flow works end-to-end
  • Session cookie persists across requests
  • All settings routes return real data, not 401

Suggested investigation steps

  1. Try setting BETTER_AUTH_URL=http://localhost:3000 (not 3100)
  2. Add export const dynamic = 'force-dynamic' to the health route
  3. Check if next.config.mjs has any authInterrupts or similar settings
  4. Try the @better-auth/pg adapter (native, no Kysely)
  5. Add console.log at the top of getAuth() to verify it's called

Related

Metadata

Metadata

Assignees

No one assigned

    Type

    No type
    No fields configured for issues without a type.

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions