Skip to content

auth: name the credential-less in-process client failure + fix the client-derivation docs drift (stage 1 of #860) #882

Closed

Description

@elliotBraem

Stage 1 of #860 — the slice that is implementable now without redesigning the plugin runtime. #860 keeps the full problem statement and the stage-2 ambition (explicit caller/session composition, no header impersonation); that redesign is deliberately out of scope here.

The class is confirmed alive: #881's `adminAddMember` bug was the same shape — a hand-rolled in-process client whose forwarded headers hit a permission model that could not authorize it, failing far from the construction site with a misleading error.

Problem

The runtime client factory is a blind context spread — `packages/every-plugin/src/runtime/index.ts` hands whatever the caller passes straight into `createRouterClient`. The auth plugin re-derives identity only from `reqHeaders` (`createRequireAuth`, `plugins/auth/src/middleware.ts`, via `createHeaders(context.reqHeaders)`), and trusts `context.user` — never `context.userId`. So:

  • `plugins.auth.client({ userId, user })` without `reqHeaders` → empty `Headers` → `getSession` returns null → generic `UNAUTHORIZED` with `data: { apiKeyProvided: false }`, which reads as an API-key configuration problem
  • passing only `{ userId }` does not help at all — that key is never read by the auth guard
  • the failure surfaces on the first authed route call, far from the factory call that dropped the credentials

The docs actively teach the wrong thing:

  • `packages/everything-dev/skills/api-and-auth/SKILL.md` shows a callable `plugins.auth({ context })` form — the actual injected shape is `{ client, router }` (`plugins.auth.client({ ... })`)
  • `packages/better-near-auth/skills/auth-plugin/SKILL.md` repeats the callable + `{ context }`-wrapping form
  • the correct pattern exists only in host code (`host/src/services/auth.ts` — `buildPluginContext` always forwards `reqHeaders`) and is hand-rolled per consumer (citynode's local `memberAuthClient`)

What we want (stage 1)

  1. Named failure — `createRequireAuth` distinguishes no credentials forwarded at all (no `reqHeaders`, no host-resolved `user`, no API-key header) from credentials present but rejected, and the former fails with a message that names the actual fix:

    in-process caller forwarded no credentials — pass `reqHeaders` from the caller's request (or the host-resolved `user`); `client({ userId })` alone carries no credentials

    Contract error shapes stay as-is (`apiKeyProvided` is contract-constrained); this is a message/data refinement in `plugins/auth/src/middleware.ts`, not a contract change.

  2. One canonical pattern, documented — the skills show the single derivation and drop the nonexistent callable form:

    ```ts
    const auth = plugins.auth.client({ reqHeaders: context.reqHeaders, ...authContext });
    ```

    Files: `packages/everything-dev/skills/api-and-auth/SKILL.md`, `packages/better-near-auth/skills/auth-plugin/SKILL.md` (and the client/tanstack skills where the same drift appears).

  3. Dev-mode construction guard (optional but preferred) — `every-plugin`'s `createClient` warns when a provided context lacks keys the plugin's declared context schema marks required. Generic (the runtime stays auth-agnostic), catches the footgun at construction time in dev. This is the only `packages/*` change here → needs a changeset.

Acceptance

Out of scope (stage 2 — tracked on #860)

Direct invocation with an explicit caller/session object, removing the header-forwarding class of bug entirely. That touches the every-plugin runtime contract, host injection, and every plugin — separate effort, separate changeset train.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Labels

    ready-for-agentFully specified, ready for an AFK agent

    Type

    No type

    Fields

    Priority

    None yet

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions