Skip to content

Upgrade WorkOS to v8, fix public auth handler socket reuse - #134

Merged
RhysSullivan merged 1 commit into
mainfrom
04-09-cloud_auth_workos_v8_upgrade
Apr 9, 2026
Merged

Upgrade WorkOS to v8, fix public auth handler socket reuse#134
RhysSullivan merged 1 commit into
mainfrom
04-09-cloud_auth_workos_v8_upgrade

Conversation

@RhysSullivan

Copy link
Copy Markdown
Collaborator

Summary

  • Upgrade @workos-inc/node to v8 and switch to the worker subpath import (@workos-inc/node/worker). The v8 worker build ships with a clean iron-webcrypto, so the require('buffer/index.js') patch is deleted.
  • Fix "Cannot perform I/O on behalf of a different request" on the second login after logout. HttpApiBuilder.toWebHandler was called once at module load for the public auth handler, which memoized DbService.Live's Layer.scoped — the sql connection was created in the module scope and reused across requests. workerd tears down TCP sockets at request boundaries, so the second request hit a dead socket. Build the public handler per-request and dispose it after.
  • Scrub internal details from error response bodies. UserStoreError / WorkOSError had a cause: Schema.Unknown field which encoded the full drizzle/workos error (SQL, params, stack) into 500 responses. Drop the field, annotate 500 status, log the real cause server-side.
  • Add a memberships table to the cloud schema as the foundation for multi-workspace support (one account → many orgs) per `notes/auth.md`. Migration regenerated since prod was wiped and reseeded.
  • Rename `APP_URL` → `VITE_PUBLIC_SITE_URL` and use it unconditionally in the login redirect URL.
  • `secure: true` cookies unconditionally.
  • Remove the standalone `POST /auth/organization` endpoint — the callback auto-provisions a workspace on first login, which is the only place orgs get created now.

The v8 upgrade, per-request public handler, `VITE_PUBLIC_SITE_URL` rename, and `secure: true` changes are cherry-picks / adaptations from #133 — credit to @aryasaatvik for diagnosing the socket reuse bug.

Test plan

  • `bun run test` — 4/4 passing (real PGlite in Workers runtime via `@cloudflare/vitest-pool-workers`)
  • `bun run typecheck` — clean
  • Local dev: first login → dashboard loads, `/auth/me` + `/scope` + `/scopes//sources` + `/scopes//tools` all 200
  • Local dev: logout → login again → no longer 500s with "Cannot perform I/O ..."
  • Error responses show only `{"_tag":"UserStoreError"}` instead of leaking query/params/stack

- Upgrade @workos-inc/node to v8 and switch to the worker subpath
  import (`@workos-inc/node/worker`). The v8 worker build ships with a
  clean `iron-webcrypto` dependency, so the `require('buffer/index.js')`
  patch is no longer needed and gets deleted.
- Fix "Cannot perform I/O on behalf of a different request" on the
  second login after logout. `HttpApiBuilder.toWebHandler` was called
  once at module load for the public auth handler, which memoized
  `DbService.Live`'s `Layer.scoped` — the `sql` connection was created
  in the module scope and reused across requests. workerd tears down
  TCP sockets at request boundaries, so the second request hit a dead
  socket. Build the public handler per-request and dispose it after.
- Scrub internal details from tagged error response payloads. The
  `UserStoreError`/`WorkOSError` schemas had a `cause: Schema.Unknown`
  field which encoded the full drizzle/workos error (SQL, params,
  stack) into 500 response bodies. Drop the field, set a 500 status
  annotation, and log the real cause server-side via `console.error`
  so we can see it in logs without leaking it to clients.
- Add a `memberships` table to the cloud schema as the foundation for
  multi-workspace support (one account → many orgs) per notes/auth.md.
  Migration regenerated since prod was wiped and reseeded.
- Rename `APP_URL` → `VITE_PUBLIC_SITE_URL` for consistency with the
  public site URL naming used elsewhere. Used in the auth login
  handler to compute the redirect origin instead of trusting the
  request's Host header (which points at the internal proxy target
  in dev).
- Tighten `secure: true` on session cookies unconditionally.
- Kill the `POST /auth/organization` standalone endpoint. The only
  place orgs are created is the auth callback, which auto-provisions
  a default workspace on first login.

The v8 upgrade, per-request handler, VITE_PUBLIC_SITE_URL rename, and
`secure: true` changes are cherry-picks / adaptations from PR #133.

Co-authored-by: Saatvik Arya <aryasaatvik@gmail.com>
@cloudflare-workers-and-pages

cloudflare-workers-and-pages Bot commented Apr 9, 2026

Copy link
Copy Markdown

Deploying with  Cloudflare Workers  Cloudflare Workers

The latest updates on your project. Learn more about integrating Git with Workers.

Status Name Latest Commit Updated (UTC)
✅ Deployment successful!
View logs
executor-cloud 283b061 Apr 09 2026, 08:22 PM

@RhysSullivan
RhysSullivan merged commit aff81b5 into main Apr 9, 2026
2 checks passed
RhysSullivan added a commit that referenced this pull request May 31, 2026
- Upgrade @workos-inc/node to v8 and switch to the worker subpath
  import (`@workos-inc/node/worker`). The v8 worker build ships with a
  clean `iron-webcrypto` dependency, so the `require('buffer/index.js')`
  patch is no longer needed and gets deleted.
- Fix "Cannot perform I/O on behalf of a different request" on the
  second login after logout. `HttpApiBuilder.toWebHandler` was called
  once at module load for the public auth handler, which memoized
  `DbService.Live`'s `Layer.scoped` — the `sql` connection was created
  in the module scope and reused across requests. workerd tears down
  TCP sockets at request boundaries, so the second request hit a dead
  socket. Build the public handler per-request and dispose it after.
- Scrub internal details from tagged error response payloads. The
  `UserStoreError`/`WorkOSError` schemas had a `cause: Schema.Unknown`
  field which encoded the full drizzle/workos error (SQL, params,
  stack) into 500 response bodies. Drop the field, set a 500 status
  annotation, and log the real cause server-side via `console.error`
  so we can see it in logs without leaking it to clients.
- Add a `memberships` table to the cloud schema as the foundation for
  multi-workspace support (one account → many orgs) per notes/auth.md.
  Migration regenerated since prod was wiped and reseeded.
- Rename `APP_URL` → `VITE_PUBLIC_SITE_URL` for consistency with the
  public site URL naming used elsewhere. Used in the auth login
  handler to compute the redirect origin instead of trusting the
  request's Host header (which points at the internal proxy target
  in dev).
- Tighten `secure: true` on session cookies unconditionally.
- Kill the `POST /auth/organization` standalone endpoint. The only
  place orgs are created is the auth callback, which auto-provisions
  a default workspace on first login.

The v8 upgrade, per-request handler, VITE_PUBLIC_SITE_URL rename, and
`secure: true` changes are cherry-picks / adaptations from PR #133.

Co-authored-by: Saatvik Arya <aryasaatvik@gmail.com>
@RhysSullivan
RhysSullivan deleted the 04-09-cloud_auth_workos_v8_upgrade branch June 11, 2026 08:00
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