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-inguest_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 requireAuthwithout 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-security108, rest121, 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

vercelBot commented Jun 20, 2026

Copy link
Copy Markdown

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

ProjectDeploymentActionsUpdated (UTC)
specReadyReadyPreview, CommentJun 20, 2026 10:00am

Request Review

@github-actionsgithub-actionsBot 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 mainJun 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 freeto join this conversation on GitHub. Already have an account? Sign in to comment

Labels

documentationImprovements or additions to documentationsize/mteststooling

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants

@os-zhuang@claude