Skip to content

feat(auth): Privy operator authentication and console gating - #57

Merged
kapustazh merged 10 commits into
developfrom
feature/privy-operator-auth
Sep 9, 2026
Merged

kapustazh merged 10 commits into
developfrom
feature/privy-operator-auth

Conversation

@kapustazh

@kapustazh kapustazh commented Sep 9, 2026 •

Copy link
Copy Markdown
Collaborator

Summary

Adds Privy ES256 operator access-token authentication to @oneshot/api with an operator DID allowlist and DID-only audit logging, fails closed when operator auth configuration is partial or malformed, preserves opaque SERVICE_BEARER_TOKEN credentials for machine/worker clients via credential-shape routing, and gates the @oneshot/web console behind an in-memory operator session (lazy-loaded Privy SDK) or machine token with 0 token leakage into the DOM or persistent storage.

Scope and acceptance criteria

  • The change is limited to the stated milestone or issue (feature/privy-operator-auth).
  • Acceptance criteria are listed and satisfied:
    • ES256 access tokens verified for issuer privy.io and app audience with clock skew tolerance.
    • Allowlist DID or 403 with DID-only audit logging (no tokens in logs).
    • Rejection of invalid JWTs (wrong alg/issuer/audience/tampered/expired).
    • Preservation of opaque SERVICE_BEARER_TOKEN for machine/worker clients.
    • Forbidden reconciliation never reaches ledger (pre-handler returns 403).
    • API Privy config absent preserves service-only behavior; partial config fails closed.
    • Web console stays gated until Privy session or in-memory machine token is active.
    • Signed-in DID is visible/copyable while tokens are never rendered, logged, or persisted in DOM/storage.
    • Privy React provider is lazy-loaded and omitted when disabled.
    • Unit, browser, build, lint, and typecheck checks hold.
  • No unrelated cleanup is included.

Product and security invariants

  • Tenant isolation remains fail-closed.
  • Sponsor authorization, auditability, and daily caps remain enforced where applicable.
  • Recipients cannot modify sponsor controls or access sponsor-only data.
  • No secret, token, production identifier, or personal data is committed or pasted into review prompts.
  • Any non-applicable invariant is explained below.

Invariant notes:

  • Settlement-path invariant (1 intent / at most 1 settlement) is untouched; the auth gate runs before all /v1/* routes.
  • Reconciliation invariant (UNKNOWN reconciles without blind retry) is preserved; the endpoint remains auth-gated and forbidden requests return 403 before touching the ledger.

Validation

Commands and results:

- pnpm --filter @oneshot/api test: 5 files / 40 tests passed
- pnpm --filter @oneshot/web test: 9 files / 50 tests passed
- pnpm --filter @oneshot/web test:browser: 7/7 Playwright tests passed
- pnpm test (full monorepo): 64 files / 942 tests passed
- pnpm typecheck: PASS
- pnpm lint: PASS
- pnpm format:check: PASS
- pnpm check:generated: PASS
- pnpm peers check: PASS
- pnpm --filter @oneshot/web build: PASS (Privy emitted as lazy chunk)
- GitHub Actions CI (all 4 jobs): PASS (ESLint & TypeScript, Browser Acceptance, Markdown & Mermaid, repository-policy)

Independent review evidence

Gate A — exact candidate tree before push

  • Base commit SHA: 78b9d74bd4b111a6418485baff6379464325960c

  • Candidate tree SHA: e24156d694e64866f872c9de5402a2d378296598

  • Candidate commit SHA (if already committed but unpushed): 18a554eb362f5331f10684b7c22a6d427056e183

  • Reviewer tool: free-pi-cli

  • Reviewer model: deepseek/deepseek-v4-flash-0731

  • Verdict: VERDICT: PASS

  • Findings or residual risks:

    • Non-blocking: apps/web/src/auth/session.ts machine-token fallback during transient null access-token window while status === 'SIGNED_IN'.
    • Non-blocking: JWT-shaped SERVICE_BEARER_TOKEN would route to Privy verifier (opaque tokens preserved).
    • Non-blocking: Audit log interpolates verified DID directly (basic newline sanitization recommended).
    • Non-blocking: Privy lazy chunk triggers Vite chunk-size warning (normal for full auth SDK).
    • Non-blocking: Local Node 22 vs engine 24 warning (CI verified on pinned Node 24).
  • The reviewed tree equals the committed tree.

Gate B — exact remote PR head

  • Pull request URL/number: feat(auth): Privy operator authentication and console gating #57 feat(auth): Privy operator authentication and console gating #57

  • Remote head commit SHA: 18a554eb362f5331f10684b7c22a6d427056e183

  • Remote head tree SHA: e24156d694e64866f872c9de5402a2d378296598

  • Reviewer tool: free-pi-cli

  • Reviewer model: glm-5.3-flash

  • Verdict: VERDICT: PASS

  • Findings or residual risks:

    • Non-blocking: apps/api/src/runtime.ts:31 DID audit string newline sanitization.
    • Non-blocking: document that SERVICE_BEARER_TOKEN must not be JWT-shaped.
    • Non-blocking: machine-token fallback during transient null access-token window while status === 'SIGNED_IN'.
    • Non-blocking: @privy-io/react-auth pinned at 3.6.1 for peer graph compatibility.
  • Gate B reviewed the current remote head and matches Gate A's approved tree, or a fresh Gate A was run for the changed tree.

  • Agent policy / repository-policy and all applicable CI checks pass.

Risk and rollback

  • Residual risks: Operator access tokens expire in ~1h; refresh failures fall back to re-login or machine token.
  • Rollback or recovery plan: git revert this PR cleanly returns to service-bearer-only authentication.

Human merge

  • A human owner has reviewed the evidence and will perform the merge.

@cloudflare-workers-and-pages

cloudflare-workers-and-pages Bot commented Sep 9, 2026 •

Copy link
Copy Markdown

Deploying with  Cloudflare Workers  Cloudflare Workers

The latest updates on your project. Learn more about integrating Git with Workers.

Status Name Latest Commit Updated (UTC)
✅ Deployment successful!
View logs
oneshot 18a554e Sep 09 2026, 11:21 PM

@kapustazh
kapustazh marked this pull request as ready for review September 9, 2026 23:24
@kapustazh
kapustazh merged commit 1125134 into develop Sep 9, 2026
5 checks passed
@kapustazh
kapustazh deleted the feature/privy-operator-auth branch September 9, 2026 23:25
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant