[GH-ISSUE #5314] OIDC and SSO plugins fail to bundle in Convex environment (Node APIs used inside HTTP routes) #10213

Closed
opened 2026-04-13 06:11:54 -05:00 by GiteaMirror · 3 comments
Owner

Originally created by @Wizzlesky124 on GitHub (Oct 15, 2025).
Original GitHub issue: https://github.com/better-auth/better-auth/issues/5314

Is this suited for github?

  • Yes, this is suited for github

To Reproduce

1.Follow the Better Auth Convex integration guide
2.Configure createAuth to include he oidcProvider or the sso plugin:

import { createClient, type GenericCtx } from "@convex-dev/better-auth";
import { convex } from "@convex-dev/better-auth/plugins";
import { betterAuth } from "better-auth";
import { oidcProvider, jwt } from "better-auth/plugins";
import { sso } from "@better-auth/sso";

export const createAuth = (ctx: GenericCtx<any>) =>
  betterAuth({
    database: createClient<any>().adapter(ctx),
    plugins: [
      convex(),
      jwt(),
      oidcProvider({ ... }),
      sso({ ... })
    ]
  });
  1. Register the routes in http.ts:
import { httpRouter } from "convex/server";
import { authComponent, createAuth } from "./auth";

const http = httpRouter();
authComponent.registerRoutes(http, createAuth, { cors: true });
export default http;

Current vs. Expected behavior

Current behavior:
Convex build fails with errors like:

The package "constants" wasn't found on the file system but is built into node.
It looks like you are using Node APIs from a file without the "use node" directive.
"use node" directive is not allowed for http.ts.

Even when "use node" is added to the file where createAuth is defined, Convex cannot bundle the OIDC and SSO plugins because HTTP routes cannot depend on Node APIs.

Expected behavior:
The OIDC and SSO plugins should be compatible with Convex’s bundling, or there should be a documented Node-only alternative pattern for using them (e.g. via actions).

What version of Better Auth are you using?

1.3.27

System info

{
  "system": {
    "platform": "win32",
    "arch": "x64",
    "version": "Windows 10 Pro",
    "release": "10.0.19045",
    "cpuCount": 4,
    "cpuModel": "Intel(R) Core(TM) i5-7600K CPU @ 3.80GHz",
    "totalMemory": "15.93 GB",
    "freeMemory": "3.07 GB"
  },
  "node": {
    "version": "v22.14.0",
    "env": "development"
  },
  "packageManager": {
    "name": "npm",
    "version": "11.2.0"
  },
  "frameworks": null,
  "databases": null,
  "betterAuth": {
    "version": "1.3.27",
    "config": null
  }
}

Which area(s) are affected? (Select all that apply)

Backend, Client

Auth config (if applicable)

import { createClient, type GenericCtx } from "@convex-dev/better-auth";
import { convex } from "@convex-dev/better-auth/plugins";
import { components } from "./_generated/api";
import { DataModel } from "./_generated/dataModel";
import { betterAuth } from "better-auth";
import { oidcProvider, jwt } from "better-auth/plugins";
import { sso } from "@better-auth/sso";

const siteUrl = process.env.SITE_URL!;

export const authComponent = createClient<DataModel>(components.betterAuth, {
  verbose: false,
});

export const createAuth = (
  ctx: GenericCtx<DataModel>,
  { optionsOnly } = { optionsOnly: false },
) => {
  return betterAuth({
    logger: { disabled: optionsOnly },
    trustedOrigins: [siteUrl],
    database: authComponent.adapter(ctx),
    emailAndPassword: {
      enabled: true,
      requireEmailVerification: false,
    },
    account: {
      accountLinking: {
        enabled: true,
      },
    },
    plugins: [
      convex(),
      jwt(),
      oidcProvider({
        loginPage: "/signin",
        useJWTPlugin: true,
        trustedClients: [
          {
            clientId: process.env.OIDC_CLIENT_ID!,
            clientSecret: process.env.OIDC_CLIENT_SECRET!,
            name: "Main Web App",
            type: "web",
            redirectURLs: [process.env.OIDC_REDIRECT_URL!],
            disabled: false,
            skipConsent: true,
            metadata: {},
          },
          {
            clientId: process.env.OIDC_CLIENT_ID_NATIVE!,
            clientSecret: process.env.OIDC_CLIENT_SECRET_NATIVE!,
            name: "Mobile App",
            type: "native",
            redirectURLs: [process.env.OIDC_REDIRECT_URL_NATIVE!],
            disabled: false,
            skipConsent: true,
            metadata: {},
          },
        ],
      }),
      sso({
        provisionUser: async ({ user, userInfo, provider }) => {
          console.log("Provision user:", userInfo.email, provider.providerId);
        },
        organizationProvisioning: {
          disabled: false,
          defaultRole: "member",
        },
      }),
    ],
  });
};

Additional context

The issue arises because Convex HTTP routes cannot use Node APIs, but the OIDC and SSO plugins depend on built-in Node modules like crypto, constants, and fs.
Other plugins (like jwt() and convex()) bundle fine.

The Better Auth + Convex integration guide works perfectly until these Node-dependent plugins are added.

Would it be possible to:

Make the OIDC/SSO plugins compatible with Convex bundling, or

Provide a recommended serverless pattern (e.g., using Convex actions)?

Originally created by @Wizzlesky124 on GitHub (Oct 15, 2025). Original GitHub issue: https://github.com/better-auth/better-auth/issues/5314 ### Is this suited for github? - [x] Yes, this is suited for github ### To Reproduce 1.Follow the Better Auth Convex integration guide 2.Configure createAuth to include he oidcProvider or the sso plugin: ``` import { createClient, type GenericCtx } from "@convex-dev/better-auth"; import { convex } from "@convex-dev/better-auth/plugins"; import { betterAuth } from "better-auth"; import { oidcProvider, jwt } from "better-auth/plugins"; import { sso } from "@better-auth/sso"; export const createAuth = (ctx: GenericCtx<any>) => betterAuth({ database: createClient<any>().adapter(ctx), plugins: [ convex(), jwt(), oidcProvider({ ... }), sso({ ... }) ] }); ``` 4. Register the routes in http.ts: ``` import { httpRouter } from "convex/server"; import { authComponent, createAuth } from "./auth"; const http = httpRouter(); authComponent.registerRoutes(http, createAuth, { cors: true }); export default http; ``` ### Current vs. Expected behavior Current behavior: Convex build fails with errors like: The package "constants" wasn't found on the file system but is built into node. It looks like you are using Node APIs from a file without the "use node" directive. "use node" directive is not allowed for http.ts. Even when "use node" is added to the file where createAuth is defined, Convex cannot bundle the OIDC and SSO plugins because HTTP routes cannot depend on Node APIs. Expected behavior: The OIDC and SSO plugins should be compatible with Convex’s bundling, or there should be a documented Node-only alternative pattern for using them (e.g. via actions). ### What version of Better Auth are you using? 1.3.27 ### System info ```bash { "system": { "platform": "win32", "arch": "x64", "version": "Windows 10 Pro", "release": "10.0.19045", "cpuCount": 4, "cpuModel": "Intel(R) Core(TM) i5-7600K CPU @ 3.80GHz", "totalMemory": "15.93 GB", "freeMemory": "3.07 GB" }, "node": { "version": "v22.14.0", "env": "development" }, "packageManager": { "name": "npm", "version": "11.2.0" }, "frameworks": null, "databases": null, "betterAuth": { "version": "1.3.27", "config": null } } ``` ### Which area(s) are affected? (Select all that apply) Backend, Client ### Auth config (if applicable) ```typescript import { createClient, type GenericCtx } from "@convex-dev/better-auth"; import { convex } from "@convex-dev/better-auth/plugins"; import { components } from "./_generated/api"; import { DataModel } from "./_generated/dataModel"; import { betterAuth } from "better-auth"; import { oidcProvider, jwt } from "better-auth/plugins"; import { sso } from "@better-auth/sso"; const siteUrl = process.env.SITE_URL!; export const authComponent = createClient<DataModel>(components.betterAuth, { verbose: false, }); export const createAuth = ( ctx: GenericCtx<DataModel>, { optionsOnly } = { optionsOnly: false }, ) => { return betterAuth({ logger: { disabled: optionsOnly }, trustedOrigins: [siteUrl], database: authComponent.adapter(ctx), emailAndPassword: { enabled: true, requireEmailVerification: false, }, account: { accountLinking: { enabled: true, }, }, plugins: [ convex(), jwt(), oidcProvider({ loginPage: "/signin", useJWTPlugin: true, trustedClients: [ { clientId: process.env.OIDC_CLIENT_ID!, clientSecret: process.env.OIDC_CLIENT_SECRET!, name: "Main Web App", type: "web", redirectURLs: [process.env.OIDC_REDIRECT_URL!], disabled: false, skipConsent: true, metadata: {}, }, { clientId: process.env.OIDC_CLIENT_ID_NATIVE!, clientSecret: process.env.OIDC_CLIENT_SECRET_NATIVE!, name: "Mobile App", type: "native", redirectURLs: [process.env.OIDC_REDIRECT_URL_NATIVE!], disabled: false, skipConsent: true, metadata: {}, }, ], }), sso({ provisionUser: async ({ user, userInfo, provider }) => { console.log("Provision user:", userInfo.email, provider.providerId); }, organizationProvisioning: { disabled: false, defaultRole: "member", }, }), ], }); }; ``` ### Additional context The issue arises because Convex HTTP routes cannot use Node APIs, but the OIDC and SSO plugins depend on built-in Node modules like crypto, constants, and fs. Other plugins (like jwt() and convex()) bundle fine. The Better Auth + Convex integration guide works perfectly until these Node-dependent plugins are added. Would it be possible to: Make the OIDC/SSO plugins compatible with Convex bundling, or Provide a recommended serverless pattern (e.g., using Convex actions)?
GiteaMirror added the locked label 2026-04-13 06:11:54 -05:00
Author
Owner

@dosubot[bot] commented on GitHub (Oct 15, 2025):

Hi @Wizzlesky124! I'm Dosu and I’m helping the better-auth team.

The OIDC and SSO plugins in Better Auth currently depend on Node.js built-in modules (like crypto, constants, and fs), which are not supported in Convex HTTP routes. This is why they fail to bundle, even if you add the "use node" directive elsewhere—Convex does not allow Node APIs in HTTP routes at all. In contrast, plugins like jwt() and convex() do not use these Node dependencies and bundle fine.

There is no built-in serverless or Convex-compatible alternative for these plugins right now. The codebase does not include conditional imports or runtime checks to avoid Node APIs in these plugins, so they expect a Node environment and will not work in Convex HTTP routes as-is.

A recent fix for a similar Convex compatibility issue (with telemetry imports) used dynamic imports and runtime checks to avoid bundling Node dependencies, which resolved the problem for telemetry. However, this pattern has not yet been applied to the OIDC or SSO plugins, so the same bundling issue remains for them (see PR #5086).

If you need OIDC or SSO support in Convex, the only current workaround would be to move any Node-dependent logic into Convex actions (which do allow Node APIs) or to a separate Node server, and expose only the necessary endpoints to Convex HTTP routes. There is no official guide or recommended pattern for this yet.

If you want to track progress or see if a fix becomes available, keep an eye on updates to the OIDC and SSO plugins or related Convex compatibility discussions in the Better Auth repository.

To reply, just mention @dosu.


How did I do? Good | Irrelevant | Incorrect | Verbose | Hallucination | Report 🐛 | Other  Chat with Dosu Join Discord Share on X

<!-- gh-comment-id:3404191507 --> @dosubot[bot] commented on GitHub (Oct 15, 2025): <!-- Greeting --> Hi @Wizzlesky124! I'm [Dosu](https://go.dosu.dev/dosubot) and I’m helping the better-auth team. <!-- Answer --> The OIDC and SSO plugins in Better Auth currently depend on Node.js built-in modules (like `crypto`, `constants`, and `fs`), which are not supported in Convex HTTP routes. This is why they fail to bundle, even if you add the `"use node"` directive elsewhere—Convex does not allow Node APIs in HTTP routes at all. In contrast, plugins like `jwt()` and `convex()` do not use these Node dependencies and bundle fine. There is no built-in serverless or Convex-compatible alternative for these plugins right now. The codebase does not include conditional imports or runtime checks to avoid Node APIs in these plugins, so they expect a Node environment and will not work in Convex HTTP routes as-is. A recent fix for a similar Convex compatibility issue (with telemetry imports) used dynamic imports and runtime checks to avoid bundling Node dependencies, which resolved the problem for telemetry. However, this pattern has not yet been applied to the OIDC or SSO plugins, so the same bundling issue remains for them [(see PR #5086)](https://github.com/better-auth/better-auth/pull/5086). If you need OIDC or SSO support in Convex, the only current workaround would be to move any Node-dependent logic into Convex actions (which do allow Node APIs) or to a separate Node server, and expose only the necessary endpoints to Convex HTTP routes. There is no official guide or recommended pattern for this yet. If you want to track progress or see if a fix becomes available, keep an eye on updates to the OIDC and SSO plugins or related Convex compatibility discussions in the Better Auth repository. <!-- Dosu Comment Footer --> *To reply, just mention [@dosu](https://go.dosu.dev/dosubot).* --- <sup>How did I do? [Good](https://app.dosu.dev/response-feedback/bec82060-a214-4ba7-816b-a6e340e4c752?feedback_type=great_response) | [Irrelevant](https://app.dosu.dev/response-feedback/bec82060-a214-4ba7-816b-a6e340e4c752?feedback_type=irrelevant_answer) | [Incorrect](https://app.dosu.dev/response-feedback/bec82060-a214-4ba7-816b-a6e340e4c752?feedback_type=incorrect_sources) | [Verbose](https://app.dosu.dev/response-feedback/bec82060-a214-4ba7-816b-a6e340e4c752?feedback_type=too_verbose) | [Hallucination](https://app.dosu.dev/response-feedback/bec82060-a214-4ba7-816b-a6e340e4c752?feedback_type=hallucination) | [Report 🐛](https://app.dosu.dev/response-feedback/bec82060-a214-4ba7-816b-a6e340e4c752?feedback_type=bug_report) | [Other](https://app.dosu.dev/response-feedback/bec82060-a214-4ba7-816b-a6e340e4c752?feedback_type=other)</sup>&nbsp;&nbsp;[![Chat with Dosu](https://dosu.dev/dosu-chat-badge.svg)](https://app.dosu.dev/cdda13d9-dd27-4d31-b09a-5d8bec92de21/ask?utm_source=github)&nbsp;[![Join Discord](https://img.shields.io/badge/join-5865F2?logo=discord&logoColor=white&label=)](https://go.dosu.dev/discord-bot)&nbsp;[![Share on X](https://img.shields.io/badge/X-share-black)](https://twitter.com/intent/tweet?text=%40dosu_ai%20helped%20me%20solve%20this%20issue!&url=https%3A//github.com/better-auth/better-auth/issues/5314)
Author
Owner

@Wizzlesky124 commented on GitHub (Oct 15, 2025):

Hi @dosu,

Thanks for the explanation. I want to avoid hosting a separate Node server entirely. Can you provide guidance or examples for moving the Node-dependent parts of the OIDC/SSO plugins entirely into Convex actions, so they can run within Convex itself and still support the necessary endpoints?

Specifically, I’m looking for:

How to structure the OIDC/SSO logic inside Convex actions.
How to expose endpoints to the client using Convex HTTP routes safely, without Node.js dependencies.

Any recommended patterns or examples would be very helpful.

Thanks!

<!-- gh-comment-id:3404323354 --> @Wizzlesky124 commented on GitHub (Oct 15, 2025): Hi @dosu, Thanks for the explanation. I want to avoid hosting a separate Node server entirely. Can you provide guidance or examples for moving the Node-dependent parts of the OIDC/SSO plugins entirely into Convex actions, so they can run within Convex itself and still support the necessary endpoints? Specifically, I’m looking for: How to structure the OIDC/SSO logic inside Convex actions. How to expose endpoints to the client using Convex HTTP routes safely, without Node.js dependencies. Any recommended patterns or examples would be very helpful. Thanks!
Author
Owner

@dosubot[bot] commented on GitHub (Oct 15, 2025):

Here’s how you can structure OIDC/SSO logic for Better Auth in Convex without a separate Node server:

Convex HTTP routes can’t use Node APIs, but Convex actions can. The recommended pattern is to keep HTTP routes minimal—just validate input and forward requests to Convex actions. Move all Node-dependent logic (cryptography, token handling, user provisioning) into actions, which run in a Node environment and can use the necessary modules.

For example, your HTTP route handler should only parse the request and call a Convex action, passing along the relevant data. The Convex action then performs the actual OIDC/SSO logic: verifying secrets, generating tokens, handling user info, etc. This means refactoring the endpoint handlers from the Better Auth OIDC/SSO plugins so that all crypto and token work happens inside actions, not in the HTTP route layer. You can see how the plugin logic is currently structured here and here.

Pattern example:

  • HTTP route receives /oauth2/token POST, validates input, and calls a Convex action like auth_handleOidcToken({ body }).
  • The action runs all Node-dependent logic (e.g., JWT signing, PKCE validation, token generation) and returns the result.
  • The HTTP route sends the action’s response back to the client.

Key considerations:

  • Validate and sanitize all input in the HTTP route before calling the action.
  • Handle errors gracefully—Convex actions should return structured error objects that the HTTP route can translate into appropriate HTTP responses.
  • Keep request/response formats consistent with OIDC/SSO specs so clients can integrate as expected.

There’s no official Better Auth example for this yet, but this pattern is compatible with Convex’s architecture and lets you run all authentication logic inside Convex actions, avoiding Node API issues in HTTP routes. You’ll need to adapt the plugin logic to split endpoint handlers into HTTP route stubs and Convex actions as described above.

To reply, just mention @dosu.


How did I do? Good | Irrelevant | Incorrect | Verbose | Hallucination | Report 🐛 | Other  Chat with Dosu Join Discord Share on X

<!-- gh-comment-id:3404330256 --> @dosubot[bot] commented on GitHub (Oct 15, 2025): <!-- Answer --> Here’s how you can structure OIDC/SSO logic for Better Auth in Convex without a separate Node server: Convex HTTP routes can’t use Node APIs, but Convex actions can. The recommended pattern is to keep HTTP routes minimal—just validate input and forward requests to Convex actions. Move all Node-dependent logic (cryptography, token handling, user provisioning) into actions, which run in a Node environment and can use the necessary modules. For example, your HTTP route handler should only parse the request and call a Convex action, passing along the relevant data. The Convex action then performs the actual OIDC/SSO logic: verifying secrets, generating tokens, handling user info, etc. This means refactoring the endpoint handlers from the Better Auth OIDC/SSO plugins so that all crypto and token work happens inside actions, not in the HTTP route layer. You can see how the plugin logic is currently structured [here](https://github.com/better-auth/better-auth/blob/c3acb35269bb2881d30580a6bfc5a0dd4ccb8691/packages/better-auth/src/plugins/oidc-provider/index.ts) and [here](https://github.com/better-auth/better-auth/blob/c3acb35269bb2881d30580a6bfc5a0dd4ccb8691/packages/better-auth/src/plugins/sso/index.ts). **Pattern example:** - HTTP route receives `/oauth2/token` POST, validates input, and calls a Convex action like `auth_handleOidcToken({ body })`. - The action runs all Node-dependent logic (e.g., JWT signing, PKCE validation, token generation) and returns the result. - The HTTP route sends the action’s response back to the client. **Key considerations:** - Validate and sanitize all input in the HTTP route before calling the action. - Handle errors gracefully—Convex actions should return structured error objects that the HTTP route can translate into appropriate HTTP responses. - Keep request/response formats consistent with OIDC/SSO specs so clients can integrate as expected. There’s no official Better Auth example for this yet, but this pattern is compatible with Convex’s architecture and lets you run all authentication logic inside Convex actions, avoiding Node API issues in HTTP routes. You’ll need to adapt the plugin logic to split endpoint handlers into HTTP route stubs and Convex actions as described above. <!-- Dosu Comment Footer --> *To reply, just mention [@dosu](https://go.dosu.dev/dosubot).* --- <sup>How did I do? [Good](https://app.dosu.dev/response-feedback/5ba32867-a45d-46f0-aa28-cdd624942715?feedback_type=great_response) | [Irrelevant](https://app.dosu.dev/response-feedback/5ba32867-a45d-46f0-aa28-cdd624942715?feedback_type=irrelevant_answer) | [Incorrect](https://app.dosu.dev/response-feedback/5ba32867-a45d-46f0-aa28-cdd624942715?feedback_type=incorrect_sources) | [Verbose](https://app.dosu.dev/response-feedback/5ba32867-a45d-46f0-aa28-cdd624942715?feedback_type=too_verbose) | [Hallucination](https://app.dosu.dev/response-feedback/5ba32867-a45d-46f0-aa28-cdd624942715?feedback_type=hallucination) | [Report 🐛](https://app.dosu.dev/response-feedback/5ba32867-a45d-46f0-aa28-cdd624942715?feedback_type=bug_report) | [Other](https://app.dosu.dev/response-feedback/5ba32867-a45d-46f0-aa28-cdd624942715?feedback_type=other)</sup>&nbsp;&nbsp;[![Chat with Dosu](https://dosu.dev/dosu-chat-badge.svg)](https://app.dosu.dev/cdda13d9-dd27-4d31-b09a-5d8bec92de21/ask?utm_source=github)&nbsp;[![Join Discord](https://img.shields.io/badge/join-5865F2?logo=discord&logoColor=white&label=)](https://go.dosu.dev/discord-bot)&nbsp;[![Share on X](https://img.shields.io/badge/X-share-black)](https://twitter.com/intent/tweet?text=%40dosu_ai%20helped%20me%20solve%20this%20issue!&url=https%3A//github.com/better-auth/better-auth/issues/5314)
Sign in to join this conversation.
1 Participants
Notifications
Due Date
No due date set.
Dependencies

No dependencies set.

Reference: github-starred/better-auth#10213