Skip to content

Tighten blog admin auth and validation handling - #52

Merged
cobraprojects merged 2 commits into
mainfrom
clawpatch-fix
Jun 15, 2026
Merged

Tighten blog admin auth and validation handling#52
cobraprojects merged 2 commits into
mainfrom
clawpatch-fix

Conversation

@cobraprojects

@cobraprojects cobraprojects commented Jun 15, 2026

Copy link
Copy Markdown
Owner

Summary by CodeRabbit

  • New Features

    • Added a Next.js health check endpoint for monitoring.
    • Introduced field projection for selected API responses to reduce payload sizes.
  • Bug Fixes

    • Improved email verification resend behavior to consistently return success responses and handle validation errors more reliably.
    • Strengthened admin authentication/authorization checks and tightened route parameter validation.
  • Refactor

    • Simplified logout flows across supported authentication providers to return the underlying logout responses directly.
  • Tests

    • Added/expanded coverage for admin pages, auth flows, and API routes (including health and verification resend).

@coderabbitai

coderabbitai Bot commented Jun 15, 2026

Copy link
Copy Markdown

Review Change Stack

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: 25051b2c-b505-4620-81ef-8e2c2b469fe8

📥 Commits

Reviewing files that changed from the base of the PR and between 50ba025 and a0d2442.

📒 Files selected for processing (2)
  • apps/blog-next/tests/edit-tag-page.test.mjs
  • apps/blog-nuxt/app/pages/categories/[slug].vue
🚧 Files skipped from review as they are similar to previous changes (2)
  • apps/blog-nuxt/app/pages/categories/[slug].vue
  • apps/blog-next/tests/edit-tag-page.test.mjs

📝 Walkthrough

Walkthrough

Refactors logoutWithClerk and logoutWithWorkos to return Promise<Response> directly, removing structured result types. Inlines auth() checks into every admin Next.js page and action, replacing the deleted requireAdminAuth() helper. Extends isValidationException to detect serialized payloads in nested wrappers. Updates CLI codegen to derive auth user types from the model registry and simplify generated logout scaffolds. Adds getAdminPostFormData, a health endpoint, and Nuxt useFetch pick narrowing. Expands the test suite significantly.

Changes

Auth Logout: Response Return Type

Layer / File(s) Summary
Clerk logout contract, implementation, and tests
packages/auth-clerk/src/contracts.ts, packages/auth-clerk/src/index.ts, packages/auth-clerk/tests/package.test.ts
ClerkAuthFacade.logoutWithClerk return type changed from Promise<ClerkLogoutResult> to Promise<Response>. Implementation rewritten to return 303 redirects with forwarded Set-Cookie headers; error path returns Response.json with status 500. ClerkLogoutResult removed from exports. Tests updated to assert Response shape, location headers, and cookie clearing.
WorkOS logout contract, implementation, and tests
packages/auth-workos/src/contracts.ts, packages/auth-workos/src/index.ts, packages/auth-workos/tests/package.test.ts
Same refactor applied to logoutWithWorkos. Returns 303 redirect to WorkOS logout URL with Set-Cookie forwarding; missing-session path returns 303 to /; error returns 500. WorkosLogoutResult removed. Tests updated to assert response status and location headers.
Logout route handlers simplified across all apps and CLI scaffolds
apps/blog-next/app/api/auth/clerk/logout/route.ts, apps/blog-next/app/api/auth/workos/logout/route.ts, apps/blog-nuxt/server/api/auth/clerk/logout.post.ts, apps/blog-nuxt/server/api/auth/workos/logout.post.ts, apps/blog-sveltekit/src/routes/api/auth/clerk/logout/+server.ts, apps/blog-sveltekit/src/routes/api/auth/workos/logout/+server.ts, packages/cli/src/project/registry-svelte.ts, packages/cli/src/project/scaffold/framework-renderers.ts
All six app-level logout handlers now directly return the provider logout helper result, removing local provider() checks and manual redirect/error shaping. CLI templates for Nuxt, Next, and SvelteKit generate the same simplified pattern.

Admin Auth Inline (blog-next)

Layer / File(s) Summary
Remove requireAdminAuth, add getAdminPostFormData, authorId override
apps/blog-next/app/admin/auth.ts, apps/blog-next/server/lib/blog.ts, apps/blog-next/app/storage/[[...path]]/route.ts, apps/blog-next/server/holo.ts
requireAdminAuth() deleted entirely. getAdminPostFormData() added to fetch categories and tags for the new-post form. createPost now accepts caller-supplied authorId to support action-provided author assignment. Storage route delegates to generated module; server/holo.ts export removed.
Admin layout, dashboard, and post pages: inline auth checks
apps/blog-next/app/admin/layout.tsx, apps/blog-next/app/admin/page.tsx, apps/blog-next/app/admin/posts/[id]/edit/page.tsx, apps/blog-next/app/admin/posts/new/page.tsx, apps/blog-next/app/admin/categories/[id]/edit/page.tsx, apps/blog-next/app/admin/tags/[id]/edit/page.tsx
Admin layout, dashboard, and edit-post pages now each call auth() and redirect to /login when unauthenticated, then authorize('viewAny', Post) before proceeding. Edit-post page adds per-record authorize('update', post). New-post page uses getAdminPostFormData. Category and tag edit pages add positive-integer regex ID validation before database lookup.
Admin server actions: inline auth per action
apps/blog-next/app/admin/actions.ts
All admin actions call auth(), redirect to /login when unauthenticated, and call authorize('viewAny', Post) before resource-level authorization. createPostAction injects authorId from currentAuth.user.id. Validation error handling remains unchanged; actions continue to return error objects instead of throwing.

Validation, Codegen, and Minor Features

Layer / File(s) Summary
isValidationException: serialized payload detection
packages/validation/src/contracts.ts, packages/validation/tests/contracts.test.ts
Adds isSerializedValidationExceptionPayload and findSerializedValidationExceptionPayload helpers to detect and extract serialized exceptions. isValidationException now returns true for exceptions nested in body/data/error/cause fields, including SvelteKit-style {status, body} error wrappers.
Resend-email validation exception suppression
apps/blog-next/app/api/verify-email/resend/route.ts, apps/blog-nuxt/server/api/verify-email/resend.post.ts, apps/blog-sveltekit/src/routes/api/verify-email/resend/+server.ts
All three resend endpoints gain resendSuccessResponse() helper and wrap the resend call in try/catch that swallows validation exceptions while rethrowing unexpected ones. All return the same generic 200 success payload.
AuthorizationTargetInstance type inference
packages/authorization/src/contracts.ts
Adds AuthorizationQueryTargetInstance helper conditional type; model targets with TInstance = unknown now resolve to the type inferred from query().first() method, improving type precision for dynamically-loaded models.
CLI: renderGeneratedAuthTypes derives user type from model registry
packages/cli/src/project/registry.ts, packages/cli/tests/authorization-registry.test.ts
renderGeneratedAuthTypes signature updated to accept normalized auth config plus model entries. Derives per-provider model names and conditionally augments HoloAuth.TypeRegistry with a user intersection type. New test verifies provider-derived user type output.
Nuxt: remove blog type aliases, narrow useFetch with pick
apps/blog-nuxt/server/lib/blog.ts, apps/blog-nuxt/app/pages/...
Eight exported Awaited<ReturnType<...>> type aliases removed from blog.ts. All Nuxt useFetch calls in admin, category, tag, and home pages add pick option to limit fetched field payloads.
Health endpoint, register root error, storage route
apps/blog-next/app/api/holo/health/route.ts, apps/blog-next/app/register/page.tsx
Adds /api/holo/health endpoint returning {ok:true, framework:'next'}. Register page reads form.errors.first('_root') and conditionally renders a root error paragraph above fields.

Test Suite Expansion (blog-next)

Layer / File(s) Summary
Auth route tests
apps/blog-next/tests/api-v1-routes.test.mjs, apps/blog-next/tests/broadcast-auth-route.test.mjs, apps/blog-next/tests/hosted-callback-routes.test.mjs, apps/blog-next/tests/hosted-login-routes.test.mjs, apps/blog-next/tests/hosted-logout-routes.test.mjs, apps/blog-next/tests/hosted-register-routes.test.mjs, apps/blog-next/tests/login-route.test.mjs, apps/blog-next/tests/super-admin-logout-route.test.mjs, apps/blog-next/tests/verify-email-resend-route.test.mjs, apps/blog-next/tests/verify-email-route.test.mjs, apps/blog-next/tests/reset-password-route.test.mjs
New and updated tests for all auth-related routes. Assertions updated to expect HTTP Response objects and JSON payloads instead of thrown errors for validation and credential failures. Tests verify response status, headers, and redirect behavior.
Admin page tests
apps/blog-next/tests/admin-actions.test.mjs, apps/blog-next/tests/admin-categories-page.test.mjs, apps/blog-next/tests/admin-dashboard-page.test.mjs, apps/blog-next/tests/admin-posts-page.test.mjs, apps/blog-next/tests/admin-tags-page.test.mjs, apps/blog-next/tests/edit-category-page.test.mjs, apps/blog-next/tests/edit-post-page.test.mjs, apps/blog-next/tests/edit-tag-page.test.mjs, apps/blog-next/tests/new-post-page.test.mjs
Full coverage for admin CRUD pages, edit pages (including ID validation rejecting non-canonical inputs), authorization ordering verification, and updated action validation assertions to expect resolved 422 responses.
Public page and misc tests
apps/blog-next/tests/health-route.test.mjs, apps/blog-next/tests/home-page.test.mjs, apps/blog-next/tests/post-detail-page.test.mjs, apps/blog-next/tests/register-page.test.mjs, apps/blog-next/tests/reset-password-page.test.mjs, apps/blog-next/tests/super-admin-page.test.mjs, apps/blog-next/tests/run.mjs, packages/core/tests/authorization-feature.test.ts, tests/example-app-auth-flow.mjs
New tests for home page rendering, post detail page with 404 behavior, health endpoint contract, register root error display, reset-password form data assertions, and super-admin page auth guard. Test runner expanded to cover all new test files; integration test timeout increased; e2e resend expectation updated to HTTP 200 success.

Estimated code review effort

🎯 5 (Critical) | ⏱️ ~120 minutes

Possibly related PRs

  • cobraprojects/holo-js#31: Directly overlaps with changes to apps/blog-next/app/api/verify-email/resend/route.ts — both PRs modify the resend handler's validation exception handling and response structure.
  • cobraprojects/holo-js#37: Both PRs touch apps/blog-next/app/api/auth/clerk/logout/route.ts, changing how the Clerk logout handler returns/handles logoutWithClerk results.

Poem

🐇 Hop hop, the old guard helper's gone away,
Each action now calls auth() — hip hooray!
Logout returns a Response, clean and bright,
No more { data, error } in the night.
The validation bunny sniffs each nested shell,
And the test suite grew quite large — oh well!
✨ A tidier warren, from root to tail!

✨ 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 clawpatch-fix

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Actionable comments posted: 2

🧹 Nitpick comments (1)
apps/blog-next/app/admin/actions.ts (1)

26-30: ⚡ Quick win

Consider extracting repeated auth check pattern into a helper.

The same 5-line authentication check and redirect pattern is repeated across all 9 actions. While the PR intentionally inlines these checks, a small helper could reduce duplication without losing clarity:

async function requireAuth() {
  const currentAuth = await auth()
  if (!currentAuth.authenticated || !currentAuth.user) {
    redirect('/login')
  }
  return currentAuth
}

Each action could then use const currentAuth = await requireAuth().

Also applies to: 58-62, 89-93, 101-105, 116-120, 132-136, 145-149, 157-161, 170-174

🤖 Prompt for 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.

In `@apps/blog-next/app/admin/actions.ts` around lines 26 - 30, Extract the
repeated 5-line authentication check pattern that appears at lines 26-30, 58-62,
89-93, 101-105, 116-120, 132-136, 145-149, 157-161, and 170-174 in
apps/blog-next/app/admin/actions.ts into a new helper function called
requireAuth(). The helper should call auth(), verify currentAuth.authenticated
and currentAuth.user are truthy (redirecting to '/login' if not), and return the
currentAuth object. Replace all 9 occurrences of the repeated pattern with a
single call to await requireAuth() and assign the result to currentAuth.
🤖 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 `@apps/blog-next/tests/edit-tag-page.test.mjs`:
- Around line 22-24: The vi.mock() call is using an incorrect path that does not
match the actual import path used by the page being tested. The page imports
updateTagAction from app/admin/actions.ts, but the mock is targeting
../app/admin/tags/actions.ts. Update the path in the vi.mock() call to match the
actual import path used by the page so that the mock for updateTagAction is
properly applied during the test.

In `@apps/blog-nuxt/app/pages/categories/`[slug].vue:
- Around line 3-5: The route slug parameter is being interpolated directly into
the fetch URL without proper encoding, which allows reserved characters like
forward slashes to break the URL structure. Fix this by wrapping
route.params.slug with encodeURIComponent() when constructing the URL in the
useFetch call, so that special characters are properly escaped before being used
in the API path.

---

Nitpick comments:
In `@apps/blog-next/app/admin/actions.ts`:
- Around line 26-30: Extract the repeated 5-line authentication check pattern
that appears at lines 26-30, 58-62, 89-93, 101-105, 116-120, 132-136, 145-149,
157-161, and 170-174 in apps/blog-next/app/admin/actions.ts into a new helper
function called requireAuth(). The helper should call auth(), verify
currentAuth.authenticated and currentAuth.user are truthy (redirecting to
'/login' if not), and return the currentAuth object. Replace all 9 occurrences
of the repeated pattern with a single call to await requireAuth() and assign the
result to currentAuth.
🪄 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: 7b49aee5-5283-4cdd-b857-b2423c3b9de8

📥 Commits

Reviewing files that changed from the base of the PR and between 0a981a3 and 50ba025.

📒 Files selected for processing (71)
  • apps/blog-next/app/admin/actions.ts
  • apps/blog-next/app/admin/auth.ts
  • apps/blog-next/app/admin/categories/[id]/edit/page.tsx
  • apps/blog-next/app/admin/layout.tsx
  • apps/blog-next/app/admin/page.tsx
  • apps/blog-next/app/admin/posts/[id]/edit/page.tsx
  • apps/blog-next/app/admin/posts/new/page.tsx
  • apps/blog-next/app/admin/tags/[id]/edit/page.tsx
  • apps/blog-next/app/api/auth/clerk/logout/route.ts
  • apps/blog-next/app/api/auth/workos/logout/route.ts
  • apps/blog-next/app/api/holo/health/route.ts
  • apps/blog-next/app/api/verify-email/resend/route.ts
  • apps/blog-next/app/register/page.tsx
  • apps/blog-next/app/storage/[[...path]]/route.ts
  • apps/blog-next/server/holo.ts
  • apps/blog-next/server/lib/blog.ts
  • apps/blog-next/tests/admin-actions.test.mjs
  • apps/blog-next/tests/admin-categories-page.test.mjs
  • apps/blog-next/tests/admin-dashboard-page.test.mjs
  • apps/blog-next/tests/admin-posts-page.test.mjs
  • apps/blog-next/tests/admin-tags-page.test.mjs
  • apps/blog-next/tests/api-v1-routes.test.mjs
  • apps/blog-next/tests/broadcast-auth-route.test.mjs
  • apps/blog-next/tests/edit-category-page.test.mjs
  • apps/blog-next/tests/edit-post-page.test.mjs
  • apps/blog-next/tests/edit-tag-page.test.mjs
  • apps/blog-next/tests/health-route.test.mjs
  • apps/blog-next/tests/home-page.test.mjs
  • apps/blog-next/tests/hosted-callback-routes.test.mjs
  • apps/blog-next/tests/hosted-login-routes.test.mjs
  • apps/blog-next/tests/hosted-logout-routes.test.mjs
  • apps/blog-next/tests/hosted-register-routes.test.mjs
  • apps/blog-next/tests/login-route.test.mjs
  • apps/blog-next/tests/new-post-page.test.mjs
  • apps/blog-next/tests/post-detail-page.test.mjs
  • apps/blog-next/tests/register-page.test.mjs
  • apps/blog-next/tests/reset-password-page.test.mjs
  • apps/blog-next/tests/reset-password-route.test.mjs
  • apps/blog-next/tests/run.mjs
  • apps/blog-next/tests/super-admin-logout-route.test.mjs
  • apps/blog-next/tests/super-admin-page.test.mjs
  • apps/blog-next/tests/verify-email-resend-route.test.mjs
  • apps/blog-next/tests/verify-email-route.test.mjs
  • apps/blog-nuxt/app/pages/admin/index.vue
  • apps/blog-nuxt/app/pages/admin/posts/[id]/edit.vue
  • apps/blog-nuxt/app/pages/admin/posts/index.vue
  • apps/blog-nuxt/app/pages/categories/[slug].vue
  • apps/blog-nuxt/app/pages/index.vue
  • apps/blog-nuxt/app/pages/tags/[slug].vue
  • apps/blog-nuxt/server/api/auth/clerk/logout.post.ts
  • apps/blog-nuxt/server/api/auth/workos/logout.post.ts
  • apps/blog-nuxt/server/api/verify-email/resend.post.ts
  • apps/blog-nuxt/server/lib/blog.ts
  • apps/blog-sveltekit/src/routes/api/auth/clerk/logout/+server.ts
  • apps/blog-sveltekit/src/routes/api/auth/workos/logout/+server.ts
  • apps/blog-sveltekit/src/routes/api/verify-email/resend/+server.ts
  • packages/auth-clerk/src/contracts.ts
  • packages/auth-clerk/src/index.ts
  • packages/auth-clerk/tests/package.test.ts
  • packages/auth-workos/src/contracts.ts
  • packages/auth-workos/src/index.ts
  • packages/auth-workos/tests/package.test.ts
  • packages/authorization/src/contracts.ts
  • packages/cli/src/project/registry-svelte.ts
  • packages/cli/src/project/registry.ts
  • packages/cli/src/project/scaffold/framework-renderers.ts
  • packages/cli/tests/authorization-registry.test.ts
  • packages/core/tests/authorization-feature.test.ts
  • packages/validation/src/contracts.ts
  • packages/validation/tests/contracts.test.ts
  • tests/example-app-auth-flow.mjs
💤 Files with no reviewable changes (3)
  • apps/blog-next/app/admin/auth.ts
  • apps/blog-next/server/holo.ts
  • apps/blog-nuxt/server/lib/blog.ts

Comment thread apps/blog-next/tests/edit-tag-page.test.mjs Outdated
Comment thread apps/blog-nuxt/app/pages/categories/[slug].vue Outdated
@cobraprojects
cobraprojects merged commit f29bbeb into main Jun 15, 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