Skip to content

Move CSRF handling into security middleware#47

Merged
cobraprojects merged 3 commits into
mainfrom
autharization-blog-test
May 26, 2026
Merged

Move CSRF handling into security middleware#47
cobraprojects merged 3 commits into
mainfrom
autharization-blog-test

Conversation

@cobraprojects
Copy link
Copy Markdown
Owner

@cobraprojects cobraprojects commented May 26, 2026

Summary by CodeRabbit

  • New Features

    • Middleware-based CSRF protection for Next/Nuxt/SvelteKit plus a helper to render CSRF form inputs and a client-side config cookie.
  • Bug Fixes

    • Unsafe requests are now rejected early with clear CSRF failure status.
  • Refactor

    • Forms no longer require a csrf option—CSRF fields are attached automatically; server throttling remains available.
  • Documentation

    • Updated framework guides and examples to reflect middleware-driven CSRF and throttling.

Review Change Stack

@coderabbitai
Copy link
Copy Markdown

coderabbitai Bot commented May 26, 2026

No actionable comments were generated in the recent review. 🎉

ℹ️ Recent review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro

Run ID: dd62c2a2-f94b-4202-a2c2-8130a5f26200

📥 Commits

Reviewing files that changed from the base of the PR and between 3a9a0d6 and da54d88.

📒 Files selected for processing (1)
  • packages/security/tests/framework-middleware.test.ts

📝 Walkthrough

Walkthrough

This PR moves CSRF enforcement into framework middleware, adds cookie-based client config and a csrf.input() helper, removes csrf options from validate/useForm, updates apps to rely on middleware for CSRF, and updates tests/docs accordingly.

Changes

CSRF Middleware Redesign

Layer / File(s) Summary
Security contracts and CSRF core
packages/security/src/contracts.ts, packages/security/src/csrf.ts, packages/security/src/client-config.ts, packages/security/src/client.ts, packages/security/src/index.ts
CSRF verification refactored to use cookie-signed tokens as authority; added csrf.input() returning hidden-input metadata; added client-config cookie parsing and cookie-backed client config reading.
Framework CSRF middleware
packages/security/src/next/server.ts, packages/security/src/nuxt/server.ts, packages/security/src/sveltekit/server.ts, packages/security/src/framework-shim.d.ts
Implements csrfProtection() middleware factories that call protect(request), map SecurityCsrfError to 419/h3 errors, and conditionally issue CSRF and client-config cookies on safe methods.
Form validation & client behavior
packages/forms/src/contracts.ts, packages/forms/src/internal/client.ts, packages/forms/src/client-security.ts, packages/forms/src/security.ts
Removed csrf?: boolean from options; validate() activates security only via throttle; client auto-attaches CSRF field for unsafe methods; getClientCsrfField() returns undefined instead of throwing when cookie/config missing.
Auth route-protection cleanup
packages/auth/src/next/server.ts, packages/auth/src/nuxt/server.ts, packages/auth/src/sveltekit/server.ts, packages/auth/src/runtime/csrfCookie.ts, packages/auth/src/nuxt-shim.d.ts
Removed CSRF-cookie helpers and provisioning from route guards; routeProtection internals no longer expose cookie issuance helpers; csrfCookie.ts removed.
Next.js blog app
apps/blog-next/...
Removed csrf: true from login/register validate/useForm calls and tests; added CSRF-first handling in proxy.ts to run middleware before auth protect; updated CSRF config to exclude /api/v1/*.
Nuxt blog app
apps/blog-nuxt/...
Removed csrf: true from useForm and API validate calls; added server/middleware/csrf.ts exporting csrfProtection(); updated CSRF config exceptions.
SvelteKit blog app
apps/blog-sveltekit/...
Mounted csrfProtection() in hooks; added page load() functions returning csrf.input(request); forms use spread data.csrf.input; layout no longer provides CSRF; removed csrf: true usage.
Tests & docs
packages/forms/tests/*, packages/security/tests/*, packages/auth/tests/*, apps/*/tests/*, apps/docs/*
Updated tests to remove csrf: true expectations and CSRF-cookie signing assertions; added middleware integration tests; updated docs and examples to the middleware-first CSRF model.
Packaging & adapters
packages/security/package.json, packages/security/tsup.config.ts, packages/adapter-next/src/client.ts
Added framework server exports and tsup entries; adapter-next stops considering csrf in option equality; client config now reads cookie-provided settings.

Estimated code review effort

🎯 4 (Complex) | ⏱️ ~60 minutes

Possibly related PRs

"

🐰 I hopped the cookie path and hid a key,
Middleware guards now watch the plea;
Forms draw tokens from the cookie’s chest,
Guards do the checking — let the middleware rest.
"

✨ Finishing Touches
📝 Generate docstrings
  • Create stacked PR
  • Commit on current branch
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch autharization-blog-test

Copy link
Copy Markdown

@coderabbitai coderabbitai Bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 3

🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In `@packages/security/src/next/server.ts`:
- Around line 51-73: The code currently always sets the CSRF and client-config
cookies for every safe request in issueCsrfCookie; change it to short-circuit
and only set cookies when needed by reading existing cookies from the incoming
request (use request.cookies.get(config.csrf.cookie) and
request.cookies.get(SECURITY_CLIENT_CONFIG_COOKIE)), then: 1) for the CSRF
cookie, only set it when the cookie is missing or invalid — verify the existing
value using the csrf verification API you have (e.g., csrf.verify or equivalent)
instead of regenerating unconditionally; 2) for the client config cookie,
compute the new value with
serializeSecurityClientConfig(createSecurityClientConfig(config)) and only call
response.cookies.set for SECURITY_CLIENT_CONFIG_COOKIE when the serialized value
differs from the existing cookie value; keep using isSecureRequest(request) and
the same cookie options when setting. Ensure the rest of the flow (importing
NextResponse, returning undefined for non-safe or disabled CSRF) remains
unchanged.

In `@packages/security/src/nuxt/server.ts`:
- Around line 45-64: The issue: issueCsrfCookie currently resets both cookies on
every safe request causing unnecessary Set-Cookie headers; fix by checking
existing cookie values and only calling setCookie when absent, invalid, or
stale. In issueCsrfCookie use the incoming Request/H3Event to read current
cookies (check config.csrf.cookie and SECURITY_CLIENT_CONFIG_COOKIE), validate
the existing CSRF token (e.g., compare/verify with csrf.token or csrf.verify
equivalent) and compare serialized createSecurityClientConfig(config) to the
existing client-config cookie (or add a version/timestamp check), and only call
setCookie(...) for the specific cookie(s) that need updating rather than
unconditionally setting both.
- Around line 35-42: createRequest currently rebuilds a Nuxt Request without the
incoming body, breaking protect() -> readFormToken() paths; fix by reading the
event body and passing it into the new Request: in createRequest (and any helper
it calls) read the raw request body (e.g. await readBody(event) or await
getRawBody(event.node.req) as appropriate), preserve Content-Type and other
relevant headers, and include the body in the Request constructor options so
readFormToken() can access form data; ensure the body read is awaited before
constructing the Request to avoid consuming the stream twice and keep
createRequest async.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro

Run ID: f0a543c3-5500-4dc9-8479-638ad087625f

📥 Commits

Reviewing files that changed from the base of the PR and between a6f2f2d and 9d30795.

⛔ Files ignored due to path filters (1)
  • bun.lock is excluded by !**/*.lock
📒 Files selected for processing (65)
  • apps/blog-next/app/api/login/route.ts
  • apps/blog-next/app/api/register/route.ts
  • apps/blog-next/app/login/actions.ts
  • apps/blog-next/app/login/page.tsx
  • apps/blog-next/app/register/actions.ts
  • apps/blog-next/app/register/page.tsx
  • apps/blog-next/config/security.ts
  • apps/blog-next/proxy.ts
  • apps/blog-next/tests/login-page.test.mjs
  • apps/blog-next/tests/register-page.test.mjs
  • apps/blog-nuxt/app/pages/login/index.vue
  • apps/blog-nuxt/app/pages/register/index.vue
  • apps/blog-nuxt/config/security.ts
  • apps/blog-nuxt/server/api/login.post.ts
  • apps/blog-nuxt/server/api/register.post.ts
  • apps/blog-nuxt/server/middleware/csrf.ts
  • apps/blog-sveltekit/config/security.ts
  • apps/blog-sveltekit/src/hooks.server.ts
  • apps/blog-sveltekit/src/routes/+layout.server.ts
  • apps/blog-sveltekit/src/routes/login/+page.server.ts
  • apps/blog-sveltekit/src/routes/login/+page.svelte
  • apps/blog-sveltekit/src/routes/register/+page.server.ts
  • apps/blog-sveltekit/src/routes/register/+page.svelte
  • apps/blog-sveltekit/src/routes/super-admin/login/+page.server.ts
  • apps/blog-sveltekit/src/routes/super-admin/login/+page.svelte
  • apps/blog-sveltekit/tests/auth-page-actions.test.mjs
  • apps/docs/docs/auth/current-auth-client.md
  • apps/docs/docs/forms/client-usage.md
  • apps/docs/docs/forms/framework-integration.md
  • apps/docs/docs/forms/server-validation.md
  • apps/docs/docs/security.md
  • packages/adapter-next/src/client.ts
  • packages/adapter-sveltekit/tests/runtime.test.ts
  • packages/auth/src/next/server.ts
  • packages/auth/src/nuxt-shim.d.ts
  • packages/auth/src/nuxt/server.ts
  • packages/auth/src/runtime/csrfCookie.ts
  • packages/auth/src/sveltekit/server.ts
  • packages/auth/tests/framework.test.ts
  • packages/forms/src/client-security.ts
  • packages/forms/src/contracts.ts
  • packages/forms/src/internal/client.ts
  • packages/forms/src/security.ts
  • packages/forms/tests/client.test.ts
  • packages/forms/tests/client.type.test.ts
  • packages/forms/tests/contracts.test.ts
  • packages/forms/tests/contracts.type.test.ts
  • packages/forms/tests/security.test.ts
  • packages/security/package.json
  • packages/security/src/client-config.ts
  • packages/security/src/client.ts
  • packages/security/src/contracts.ts
  • packages/security/src/csrf.ts
  • packages/security/src/framework-shim.d.ts
  • packages/security/src/index.ts
  • packages/security/src/next/server.ts
  • packages/security/src/nuxt/server.ts
  • packages/security/src/sveltekit/server.ts
  • packages/security/tests/client.test.ts
  • packages/security/tests/client.type.test.ts
  • packages/security/tests/docs-smoke.test.ts
  • packages/security/tests/framework-middleware.test.ts
  • packages/security/tests/package.test.ts
  • packages/security/tests/sveltekit.test.ts
  • packages/security/tsup.config.ts
💤 Files with no reviewable changes (21)
  • apps/blog-nuxt/server/api/register.post.ts
  • apps/blog-next/app/api/register/route.ts
  • apps/blog-nuxt/server/api/login.post.ts
  • apps/blog-next/app/login/actions.ts
  • packages/adapter-next/src/client.ts
  • packages/forms/tests/client.type.test.ts
  • apps/blog-next/app/login/page.tsx
  • apps/blog-next/tests/login-page.test.mjs
  • packages/auth/src/runtime/csrfCookie.ts
  • apps/blog-next/app/api/login/route.ts
  • apps/blog-next/app/register/page.tsx
  • packages/security/tests/client.type.test.ts
  • packages/auth/src/nuxt-shim.d.ts
  • apps/blog-nuxt/app/pages/login/index.vue
  • packages/auth/src/sveltekit/server.ts
  • apps/blog-next/app/register/actions.ts
  • apps/blog-sveltekit/tests/auth-page-actions.test.mjs
  • apps/blog-next/tests/register-page.test.mjs
  • packages/forms/src/security.ts
  • apps/docs/docs/auth/current-auth-client.md
  • apps/blog-nuxt/app/pages/register/index.vue

Comment thread packages/security/src/next/server.ts
Comment thread packages/security/src/nuxt/server.ts
Comment thread packages/security/src/nuxt/server.ts
@cobraprojects
Copy link
Copy Markdown
Owner Author

@CodeRabbit review

@coderabbitai
Copy link
Copy Markdown

coderabbitai Bot commented May 26, 2026

✅ Actions performed

Review triggered.

Note: CodeRabbit is an incremental review system and does not re-review already reviewed commits. This command is applicable only when automatic reviews are paused.

Copy link
Copy Markdown

@coderabbitai coderabbitai Bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 1

🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In `@packages/security/tests/framework-middleware.test.ts`:
- Around line 112-113: The test currently asserts the previous token value isn't
present by checking staleConfigResponse?.headers.get('set-cookie') doesn't
contain `XSRF-TOKEN=${encodeURIComponent(token)`, which can false-negative if a
new token with a different value is issued; instead assert that no CSRF cookie
is reissued by checking the cookie name prefix is absent (e.g. ensure the
'set-cookie' header does not contain the `XSRF-TOKEN=` name) while still
ensuring the configuration cookie exists via `SECURITY_CLIENT_CONFIG_COOKIE=`;
update the two expectations that reference
staleConfigResponse?.headers.get('set-cookie') accordingly so the first checks
for the cookie name absence and the second still checks for
`${SECURITY_CLIENT_CONFIG_COOKIE}=`.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro

Run ID: 325f535b-9d5c-49a2-9f82-6bf72559ec15

📥 Commits

Reviewing files that changed from the base of the PR and between 9d30795 and 3a9a0d6.

📒 Files selected for processing (3)
  • packages/security/src/next/server.ts
  • packages/security/src/nuxt/server.ts
  • packages/security/tests/framework-middleware.test.ts

Comment thread packages/security/tests/framework-middleware.test.ts Outdated
@cobraprojects cobraprojects merged commit bf9cb6e into main May 26, 2026
1 check passed
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