Skip to content

feat(auth): Hazel sign-in on @yielded/auth (backend + Clerk user migration) - #329

Merged
Makisuo merged 11 commits into
mainfrom
auth/yielded-backend
Oct 13, 2026
Merged

Makisuo merged 11 commits into
mainfrom
auth/yielded-backend

Merge main and fix Hazel auth review findings

7b899c9
Select commit
Loading
Failed to load commit list.
Maple Review Bot / Maple / review completed Oct 13, 2026 in 14m 8s

Confidence 6/10 · 1 issue to address

🟡 Confidence 6/10 · needs attention
Auth/tenancy and migration code carries high risk, and the new auth routes' span coverage was not verified; earlier-review fixes all hold at this head.
quality 88/100 · 1 warning · 1 note · tests covered · risk high · 11 files not read

Adds Hazel's own sign-in on @yielded/auth (routes, session resolution, Clerk-identity import, Postgres schema) alongside Clerk, gated on AUTH_GITHUB_CLIENT_ID. Every earlier-review defect holds fixed at this head; two small new defects remain. The listed open error issues (Warehouse*, McpQuery, Vcs*, IntegrationsUpstream, GithubAppError) come from other services and only share bundler-mapped paths with packages/auth/src/yielded/tables.ts and web-foldkit/src/page/registry.ts — this change does not touch them. The new test files and the deploy workflow were not read.

  • HazelSession resolves a session cookie (trusted Origin) or bearer, then falls back to Clerk
  • New /auth/*, POST /auth/token and /.well-known/jwks.json behind AUTH_GITHUB_CLIENT_ID
  • Auth storage runs on the request's own lazily-opened Postgres connection
  • import-clerk-identities.ts links existing users by verified email

Before merge

Findings

🟠 Warning · F10 · slugify can end a long workspace slug in -, which the form then rejects

correctness · apps/web-foldkit/src/page/create-workspace.ts:49-56

.replace(/^-+|-+$/g, "") runs before .slice(0, 48), so a name whose hyphenated form is longer than 48 characters and has a hyphen at index 48 (a…a b → a…a-) auto-fills a slug that SLUG_PATTERN rejects, and SubmittedWorkspace returns without dispatching CreateWorkspace, showing "Use at least 3 lowercase letters, numbers or hyphens" for a slug the user never typed.

Strip trailing hyphens after truncating: `.replace(/[^a-z0-9]+/g, "-").slice(0, 48).replace(/-+$/g, "")`, and add a test for a name at the slice boundary.
🔵 Note · F11 · Hand-written DDL names the PK yielded_browser_login_namespace_attempt_id_pk, the migration says yielded_browser_login_pkey

maintainability · packages/db/sql/2026-10-13-desktop-sign-in.sql:17

The runbook applies this file to prd before db:migrate, so CREATE TABLE IF NOT EXISTS "yielded_browser_login" in packages/db/migrations/20261013222300_hazel_auth/migration.sql is skipped and the live table keeps this PK name, which the snapshot records as yielded_browser_login_pkey — the tracked schema then never matches the database db:generate/db:verify diff against.

Rename the constraint here to `"yielded_browser_login_pkey"`, or regenerate the migration from `packages/db/src/tables/auth.ts` so both artifacts name it the same.

Fixed since the last review

  • ✅ F1 · safeReturnTo accepts /\\t/evil.example, an off-origin post-sign-in navigation
  • ✅ F2 · readSession retries permanent failures forever, so the app never leaves the loader
  • ✅ F4 · New 503 auth-unavailable branch does not increment proxyAuthFailures
  • ✅ F5 · One malformed address fails every invite in its role batch
  • ✅ F6 · The Downloading toast pushed after Install & Restart lands on a toast already marked removed
  • ✅ F7 · exchange posts the session cookie without the required x-effect-auth-csrf header
  • ✅ F8 · open consumes a stale queued callback from an earlier attempt
  • ✅ F9 · 2026-10-13-desktop-sign-in.sql cannot be re-applied

Production impact

Open errors in the changed files
Issue Service Occurrences File
@maple/api/vcs/VcsSourceRepositoryNotFoundError maple-ai 68 apps/web-foldkit/src/page/registry.ts
@maple/http/errors/WarehouseMalformedQueryError maple-vcs-sync 60 packages/auth/src/yielded/tables.ts
@maple/mcp/errors/McpQueryError maple-chat 45 apps/web-foldkit/src/page/registry.ts
@maple/http/errors/WarehouseMalformedQueryError maple-vcs-sync 42 packages/auth/src/yielded/tables.ts
@maple/mcp/errors/McpQueryError maple-chat 36 apps/web-foldkit/src/page/registry.ts
@maple/http/errors/WarehouseConfigError maple-api 18 packages/auth/src/yielded/tables.ts
@maple/http/errors/WarehouseConfigError maple-api 18 packages/auth/src/yielded/tables.ts
@maple/mcp/errors/McpQueryError maple-ai 18 apps/web-foldkit/src/page/registry.ts
@maple/http/errors/IntegrationsUpstreamError maple-chat 15 apps/web-foldkit/src/page/registry.ts
@maple/api/vcs/GithubAppError maple-chat 14 apps/web-foldkit/src/page/registry.ts

After this merges, Maple checks whether they stop.

Production traffic of the changed files (last 7 days)
File Calls/day Busiest operations
apps/backend/src/services/hazel-auth.ts 1.4k POST 573/day, 0.1% err
GET 446/day, 1.4% err
GET 168/day, 0.0% err
apps/backend/src/rpc/middleware/auth.ts 737 POST 573/day, 0.1% err
POST 164/day, 0.4% err
apps/web/src/lib/access-token.ts 737 POST 573/day, 0.1% err
POST 164/day, 0.4% err
packages/auth/src/yielded/desktop-login.ts 737 POST 573/day, 0.1% err
POST 164/day, 0.4% err
apps/backend/src/services/hazel-session.ts 614 GET 446/day, 1.4% err
GET 168/day, 0.0% err
Telemetry this change adds and removes (4)
  • ➕ span name InviteMailer.send · apps/backend/src/services/invite-mailer.ts:72
  • ➕ span name AccessTokenVerifier.verify · packages/auth/src/yielded/access-token.ts:132
  • ➕ span name HazelAuth.authorizePasskeyAction · packages/auth/src/yielded/policy.ts:42
  • ➕ span name HazelAuth.authorizeOAuthAction · packages/auth/src/yielded/policy.ts:86
What was checked
  • safeReturnTo parses against a fixed origin, so / /evil.example returns null (return-to.ts:15-16)
  • readSession retries with times: 3 then falls back to SignedOut (app/auth.ts:36-37)
  • exchange sends x-effect-auth-csrf: 1 with the cookie (access-token.ts:39)
Files not reviewed (11)

The review ended before it read these diffs, so nothing above vouches for them.

  • .github/workflows/deploy-prd.yml
  • apps/backend/package.json
  • apps/backend/src/rpc/handlers/users-avatar.test.ts
  • apps/backend/src/rpc/middleware/auth.test.ts
  • apps/backend/src/scripts/migrate-to-hazel-auth/import-clerk-identities.test.ts
  • apps/backend/src/services/hazel-auth-config.test.ts
  • apps/backend/src/services/hazel-auth.test.ts
  • apps/backend/src/services/invitations.test.ts
  • apps/backend/src/services/invite-mailer.test.ts
  • apps/backend/src/test/chat-sync-db-harness.ts
  • apps/backend/src/worker.ts

7b899c9 · Updated on every push. Reply "won't fix" to dismiss a finding, or mention @maple-review-bot to ask about one.

Annotations

Check warning on line 56 in apps/web-foldkit/src/page/create-workspace.ts

See this annotation in the file changed.

@maple-review-bot maple-review-bot / Maple / review

correctness: `slugify` can end a long workspace slug in `-`, which the form then rejects

`.replace(/^-+|-+$/g, "")` runs before `.slice(0, 48)`, so a name whose hyphenated form is longer than 48 characters and has a hyphen at index 48 (`a…a b` → `a…a-`) auto-fills a slug that `SLUG_PATTERN` rejects, and `SubmittedWorkspace` returns without dispatching `CreateWorkspace`, showing "Use at least 3 lowercase letters, numbers or hyphens" for a slug the user never typed.

Check notice on line 17 in packages/db/sql/2026-10-13-desktop-sign-in.sql

See this annotation in the file changed.

@maple-review-bot maple-review-bot / Maple / review

maintainability: Hand-written DDL names the PK `yielded_browser_login_namespace_attempt_id_pk`, the migration says `yielded_browser_login_pkey`

The runbook applies this file to prd before `db:migrate`, so `CREATE TABLE IF NOT EXISTS "yielded_browser_login"` in `packages/db/migrations/20261013222300_hazel_auth/migration.sql` is skipped and the live table keeps this PK name, which the snapshot records as `yielded_browser_login_pkey` — the tracked schema then never matches the database `db:generate`/`db:verify` diff against.