feat(server): plan for auth and read-only mode (#312)#334
Conversation
Replace the prior design doc (server-auth-readonly-design.md) with a single forward-looking plan where each phase pairs code with the tests that lock its behavior. Reflects the final CLI (--enable-auth as the source of truth, --server-config, mandatory plain:/bcrypt: password prefix) and a deliberately small architecture — two modes (Authenticated / Disabled), --enable-auth without creds errors out at startup, password verifier as an enum, require_auth mounted before the /api nest to sidestep OriginalUri pitfalls. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
181bc41 to
a38c29e
Compare
|
Hello again, I've reworked the plan to fold the test strategy in and simplify the CLI. CLI simplification: Single Architecture simplification: Two modes only — |
|
Hello, I'm really interested in this since I'm not feeling good about letting anyone write on my server or delete recipes. When do you think you will be able to implement this ? (No pressure intended 😄 ) |
|
For info, I asked Claude to review the plan, here is his comments: Strengths
Issues Worth Fixing
The AuthConfig struct holds the raw prefixed string: But AuthState.config: Option means the password is never the parsed Password enum at rest — verify() would need to re-parse on every login
This is the biggest ambiguity in the architecture section.
"Bind to :0, read the port, drop the listener, hand the port to the child process" — between dropping the listener and the child binding, another process can
The with_session_path builder method sets a custom path, but the default isn't specified. If tests don't call with_session_path, they may share the
The plan tests next=//evil.com open redirect, but doesn't cover:
These are the common bypass vectors for open-redirect guards. Add them to sanitize_next_basic.
"both paths ≥ 250 ms" — this will occasionally fail on a loaded CI machine that takes > 250 ms on unrelated overhead, or pass when the server is slow for
Minor Points
Verdict The plan is implementable as-is. The Password storage ambiguity (issue # 1) and session file isolation (issue # 3) should be clarified before Phase 1 begins, |
|
Hello @romainhild ,
It is my first contribution to this repository (and, as I said, I'm beginner with rust) so I was waiting for feedback and comments from people familiar with the codebase 🙂.
I'll take a look at it and adjust the plan accordingly, thank you for your feedback 🙏. |
Address the review on PR cooklang#334: - store the parsed Password in AuthState (fail-fast at startup, no re-parse) - default COOK_SESSION_FILE to a temp path in tests for hermeticity - discover the test port via `--port 0` + stdout (drop the racy pick_free_port) - make the login delay injectable (COOK_LOGIN_DELAY_MS) for a deterministic constant-delay test - add the encoded `%2F%2Fevil.com` open-redirect vector to sanitize_next tests - model AuthContext as an enum so illegal auth states are unrepresentable - note the wildcard CORS / cookie-auth interaction and the /ws/lsp CSRF caveat - scope fail-fast password parsing to --enable-auth only - document session-store growth as a known limitation - add a `cook server --help` assertion in Phase 6 Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
f546ea0 to
3f106c9
Compare
|
Follow-up to my earlier reply, this lands with the commit that revises the plan, so most points are now in the diff. A few things worth reading beyond it:
|
Hello there,
This PR addresses cooklang/cookcli#312 — making the
cook serverwrite operations require authentication, while keeping anonymous read access available by default.What's in here
A single new file:
docs/plans/2026-04-29-server-auth-plan.md, structured as:server.tomlwith mandatoryplain:/bcrypt:prefixed password)and an extensiblewith a plain Password enum (extended later by adding a variant)PasswordVerifiertrait--auth,--no-auth,--auth-config--enable-auth,--server-config, pluscook server hash-passwordsubcommand)ThreeTwoAuthModestates (Authenticated/ReadOnly/Disabled) and a resolution tableAuthModestates (Authenticated/Disabled) and a resolution table;--enable-authwithout credentials is a startup error rather than a thirdReadOnlyvariantAuthContext, hidden write actions, login page)A 7-phase implementation breakdownA 6-phase implementation breakdown where each phase pairs code with the tests that lock its behaviourNotable design decisions (vs. the issue)
Disabled(legacy behavior preserved with a console warning) instead ofReadOnly, to avoid a breaking change for existing users.Switching to read-only requires explicitEnabling protection requires explicit --enable-auth together with credentials in server.toml; passing the flag alone aborts at startup.--auth/api/sync/*flow is unchanged, just protected by the new local auth.Looking for feedback on
Disabledvs strictly following the issue'sReadOnly)Whether thedropped the trait in favor of a plain Password enumPasswordVerifiertrait abstraction is overkill for shipping justplain+bcryptDisclosure
I'm new to Rust, so I'm using Claude Code to help me explore the codebase, design the plan, and (later) write idiomatic Rust. All design decisions and the final wording of this plan are my own — I've reviewed everything carefully — but it's only fair to flag that a coding assistant is in the loop.