Skip to content

feat(security): ADR-0056 Option A — declaration-derived public-form authorization#2073

Merged
os-zhuang merged 1 commit into
mainfrom
claude/adr-0056-form-self-auth
Jun 20, 2026
Merged

feat(security): ADR-0056 Option A — declaration-derived public-form authorization#2073
os-zhuang merged 1 commit into
mainfrom
claude/adr-0056-form-self-auth

Conversation

@os-zhuang

Copy link
Copy Markdown
Contributor

What (ADR-0056 — Option A: declaration-derived public-form authorization)

Implements the recommended fix for the one blocker of the requireAuth default flip: public forms.

Today a public form submit relies on the anonymous fail-open (+ an optional, not-built-in guest_portal profile). Under secure-by-default that breaks. Option A makes the form self-authorizing from its declaration:

  • The form-submit route derives a narrow publicFormGrant: { object } from the matched form's target object.
  • The SecurityPlugin honors it as a least-privilege capabilitycreate + the immediate read-back on that object only, with no userId, no deployment guest_portal, and crucially not the anonymous fall-open.
  • The field allow-list is still enforced at the route; guest_portal/anonymous stay on the context for back-compat with guest-detection hooks.

This lets public forms work under requireAuth without any per-deployment config, scoped to exactly the declared object — and generalizes the platform principle "public access = declared + runtime-derived scoped grant" (the same shape share-links already use: validate → narrow grant, never a standing broad profile relied on by fail-open).

Verification

form-self-auth dogfood 3/3: create on the form target authorized (no userId / no guest_portal); cross-object create denied (narrow, not fall-open); update/delete denied. Regression: plugin-security 108, rest 121, full dogfood 98 — clean. Build 76/76.

Relation to ADR-0056

This satisfies the requireAuth-flip prerequisite documented in the D10 conformance matrix (#2071). With it, the only remaining step for the flip is the release-gated rollout (warn→enforce + migration note). Follow-on: update the matrix's requireAuth-default-flip note to point at this (both must be in main first).

🤖 Generated with Claude Code


Generated by Claude Code

…uthorization

Public form submit now derives a narrow `publicFormGrant: { object }` from the
form's declared target; the SecurityPlugin honors it as create + read-back on that
object ONLY (no userId, no deployment guest_portal, never the anonymous fall-open).
Lets public forms survive secure-by-default while staying least-privilege — the
prerequisite for the requireAuth default flip. Proven by form-self-auth dogfood
(3/3: target create allowed; cross-object + update/delete denied). No regression
(plugin-security 108, rest 121, full dogfood 98).

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01XVdnfUAx85amkerym26vdx
@vercel

vercel Bot commented Jun 20, 2026

Copy link
Copy Markdown

The latest updates on your projects. Learn more about Vercel for GitHub.

Project Deployment Actions Updated (UTC)
spec Ready Ready Preview, Comment Jun 20, 2026 10:00am

Request Review

@github-actions github-actions Bot added documentation Improvements or additions to documentation tests tooling size/m labels Jun 20, 2026
@github-actions

Copy link
Copy Markdown
Contributor

📓 Docs Drift Check

This PR changes 3 package(s): @objectstack/dogfood, @objectstack/plugin-security, @objectstack/rest.

9 hand-written doc(s) reference the affected code and may need an implementation-accuracy re-verification:

  • content/docs/concepts/implementation-status.mdx (via @objectstack/plugin-security, @objectstack/rest)
  • content/docs/concepts/packages.mdx (via @objectstack/plugin-security, @objectstack/rest)
  • content/docs/getting-started/cli.mdx (via @objectstack/plugin-security)
  • content/docs/guides/api-reference.mdx (via @objectstack/rest)
  • content/docs/guides/cheatsheets/permissions-matrix.mdx (via packages/plugins/plugin-security)
  • content/docs/guides/metadata/dashboard.mdx (via @objectstack/plugin-security)
  • content/docs/guides/packages.mdx (via @objectstack/plugin-security, @objectstack/rest)
  • content/docs/guides/plugins.mdx (via @objectstack/plugin-security, @objectstack/rest)
  • content/docs/guides/security.mdx (via @objectstack/plugin-security)

Advisory only. To re-verify, run the docs-accuracy-audit workflow scoped to these files:
node scripts/docs-audit/affected-docs.mjs origin/main → pass the list as args.docs.

@os-zhuang
os-zhuang marked this pull request as ready for review June 20, 2026 10:00
@os-zhuang
os-zhuang merged commit 751f5cf into main Jun 20, 2026
17 checks passed
@os-zhuang
os-zhuang deleted the claude/adr-0056-form-self-auth branch June 20, 2026 10:27
os-zhuang added a commit that referenced this pull request Jun 20, 2026
…ngs (#2075)

The nine ADR-0056 PRs merged but updated only one doc line (the email-RLS
note in #2054). This brings the hand-written security docs and the data
authoring skill in line with what actually shipped and is dogfood-proven:

- public-forms: public forms are now self-authorizing via a declaration-
  derived `publicFormGrant` (create + read-back on the form's target
  object). A `guest_portal` profile is no longer required — it is retained
  only for back-compat (hooks detecting a guest via falsy `ctx.user?.id`).
  (ADR-0056 Option A, #2073)
- implementation-status + security guide: corrected the "anonymous traffic
  bypasses enforcement" posture (boot warning lands in D2; public forms no
  longer depend on the fall-open; the default-deny flip is release-gated);
  marked OWD/sharing-model enforcement, the sharing-rule evaluator, and the
  app-declarable default profile as live.
- security guide: documented the `role_and_subordinates` sharing recipient
  (configurable role-hierarchy widening, D6).
- permissions-matrix: `object.sharingModel` now accepts the canonical OWD
  vocabulary (`private`/`public_read`/`public_read_write`/
  `controlled_by_parent`) alongside the legacy spellings (D1).
- objectstack-data skill: corrected the RLS section to the enforced
  `rowLevelSecurity` policy shape (`using`/`check` + `current_user.*`
  placeholders, incl. the new `current_user.email`), and flagged the
  object-level CEL `rls` config as experimental (D8).


Claude-Session: https://claude.ai/code/session_01XVdnfUAx85amkerym26vdx

Co-authored-by: Claude <noreply@anthropic.com>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

documentation Improvements or additions to documentation size/m tests tooling

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants