Skip to content

Fix dashboard auth review issues#98

Merged
iipanda merged 7 commits into
devfrom
codex/auth-review-followup
Jun 30, 2026
Merged

Fix dashboard auth review issues#98
iipanda merged 7 commits into
devfrom
codex/auth-review-followup

Conversation

@iipanda

@iipanda iipanda commented Jun 28, 2026

Copy link
Copy Markdown
Collaborator

Summary

  • harden dashboard auth/session proxying and role-gated navigation
  • fix invite acceptance preview flows and persist-before-send invite delivery behavior
  • add auth data invariants/migration coverage and worker route error handling fixes
  • update docs that contradicted the current dashboard/worker auth behavior

Validation

  • CI=true pnpm --filter worker test -- src/lib/auth/invites.test.ts src/lib/auth/invite-acceptance.test.ts src/lib/auth/users-read.test.ts src/routes/api/v1/invites.test.ts src/routes/api/v1/users.test.ts src/routes/webhooks/resend.post.test.ts src/routes/api/dashboard-auth/sso/start.get.test.ts src/routes/api/auth/auth-route.test.ts
  • CI=true pnpm --filter worker typecheck
  • CI=true WORKER_BASE_URL=http://localhost:3000 pnpm --filter ai-workflow-dashboard build
  • git diff --check

Summary by CodeRabbit

  • New Features
    • Cockpit now drives “manage users” UI from the authenticated session and shows users/admin screens only when permitted.
    • Added worker-backed dashboard session support plus SSO sign-in/status and richer invite acceptance for new, existing-password, and SSO-only users.
  • Bug Fixes
    • Improved login/password reset/logout and invite flows to trim/normalize inputs, handle multi-valued query params, and return clearer errors.
    • Enhanced auth/banner accessibility and improved cockpit navigation, trace labeling, and users screen state sync.
  • Documentation
    • Added dashboard auth migration/invariant guidance and verification checklist.

@chatgpt-codex-connector

Copy link
Copy Markdown

Codex usage limits have been reached for code reviews. Please check with the admins of this repo to increase the limits by adding credits.
Credits must be used to enable repository wide code reviews.

@vercel

vercel Bot commented Jun 28, 2026

Copy link
Copy Markdown

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

Project Deployment Actions Updated (UTC)
ai-workflow-app-dashboard Ready Ready Preview, Comment Jun 30, 2026 2:13am

Request Review

@coderabbitai

coderabbitai Bot commented Jun 28, 2026

Copy link
Copy Markdown

Review Change Stack

Important

Review skipped

Auto reviews are disabled on base/target branches other than the default branch.

Please check the settings in the CodeRabbit UI or the .coderabbit.yaml file in this repository. To trigger a single review, invoke the @coderabbitai review command.

⚙️ Run configuration

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro

Run ID: d85f0d3d-513e-43c1-8146-964043b7a748

You can disable this status message by setting the reviews.review_status to false in the CodeRabbit configuration file.

Use the checkbox below for a quick retry:

  • ✅ Review completed - (🔄 Check again to review again)
📝 Walkthrough

Walkthrough

This PR hardens the dashboard auth system end-to-end: DB-level constraints enforce case-insensitive email uniqueness, account composite uniqueness, and role allow-lists; a new /api/v1/session worker endpoint returns a typed DashboardSession; invite acceptance resolves a mode (new_user, existing_password, sso_only) and drives a multi-branch UI; invite management adds actor-org authorization, delivery-ID tracking, and failure-without-deletion semantics; and canManageUsers gates cockpit navigation items.

Changes

Auth hardening, invite acceptance flow, and cockpit permission gating

Layer / File(s) Summary
DB auth schema constraints and migrations
apps/worker/src/db/auth-schema.ts, apps/worker/drizzle/0004_cuddly_bishop.sql, apps/worker/drizzle/0005_quiet_fantastic_four.sql, apps/worker/drizzle/0007_auth_invariants.sql, apps/worker/drizzle/meta/*
auth-schema.ts replaces email.unique() with uniqueIndex on lower(email), adds composite uniqueIndex on account(providerId, accountId), and adds CHECK constraints on member.role and invitation.role. Migrations 0004, 0005, and 0007 apply these to deployed DBs; all Drizzle snapshots and the journal are updated.
Auth bootstrap hardening
apps/worker/scripts/seed-auth-user.ts, apps/worker/src/auth.ts
seed-auth-user.ts fails closed on missing env vars outside local dev. auth.ts normalizes emails to lowercase, rewrites ensureDashboardOrganization, ensureOwnerMembership, and ensureSsoProvider to insert-with-onConflictDoNothing patterns, adds isUniqueViolation helper, and sets SSO providersLimit to 10.
Session contract, /api/v1/session endpoint, and worker URL strictness
apps/dashboard/lib/auth/session.ts, apps/worker/src/routes/api/v1/session.get.ts, apps/dashboard/lib/api/proxy.ts, apps/dashboard/lib/api/server.ts, apps/dashboard/lib/auth/worker.ts, apps/worker/src/routes/api/auth/auth-route.test.ts
DashboardSession type (role + canManageUsers) is exported from session.ts; requireSession() now calls /api/v1/session and returns it. New worker route returns role and canInvite. proxy.ts, server.ts, and worker.ts add a workerUrl() helper that throws when WORKER_BASE_URL is missing. Auth catch-all test is refactored to mock the auth instance.
Invite acceptance state lookup, worker route, dashboard proxy, and UI
apps/worker/src/lib/auth/invite-acceptance.ts, apps/worker/src/lib/auth/invite-acceptance.test.ts, apps/worker/src/routes/api/dashboard-auth/invite/[inviteId].get.ts, apps/dashboard/app/api/auth/invite/[inviteId]/route.ts, apps/dashboard/app/invite/accept/invite-accept-form.tsx, apps/dashboard/app/invite/accept/page.tsx
getDashboardInviteAcceptanceState() resolves invite mode (new_user, existing_password, sso_only). A new worker GET route and a new dashboard API proxy route expose this. InviteAcceptForm fetches the state and renders mode-specific UI (name+password, current-password-only, or SSO-only banner). Page normalizes array searchParams.
Invite management authorization, email delivery correlation, and API error-handling
apps/worker/src/lib/auth/invites.ts, apps/worker/src/lib/auth/invites.test.ts, apps/worker/src/lib/email/invite-delivery.ts, apps/worker/src/lib/email/templates.ts, apps/worker/src/routes/api/v1/invites*, apps/worker/src/routes/api/v1/users*, apps/worker/src/routes/api/dashboard-auth/sso/*, apps/worker/src/routes/api/dashboard-auth/invite/accept.post.ts, apps/worker/src/routes/webhooks/resend.post.test.ts, apps/worker/src/lib/auth/users-read.ts
invites.ts adds assertActorInOrganization checks to all operations, passes deliveryId into sendInviteEmail, marks delivery failed instead of deleting invite on send error, and throws 409 when canceling a non-pending invite. invite-delivery.ts extracts deliveryId from Resend webhook tags and updates by id or resendEmailId. templates.ts centralizes HTML escaping inside renderEmailHtml. Route handlers move actor resolution inside try blocks; invites.post/resend forward invite_delivery_id tags to Resend.
Cockpit navigation permission gating and auth page polish
apps/dashboard/app/(cockpit)/layout.tsx, apps/dashboard/app/(cockpit)/cockpit-shell.tsx, apps/dashboard/components/cockpit/chrome.tsx, apps/dashboard/components/cockpit/mobile/more-sheet.tsx, apps/dashboard/components/cockpit/screens/users.tsx, apps/dashboard/app/login/page.tsx, apps/dashboard/app/reset-password/page.tsx, apps/dashboard/app/api/auth/sso/complete/route.ts, apps/dashboard/components/auth/auth-shell.tsx
CockpitLayout passes DashboardSession to CockpitShell. cockpitNavItems() filters nav by canManageUsers; CkSidebar and MoreSheet consume it. UsersScreen syncs local state from props and adds a return link. Login page branches on 401/403; reset-password and invite-accept pages normalize array searchParams; SSO complete trims the session token; AuthBanner gains ARIA live-region attributes.
Docs, trace rename, and live-polling spec
README.md, .claude/learnings.md, apps/dashboard/components/cockpit/screens/trace.tsx, docs/superpowers/...
README describes the dashboard as a cockpit BFF. learnings.md adds a deploy-verification checklist for auth schema invariants. trace.tsx renames isLive to isRunning. Live-polling toggle plan and spec move the toggle from the sidebar to the top bar and mobile header.

Estimated code review effort

🎯 5 (Critical) | ⏱️ ~120 minutes

Possibly related PRs

  • Blazity/ai-workflow#87: Directly related — modifies the same apps/dashboard/lib/api/server.ts worker URL and bearer Authorization forwarding logic updated in this PR.
  • Blazity/ai-workflow#88: Directly related — modifies cockpit-shell.tsx and more-sheet.tsx, the same files where canManageUsers is threaded in this PR.
  • Blazity/ai-workflow#96: Directly related — introduces the dashboard auth/users management codepaths (session gating, cockpit layout, invite management) that this PR hardens and extends.

Poem

🐇 Hopping through the schema maze,
Constraints added, emails lowered-cased,
Session tokens trimmed and traced,
Invites flow in three-mode grace,
canManageUsers proudly placed!
No rabbit left without a role —
Auth is tight from top to hole. 🔐

🚥 Pre-merge checks | ✅ 4
✅ Passed checks (4 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title is clearly related to the PR’s dashboard auth and session hardening work, even if it is broad.
Linked Issues check ✅ Passed Check skipped because no linked issues were found for this pull request.
Out of Scope Changes check ✅ Passed Check skipped because no linked issues were found for this pull request.
✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch codex/auth-review-followup

Comment @coderabbitai help to get the list of available commands.

@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: 6

🧹 Nitpick comments (3)
apps/worker/src/routes/api/v1/invites.test.ts (1)

185-189: 📐 Maintainability & Code Quality | 🔵 Trivial | ⚡ Quick win

Use arrayContaining for the tag assertion.

This only passes when tags is exactly a one-element array with invite_delivery_id in that slot. If the sender later adds another tag, the delivery-correlation contract still holds but the test will fail. expect.arrayContaining([expect.objectContaining({ name: "invite_delivery_id" })]) keeps the assertion focused on the real invariant.

Suggested test tweak
     expect(state.resendSend).toHaveBeenLastCalledWith(
       expect.objectContaining({
-        tags: [expect.objectContaining({ name: "invite_delivery_id" })],
+        tags: expect.arrayContaining([
+          expect.objectContaining({ name: "invite_delivery_id" }),
+        ]),
       }),
     );
🤖 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/worker/src/routes/api/v1/invites.test.ts` around lines 185 - 189, The
assertion in the resend invite test is too strict because it requires tags to be
exactly a single-item array; update the `state.resendSend` expectation in
`invites.test.ts` to use `expect.arrayContaining(...)` for the `tags` field so
it only verifies that a tag with `name: "invite_delivery_id"` is present. Keep
the rest of the `toHaveBeenLastCalledWith` check unchanged and use the existing
`expect.objectContaining` around the send payload.
apps/dashboard/app/(cockpit)/cockpit-shell.tsx (1)

72-74: 📐 Maintainability & Code Quality | 🔵 Trivial | ⚡ Quick win

Keep the “More” ids in one place.

These ids are also encoded in apps/dashboard/components/cockpit/mobile/more-sheet.tsx, so BottomTabBar.moreActive can drift from the sheet contents. Reuse a shared constant/helper for both paths.

🤖 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/dashboard/app/`(cockpit)/cockpit-shell.tsx around lines 72 - 74, The
“More” tab ids are duplicated between cockpit-shell.tsx and more-sheet.tsx,
which can cause BottomTabBar.moreActive to drift from the sheet contents. Move
the ids into a shared constant or helper and have both cockpitNavItems()
filtering and the mobile more-sheet.tsx contents read from that single source.
Use the shared symbol to keep the active-state logic and sheet entries in sync.
apps/dashboard/lib/auth/session.ts (1)

22-31: 🗄️ Data Integrity & Integration | 🔵 Trivial | ⚡ Quick win

Validate the session payload at the boundary.

Line 31 trusts arbitrary JSON with a type cast, so a worker/dashboard contract drift becomes a silent auth-UI regression instead of a controlled failure. Parse the response and verify role/canManageUsers before returning it.

🤖 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/dashboard/lib/auth/session.ts` around lines 22 - 31, requireSession
currently trusts the /api/v1/session JSON via a type cast, so the session
payload should be validated before returning it. Update the requireSession
function to parse the fetchAuthWorker response and verify the expected
DashboardSession shape, especially role and canManageUsers, and redirect to
/login if the payload is missing or malformed. Use the existing requireSession
symbol as the boundary check point so contract drift fails closed instead of
propagating invalid session data.
🤖 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/worker/drizzle/0007_auth_invariants.sql`:
- Around line 2-7: The auth invariants migration currently adds unique indexes
and role checks directly, so it can fail on existing bad data. Update the
migration around the user/account/invitation/member DDL to include a preflight
cleanup or validation step before enforcing these constraints, handling
duplicate lowercased user emails, duplicate account provider/account pairs, and
invalid invitation/member roles. Use the existing migration block in the auth
invariants SQL to locate where to insert the cleanup so the constraints can be
applied safely.

In `@apps/worker/scripts/seed-auth-user.ts`:
- Around line 26-45: The missing-env check in seed-auth-user.ts only treats
falsy values as unset, so whitespace-only strings can still pass and later fail
in the auth setup flow. Update the validation in the seed-auth-user script to
trim each required env value before evaluating it, and use the trimmed result
consistently when building missingRequiredEnv and when continuing into the
bootstrap logic. Keep the existing control flow around allowLocalSkip and the
throw path, but ensure DASHBOARD_AUTH_EMAIL, DASHBOARD_AUTH_PASSWORD,
BETTER_AUTH_URL, DATABASE_URL, and BETTER_AUTH_SECRET are considered missing if
they are blank after trimming.

In `@apps/worker/src/lib/auth/invite-acceptance.ts`:
- Around line 53-58: The invite acceptance state is hardcoding the role to
"member", which loses the real privilege from `invitation.role`. Update
`DashboardInviteAcceptanceState` and the related invite flow in
`acceptInvitation`/dashboard form wiring to carry through the actual invite role
(`owner | admin | member`) from `invite.role` instead of narrowing it. Make sure
the preview UI and downstream state consumers read that propagated role so
admin/owner invites are labeled correctly.

In `@apps/worker/src/routes/api/dashboard-auth/invite/accept.post.ts`:
- Around line 10-14: The accept.post handler is swallowing all readBody()
failures by defaulting to an empty object, which turns malformed JSON and other
body-read errors into misleading missing-field responses. Update the body
parsing in this route to either let the readBody() error propagate or catch it
and rethrow a clear invalid-body error, and keep the later
inviteId/name/password validation separate so the existing field checks only run
when the request body was parsed successfully.

In `@apps/worker/src/routes/api/dashboard-auth/sso/consume.post.ts`:
- Around line 8-15: The SSO consume handler is trimming the token before
validating its type, which can throw on non-string payloads and bypass the
intended 400 response. Update the token handling in the consume post route to
first confirm `body.token` is a string in `consumeDashboardSsoHandoff`’s caller
path, then trim it and reject missing/empty values with the existing
`createError` flow so bad requests stay as 400s.

In `@docs/superpowers/specs/2026-06-22-live-polling-toggle-design.md`:
- Around line 118-139: The cockpit context names in this spec don’t match the
real contract, so update the wording to use the actual symbols exposed by the
shared state. Replace references to live and onToggleLive with livePolling and
toggleLive, and keep nextRefreshAt where needed so the desktop top bar and
mobile header implementation aligns with the existing cockpit context API.

---

Nitpick comments:
In `@apps/dashboard/app/`(cockpit)/cockpit-shell.tsx:
- Around line 72-74: The “More” tab ids are duplicated between cockpit-shell.tsx
and more-sheet.tsx, which can cause BottomTabBar.moreActive to drift from the
sheet contents. Move the ids into a shared constant or helper and have both
cockpitNavItems() filtering and the mobile more-sheet.tsx contents read from
that single source. Use the shared symbol to keep the active-state logic and
sheet entries in sync.

In `@apps/dashboard/lib/auth/session.ts`:
- Around line 22-31: requireSession currently trusts the /api/v1/session JSON
via a type cast, so the session payload should be validated before returning it.
Update the requireSession function to parse the fetchAuthWorker response and
verify the expected DashboardSession shape, especially role and canManageUsers,
and redirect to /login if the payload is missing or malformed. Use the existing
requireSession symbol as the boundary check point so contract drift fails closed
instead of propagating invalid session data.

In `@apps/worker/src/routes/api/v1/invites.test.ts`:
- Around line 185-189: The assertion in the resend invite test is too strict
because it requires tags to be exactly a single-item array; update the
`state.resendSend` expectation in `invites.test.ts` to use
`expect.arrayContaining(...)` for the `tags` field so it only verifies that a
tag with `name: "invite_delivery_id"` is present. Keep the rest of the
`toHaveBeenLastCalledWith` check unchanged and use the existing
`expect.objectContaining` around the send payload.
🪄 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: 2015e3a9-b119-4eb6-b70e-3a11b71a347f

📥 Commits

Reviewing files that changed from the base of the PR and between ab1631b and f483fb1.

📒 Files selected for processing (55)
  • .claude/learnings.md
  • README.md
  • apps/dashboard/app/(cockpit)/cockpit-shell.tsx
  • apps/dashboard/app/(cockpit)/layout.tsx
  • apps/dashboard/app/api/auth/invite/[inviteId]/route.ts
  • apps/dashboard/app/api/auth/sso/complete/route.ts
  • apps/dashboard/app/invite/accept/invite-accept-form.tsx
  • apps/dashboard/app/invite/accept/page.tsx
  • apps/dashboard/app/login/page.tsx
  • apps/dashboard/app/reset-password/page.tsx
  • apps/dashboard/components/auth/auth-shell.tsx
  • apps/dashboard/components/cockpit/chrome.tsx
  • apps/dashboard/components/cockpit/mobile/more-sheet.tsx
  • apps/dashboard/components/cockpit/screens/trace.tsx
  • apps/dashboard/components/cockpit/screens/users.tsx
  • apps/dashboard/lib/api/proxy.ts
  • apps/dashboard/lib/api/server.ts
  • apps/dashboard/lib/auth/session.ts
  • apps/dashboard/lib/auth/worker.ts
  • apps/worker/drizzle/0004_cuddly_bishop.sql
  • apps/worker/drizzle/0005_quiet_fantastic_four.sql
  • apps/worker/drizzle/0007_auth_invariants.sql
  • apps/worker/drizzle/meta/0004_snapshot.json
  • apps/worker/drizzle/meta/0005_snapshot.json
  • apps/worker/drizzle/meta/0006_snapshot.json
  • apps/worker/drizzle/meta/0007_snapshot.json
  • apps/worker/drizzle/meta/_journal.json
  • apps/worker/scripts/seed-auth-user.ts
  • apps/worker/src/auth.ts
  • apps/worker/src/db/auth-schema.ts
  • apps/worker/src/lib/auth/invite-acceptance.test.ts
  • apps/worker/src/lib/auth/invite-acceptance.ts
  • apps/worker/src/lib/auth/invites.test.ts
  • apps/worker/src/lib/auth/invites.ts
  • apps/worker/src/lib/auth/users-read.ts
  • apps/worker/src/lib/email/invite-delivery.ts
  • apps/worker/src/lib/email/templates.ts
  • apps/worker/src/routes/api/auth/auth-route.test.ts
  • apps/worker/src/routes/api/dashboard-auth/invite/[inviteId].get.ts
  • apps/worker/src/routes/api/dashboard-auth/invite/accept.post.ts
  • apps/worker/src/routes/api/dashboard-auth/sso/consume.post.ts
  • apps/worker/src/routes/api/dashboard-auth/sso/start.get.test.ts
  • apps/worker/src/routes/api/dashboard-auth/sso/start.get.ts
  • apps/worker/src/routes/api/v1/invites.get.ts
  • apps/worker/src/routes/api/v1/invites.post.ts
  • apps/worker/src/routes/api/v1/invites.test.ts
  • apps/worker/src/routes/api/v1/invites/[inviteId]/cancel.post.ts
  • apps/worker/src/routes/api/v1/invites/[inviteId]/resend.post.ts
  • apps/worker/src/routes/api/v1/session.get.ts
  • apps/worker/src/routes/api/v1/users.get.ts
  • apps/worker/src/routes/api/v1/users.test.ts
  • apps/worker/src/routes/api/v1/users/[userId]/role.patch.ts
  • apps/worker/src/routes/webhooks/resend.post.test.ts
  • docs/superpowers/plans/2026-06-22-live-polling-toggle.md
  • docs/superpowers/specs/2026-06-22-live-polling-toggle-design.md

Comment thread apps/worker/drizzle/0007_auth_invariants.sql
Comment thread apps/worker/scripts/seed-auth-user.ts Outdated
Comment thread apps/worker/src/lib/auth/invite-acceptance.ts
Comment thread apps/worker/src/routes/api/dashboard-auth/invite/accept.post.ts Outdated
Comment thread apps/worker/src/routes/api/dashboard-auth/sso/consume.post.ts
Comment thread docs/superpowers/specs/2026-06-22-live-polling-toggle-design.md Outdated

@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

Caution

Some comments are outside the diff and can’t be posted inline due to platform limitations.

⚠️ Outside diff range comments (2)
apps/dashboard/lib/auth/worker.ts (1)

34-37: 🩺 Stability & Availability | 🟠 Major | ⚡ Quick win

Keep missing-WORKER_BASE_URL on the existing null-return path.

Line 34 now throws before fetchAuthWorker() reaches its try/catch, so a missing WORKER_BASE_URL turns these auth routes into uncaught 500s instead of the intended authWorkerUnavailable(...) 502 flow.

Proposed fix
-  const url = workerUrl(path);
-
   try {
-    return await fetch(url, {
+    return await fetch(workerUrl(path), {
🤖 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/dashboard/lib/auth/worker.ts` around lines 34 - 37, The
missing-WORKER_BASE_URL check in fetchAuthWorker() is now happening before the
existing try/catch, which bypasses the intended null-return handling and turns
auth routes into uncaught 500s. Move the workerUrl(path) resolution back into
the guarded path or otherwise catch the missing-base-url case inside
fetchAuthWorker() so it returns null and lets authWorkerUnavailable(...)
preserve the 502 flow.
apps/worker/src/lib/auth/invite-acceptance.ts (1)

253-274: 🗄️ Data Integrity & Integration | 🟠 Major | ⚡ Quick win

Honor the invite role when the user is already in the org.

ensureInviteMembership() still returns early on any existing membership, so an existing member who accepts an admin/owner invite keeps the old role even though the invite is marked accepted. That makes the new dynamic-role flow incorrect for already-enrolled users. This helper needs to reconcile member.role with input.role here, or the flow should reject role-changing invites before acceptance.

🤖 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/worker/src/lib/auth/invite-acceptance.ts` around lines 253 - 274,
ensureInviteMembership() is skipping role reconciliation for users who already
exist in memberTable, so accepting an invite with a higher role leaves the old
membership role unchanged. Update the existing-member path in
ensureInviteMembership() to either update memberTable.role to input.role when
the current role differs, or explicitly reject role-changing invites before
acceptance; keep the insert path unchanged for new members.
🤖 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/worker/src/lib/auth/seed-auth-env.ts`:
- Around line 9-17: `DASHBOARD_ORIGIN` is incorrectly listed in `OPTIONAL_ENV`,
which lets `seed-auth-user.ts` skip it even though the worker requires it.
Update `seed-auth-env.ts` so `DASHBOARD_ORIGIN` is treated as required, keeping
it out of the optional list used by the auth seeding flow and aligned with the
validation in `apps/worker/env.ts`.

In `@apps/worker/src/routes/api/dashboard-auth/invite/accept.post.ts`:
- Around line 10-21: The request parsing in accept.post.ts only checks JSON
syntax and then trusts the inferred body shape, so malformed field types can
still reach acceptDashboardInvite() and crash on .trim(). Update the body
validation after readBody(event) to reject non-object payloads, require inviteId
and password to be strings, and allow name only when it is absent or a string;
keep the existing Invalid request body handling for any type mismatch before
calling acceptDashboardInvite().

---

Outside diff comments:
In `@apps/dashboard/lib/auth/worker.ts`:
- Around line 34-37: The missing-WORKER_BASE_URL check in fetchAuthWorker() is
now happening before the existing try/catch, which bypasses the intended
null-return handling and turns auth routes into uncaught 500s. Move the
workerUrl(path) resolution back into the guarded path or otherwise catch the
missing-base-url case inside fetchAuthWorker() so it returns null and lets
authWorkerUnavailable(...) preserve the 502 flow.

In `@apps/worker/src/lib/auth/invite-acceptance.ts`:
- Around line 253-274: ensureInviteMembership() is skipping role reconciliation
for users who already exist in memberTable, so accepting an invite with a higher
role leaves the old membership role unchanged. Update the existing-member path
in ensureInviteMembership() to either update memberTable.role to input.role when
the current role differs, or explicitly reject role-changing invites before
acceptance; keep the insert path unchanged for new members.
🪄 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: e7817df3-0b2d-4808-ac7d-c965d6a0c1c0

📥 Commits

Reviewing files that changed from the base of the PR and between f483fb1 and 7f45fee.

📒 Files selected for processing (28)
  • .claude/learnings.md
  • apps/dashboard/app/(cockpit)/cockpit-shell.tsx
  • apps/dashboard/app/api/auth/forgot-password/route.ts
  • apps/dashboard/app/api/auth/login/route.ts
  • apps/dashboard/app/api/auth/logout/route.ts
  • apps/dashboard/app/api/auth/reset-password/route.ts
  • apps/dashboard/app/api/auth/sso/status/route.ts
  • apps/dashboard/app/invite/accept/invite-accept-form.tsx
  • apps/dashboard/app/login/page.tsx
  • apps/dashboard/components/cockpit/chrome.tsx
  • apps/dashboard/components/cockpit/mobile/more-sheet.tsx
  • apps/dashboard/lib/auth/session.ts
  • apps/dashboard/lib/auth/worker.ts
  • apps/worker/drizzle/0007_auth_invariants.sql
  • apps/worker/scripts/seed-auth-user.ts
  • apps/worker/src/db/auth-schema.test.ts
  • apps/worker/src/lib/auth/invite-acceptance.test.ts
  • apps/worker/src/lib/auth/invite-acceptance.ts
  • apps/worker/src/lib/auth/seed-auth-env.test.ts
  • apps/worker/src/lib/auth/seed-auth-env.ts
  • apps/worker/src/routes/api/dashboard-auth/invite/accept.post.test.ts
  • apps/worker/src/routes/api/dashboard-auth/invite/accept.post.ts
  • apps/worker/src/routes/api/dashboard-auth/sso/consume.post.test.ts
  • apps/worker/src/routes/api/dashboard-auth/sso/consume.post.ts
  • apps/worker/src/routes/api/dashboard-auth/sso/status.get.test.ts
  • apps/worker/src/routes/api/dashboard-auth/sso/status.get.ts
  • apps/worker/src/routes/api/v1/invites.test.ts
  • docs/superpowers/specs/2026-06-22-live-polling-toggle-design.md
✅ Files skipped from review due to trivial changes (2)
  • apps/worker/src/lib/auth/seed-auth-env.test.ts
  • .claude/learnings.md
🚧 Files skipped from review as they are similar to previous changes (8)
  • apps/worker/src/routes/api/dashboard-auth/sso/consume.post.ts
  • apps/worker/src/routes/api/v1/invites.test.ts
  • apps/dashboard/components/cockpit/chrome.tsx
  • apps/worker/scripts/seed-auth-user.ts
  • apps/dashboard/lib/auth/session.ts
  • apps/dashboard/app/(cockpit)/cockpit-shell.tsx
  • docs/superpowers/specs/2026-06-22-live-polling-toggle-design.md
  • apps/dashboard/app/invite/accept/invite-accept-form.tsx

Comment thread apps/worker/src/lib/auth/seed-auth-env.ts
Comment thread apps/worker/src/routes/api/dashboard-auth/invite/accept.post.ts Outdated

@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.

🧹 Nitpick comments (1)
apps/dashboard/lib/auth/worker.test.ts (1)

4-6: 📐 Maintainability & Code Quality | 🔵 Trivial | ⚡ Quick win

Exercise fetchAuthWorker() here, or rename the test.

The test name claims wrapper coverage, but it bypasses fetchAuthWorker() and only exercises fetchWorker(). That won’t catch regressions in the wrapper’s env handoff or any wrapper-specific request shaping.

🤖 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/dashboard/lib/auth/worker.test.ts` around lines 4 - 6, The test name and
implementation are mismatched: it claims to cover fetchAuthWorker(), but it only
calls fetchWorker() directly. Update the test in worker.test.ts to exercise
fetchAuthWorker() so it verifies the wrapper’s env handoff and request shaping,
or rename the test to match the lower-level fetchWorker() behavior if that’s the
intended scope. Use the fetchAuthWorker and fetchWorker symbols to align the
test with the actual code path.
🤖 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.

Nitpick comments:
In `@apps/dashboard/lib/auth/worker.test.ts`:
- Around line 4-6: The test name and implementation are mismatched: it claims to
cover fetchAuthWorker(), but it only calls fetchWorker() directly. Update the
test in worker.test.ts to exercise fetchAuthWorker() so it verifies the
wrapper’s env handoff and request shaping, or rename the test to match the
lower-level fetchWorker() behavior if that’s the intended scope. Use the
fetchAuthWorker and fetchWorker symbols to align the test with the actual code
path.

ℹ️ Review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro

Run ID: 644ae343-6bb7-4d87-b401-dfa72120af40

📥 Commits

Reviewing files that changed from the base of the PR and between 7f45fee and 3806ada.

📒 Files selected for processing (9)
  • apps/dashboard/lib/auth/worker-core.ts
  • apps/dashboard/lib/auth/worker.test.ts
  • apps/dashboard/lib/auth/worker.ts
  • apps/worker/src/lib/auth/invite-acceptance.test.ts
  • apps/worker/src/lib/auth/invite-acceptance.ts
  • apps/worker/src/lib/auth/seed-auth-env.test.ts
  • apps/worker/src/lib/auth/seed-auth-env.ts
  • apps/worker/src/routes/api/dashboard-auth/invite/accept.post.test.ts
  • apps/worker/src/routes/api/dashboard-auth/invite/accept.post.ts
✅ Files skipped from review due to trivial changes (1)
  • apps/dashboard/lib/auth/worker-core.ts
🚧 Files skipped from review as they are similar to previous changes (4)
  • apps/worker/src/lib/auth/seed-auth-env.test.ts
  • apps/worker/src/lib/auth/seed-auth-env.ts
  • apps/worker/src/lib/auth/invite-acceptance.test.ts
  • apps/worker/src/lib/auth/invite-acceptance.ts

@iipanda iipanda merged commit 18d91e9 into dev Jun 30, 2026
7 checks passed
@iipanda iipanda mentioned this pull request Jun 30, 2026
This was referenced Jul 1, 2026
Merged
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