Skip to content

[sdk-client S5] feat(sdk/auth): prove typed credential contributions end to end #1352

Description

@rickylabs

Important

0.0.7 normative scope amendment — 2026-08-13. RFC 0001 Stage 4 supersedes conflicting rows
below. Ship the @netscript/plugin-auth-core/sdk bearer credential contribution, access-metadata
behavior, secret redaction, partitioned/direct-only cache rules, plugin manifest reference, docs,
and explicit scaffold/app selection. Cookie/session transport does not land in this issue and
the canonical module path is @netscript/plugin-auth-core/sdk. The contribution must compose
only through the Stage-2 public protocol and must not expose upstream oRPC identities.

Summary

@netscript/service/auth reads Authorization: Bearer … and x-api-key, and
createServiceClient cannot send either — the SDK has no concept of a credential at any layer.
NetScript's own CLI proves the consequence: it calls the auth service with raw fetch and
hand-rolled JSON shape-sniffing rather than the typed client it already depends on. This issue makes
auth the first dogfood consumer of the RFC-A contribution chain: a first-party authClient(…) /
authContribution(…) that attaches credentials, reads procedure policy metadata, surfaces a defined
credential error, and is declared from the auth plugin's manifest — so the seam is validated by the
sharpest real consumer rather than by a toy.

Evidence

Current surface

Server-side auth is real and correctly ordered — withAuthn/withAuthz install before RPC routes
mount, authz fails closed, and the principal reaches the oRPC handler context
(research/repo-audit/auth.md §1.1). Client-side there is nothing: no credential option, no
per-call override, no cookie forwarding, no emitter for the trusted-header authenticator that
packages/service/src/auth/trusted-header-authenticator.ts:32-54 is waiting for. The docs cannot
show an authenticated typed call because none can be written — the authz tutorial can only show
curl -H 'authorization: Bearer read' (docs/site/tutorials/workspace/05-route-authz.md:248-258).

Target contract

A first-party contribution shipped from the auth plugin, consumed through the RFC-A chain:

import { authContribution } from '@netscript/plugin-auth/sdk';

const users = createServiceClient({
  contract: UsersContractV1,
  serviceName: 'users',
  with: [authContribution({ scheme: 'bearer' })],
});
await users.list({ limit: 20 }, { context: { auth: { token } } });
  1. authContribution(options) declares { auth: { token: string } } as per-call context, so
    oRPC's ClientRest makes it required at the call site — omitting it is a compile error, not a
    401. A server-only variant (@netscript/plugin-auth/sdk/server) may close over a resolver that
    reads a secret; the isomorphic descriptor never does.
  2. authClient(...) is the convenience wrapper the CLI and generated apps use: a
    createServiceClient pre-composed with the auth contribution against authContractV1, replacing
    the raw-fetch client at
    packages/cli/src/public/features/plugins/auth/auth-session-client.ts.
  3. Policy-aware behaviour. The contribution reads NetScriptProcedureMeta.policy (RFC-A §3.6):
    it does not attach a credential to a policy.public procedure, and it raises a defined
    CREDENTIAL_UNAVAILABLE rather than sending an unauthenticated request to a non-public one.
  4. Declared from the manifest. The auth plugin declares the contribution through the
    PluginContributions client group added by RFC-A — with a nameable, non-closed shape (contrast
    plugin-contributions.ts:16).
  5. Scheme scope is bounded by RFC-A Q1. Bearer and x-api-key land here. Cookie/session
    transport does notresearch/repo-audit/auth.md §4.2 proves it cannot work across the
    current cross-origin discovery + __Host- prefix + origin:'*' CORS combination, and the
    topology decision is the auth pack's.

Acceptance

Boundaries

Docs/consumer proof

docs/site/identity-access/how-to/add-authentication.md and docs/site/services-sdk/sdk.md gain the
first authenticated typed-client example in the repo's history, and
docs/site/tutorials/workspace/05-route-authz.md:248-258 swaps its curl for that example. Consumer
proof is that NetScript's own CLI stops hand-rolling fetch against the auth service — the deleted
auth-session-client.ts request/parse code is the adoption evidence, and the two negative gates
prove the credential is actually load-bearing rather than decorative.

Provenance

Seed run plan-fable5-remediation-roadmap--seed, PR #1347, 2026-08-08. Sourced from
research/repo-audit/auth.md (G1, and §9's ordering that names auth as the first dogfood) and
research/repo-audit/services-sdk.md (S5); the absence of an authClient symbol and of any oRPC
.meta() usage was re-verified by grep against worktree baseline fac9e339042c. No GitHub mutation
was performed.


Filed from planning seed PR #1347 · source Draft-ID T1-05 · live issue #1352.

Metadata

Metadata

Assignees

No one assigned

    Projects

    No projects

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions