docs: add how-to guide for anonymous/pre-account access control - #1012
Conversation
Documents the access-scoped pattern for pre-account and anonymous flows (session.userId with no owning row yet) so sudo() + hand-rolled ownership checks stop being the only discoverable option: scope by traversal from session.userId, deny explicitly on a null session, force ownership on create via resolveInput, use validate for choices a hook can't force, and contrast context.withSession() with sudo(). Closes #966 Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_016YUoqak9UpdBu1RednLXNy
|
|
The latest updates on your projects. Learn more about Vercel for GitHub.
|
There was a problem hiding this comment.
💡 Codex Review
Here are some automated review suggestions for this pull request.
Reviewed commit: 636269ef5e
ℹ️ About Codex in GitHub
Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you
- Open a pull request for review
- Mark a draft as ready
- Comment "@codex review".
If Codex has suggestions, it will comment; otherwise it will react with 👍.
Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".
Coverage Report for Core Package Coverage (./packages/core)
File CoverageNo changed files found. |
Coverage Report for UI Package Coverage (./packages/ui)
File CoverageNo changed files found. |
Coverage Report for CLI Package Coverage (./packages/cli)
File CoverageNo changed files found. |
Coverage Report for Auth Package Coverage (./packages/auth)
File CoverageNo changed files found. |
Coverage Report for Storage Package Coverage (./packages/storage)
File CoverageNo changed files found. |
Coverage Report for RAG Package Coverage (./packages/rag)
File CoverageNo changed files found. |
Coverage Report for Storage S3 Package Coverage (./packages/storage-s3)
File CoverageNo changed files found. |
Coverage Report for Storage Vercel Package Coverage (./packages/storage-vercel)
File CoverageNo changed files found. |
…guide The worked example named its business list Account, which collides with authPlugin()'s own default Account list (the OAuth/credential model) the moment a reader's config actually uses authPlugin() — the guide's own premise. Renamed to Workspace and fixed the owner relationship to target the default auth User list (it referenced a nonexistent AuthUser). Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_016YUoqak9UpdBu1RednLXNy
|
Deployment failed for project stack-docs with the following error: Learn More: https://vercel.com/open-saas?upgradeToPro=build-rate-limit |
Self-reviewRan a code review pass on this PR before requesting human review. Two correctness findings, both confirmed against the actual codebase and fixed in cea1f8d:
Re-ran Generated by Claude Code |
Two P1s from Codex review on PR #1012, both real bugs in the worked example: - The auth User list ships closed by default (ADR-0013), so the nested `owner: { connect }` in Workspace.resolveInput would be denied without explicit self-only query access on User — every Workspace create in the guide would fail as written. - Template had no query access at all, so the validate hook's scoped findFirst lookup would always return null and reject every legitimate template selection. Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_016YUoqak9UpdBu1RednLXNy
|
Addressed both P1 findings from the Codex review (72e44e5) — replied and resolved both threads. The Generated by Claude Code |
Summary
Adds a how-to guide showing the access-scoped pattern for pre-account and anonymous flows — a signed-in-but-not-yet-onboarded session (real
session.userId, no owning row yet) — sosudo()+ hand-rolled ownership checks stop being the only discoverable option.Per triage on #966, this is a documentation gap, not a missing mechanism: everything the guide shows already exists in the engine. The page is built around one continuous worked example (
Account→Project→Template) and establishes:session.userId, not a derived session field ({ account: { user: { id: { equals: session.userId } } } }resolves correctly whether or not the owning row exists yet — fail-closed by construction).session ? {…} : false, never a filter built from a possibly-nullsession.userId).resolveInput, overwriting the owner field from the session rather than validating a client-supplied value.validatefor what a hook can't force (e.g. a relationship target — an existingTemplate— that must belong to the caller).context.withSession()substitutes the session without elevating, contrasted explicitly withsudo().sudo()is still the right call, and what it costs (bypasses operation- and field-level access; every check under it is hand-rolled and fail-open).Also links the new guide from the Access Control concepts page and registers it in docs navigation (How-to → Build).
No engine or package code changes.
Test plan
pnpm link-check(docs) passes — all internal links resolvepnpm build(docs) succeeds and statically generates the new pagepnpm formatrun over the changed filescreateaccess truly has noinputData(only field-level access does) againstpackages/core/src/access/types.ts, and that returning a filter fromcreateaccess is currently treated as full allow (write-pipeline.ts) — matches what the guide says not to doCloses #966
Generated by Claude Code