Skip to content

Plan oauth-provider migration (#1)#380

Open
smart wants to merge 5 commits into
get-convex:mainfrom
BandWithUs:main
Open

Plan oauth-provider migration (#1)#380
smart wants to merge 5 commits into
get-convex:mainfrom
BandWithUs:main

Conversation

@smart

@smart smart commented May 20, 2026

Copy link
Copy Markdown

Summary

This PR migrates the Convex Better Auth component from Better Auth’s deprecated oidc-provider plugin to the new @better-auth/oauth-provider package.

The migration keeps Convex as the OAuth/OIDC issuer, preserves Convex-compatible JWT/JWKS behavior, and adds the route/discovery support needed for OAuth 2.1, OIDC, and MCP clients.

What changed

  • Replace better-auth/plugins/oidc-provider with @better-auth/oauth-provider
  • Pin better-auth and @better-auth/oauth-provider to 1.6.11
  • Regenerate the component schema for oauth-provider tables:
    • oauthClient
    • oauthRefreshToken
    • updated oauthAccessToken
    • updated oauthConsent
  • Preserve existing Convex JWT behavior:
    • convex_jwt cookie
    • /convex/token
    • /convex/jwks
    • static JWKS support
  • Ensure OAuth/OIDC metadata uses CONVEX_SITE_URL as issuer even when app baseURL differs
  • Add root and path-aware discovery routes:
    • /.well-known/openid-configuration
    • /.well-known/oauth-authorization-server
    • /.well-known/oauth-protected-resource
  • Add path-specific protected resource metadata support for MCP-style resources
  • Expose WWW-Authenticate through CORS
  • Add MCP helper exports:
    • withMcpAuth
    • oAuthDiscoveryMetadata
    • oAuthProtectedResourceMetadata
  • Update SvelteKit docs with OAuth/OIDC provider configuration and oauth_query continuation guidance
  • Add migration docs for existing OIDC-provider data
  • Update e2e local backend harness for newer Convex backend binaries requiring --instance-secret and --instance-name

Migration notes

Existing OAuth/OIDC provider data requires migration.

Old oauthApplication / legacy oidcApplication records should be migrated to the new oauthClient table shape. Existing OAuth access and refresh tokens should be invalidated and clients should reauthorize because oauth-provider stores tokens differently and separates refresh tokens into oauthRefreshToken.

Local-install users should regenerate their Better Auth schema after upgrading.

Testing

Passed locally:

  • npm test
  • npm run typecheck
  • npm run lint
  • npm run build
  • npm run test:e2e
  • npx convex dev --once --typecheck disable in examples/react

Also verified with a throwaway Convex fixture using:

  • better-auth@1.6.11
  • @better-auth/oauth-provider@1.6.11
  • convex@1.39.1

The fixture bundled successfully without Node built-in import failures.

Notes / remaining follow-up

This PR adds route-level and existing e2e coverage, but a full real-cloud OAuth provider acceptance test is still recommended before release:

  • RFC 7591 Dynamic Client Registration
  • auth-code + PKCE
  • ID token validation against JWKS
  • /oauth2/userinfo
  • /oauth2/end-session
  • MCP discovery → DCR → authorize → token → tool call

That flow requires a real Convex deployment and deployment credentials, so it should be added as an opt-in cloud e2e suite.

* Plan oauth-provider migration

Co-authored-by: Steve Martocci <smart@users.noreply.github.com>

* Migrate Convex auth to oauth provider

Co-authored-by: Steve Martocci <smart@users.noreply.github.com>

* Fix oauth provider validation issues

Co-authored-by: Steve Martocci <smart@users.noreply.github.com>

* Fix local backend e2e harness

Co-authored-by: Steve Martocci <smart@users.noreply.github.com>

* Address oauth provider PR review comments

Co-authored-by: Steve Martocci <smart@users.noreply.github.com>

* Add path OpenID discovery route

Co-authored-by: Steve Martocci <smart@users.noreply.github.com>

* Add generic protected resource metadata route

Co-authored-by: Steve Martocci <smart@users.noreply.github.com>

* Force Convex issuer in OAuth metadata

Co-authored-by: Steve Martocci <smart@users.noreply.github.com>

* Fix auth base path metadata handling

Co-authored-by: Steve Martocci <smart@users.noreply.github.com>

* Use Convex issuer in protected resource metadata

Co-authored-by: Steve Martocci <smart@users.noreply.github.com>

---------

Co-authored-by: Cursor Agent <cursoragent@cursor.com>
Co-authored-by: Steve Martocci <smart@users.noreply.github.com>
@vercel

vercel Bot commented May 20, 2026

Copy link
Copy Markdown

@smart is attempting to deploy a commit to the Convex Team on Vercel.

A member of the Team first needs to authorize it.

@coderabbitai

coderabbitai Bot commented May 20, 2026

Copy link
Copy Markdown

Review Change Stack

No actionable comments were generated in the recent review. 🎉

ℹ️ Recent review info
⚙️ Run configuration

Configuration used: Repository: get-convex/coderabbit/.coderabbit.yaml

Review profile: CHILL

Plan: Pro

Run ID: 425caad7-7717-4c5a-b70a-b233965f6809

📥 Commits

Reviewing files that changed from the base of the PR and between 3a66f86 and 1e89632.

📒 Files selected for processing (1)
  • package.json
💤 Files with no reviewable changes (1)
  • package.json

📝 Walkthrough

Walkthrough

This PR migrates the Convex Better Auth integration from the deprecated oidc-provider plugin to the new @better-auth/oauth-provider. It introduces a comprehensive plan document, updates all documentation and framework guides to explain OAuth/OIDC endpoint exposure and configuration, bumps dependencies to version 1.6.11, regenerates the Convex schema with new OAuth client/refresh-token tables, rewires the Convex plugin to compose and proxy oauth-provider endpoints, adds route registration helpers for well-known discovery endpoints with proper CORS exposure, exports new MCP helper utilities for protected resource authentication and metadata, and includes extensive test coverage and E2E infrastructure updates.

Sequence Diagram(s)

sequenceDiagram
  participant Dev
  participant ConvexPlugin
  participant OAuthProvider
  participant JWTPlugin
  participant ConvexHTTP
  Dev->>ConvexPlugin: configure(opts.oauthProvider)
  ConvexPlugin->>OAuthProvider: init(opts)
  OAuthProvider->>ConvexPlugin: hooks, endpoints, schema contributions
  ConvexPlugin->>JWTPlugin: read issuer/jwks via getPlugin("jwt")
  ConvexPlugin->>ConvexHTTP: proxy well-known endpoints (rewritten base URL)
  ConvexHTTP->>Client: serve discovery & protected-resource metadata (WWW-Authenticate exposed)
Loading

Possibly related PRs

  • get-convex/better-auth#302: Earlier changes to registerRoutesLazy and well-known/protected-resource metadata route wiring that this PR extends.
  • get-convex/better-auth#245: Touches Convex plugin wiring for auth endpoints and overlaps with provider integration changes.

Suggested reviewers

  • erquhart
🚥 Pre-merge checks | ✅ 4
✅ Passed checks (4 passed)
Check name Status Explanation
Title check ✅ Passed The title 'Plan oauth-provider migration (#1)' directly relates to the main objective of this PR, which is to migrate the Convex Better Auth component from the deprecated oidc-provider plugin to the new oauth-provider package.
Description check ✅ Passed The PR description comprehensively explains the migration from oidc-provider to oauth-provider, detailing schema changes, preservation of JWT behavior, new discovery routes, MCP helpers, documentation updates, and migration notes for existing data.
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.

✏️ Tip: You can configure your own custom pre-merge checks in the settings.

✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

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

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

🧹 Nitpick comments (1)
src/client/create-client.ts (1)

149-200: 💤 Low value

Consider validating CONVEX_SITE_URL consistently.

protectedResourceMetadata properly throws when CONVEX_SITE_URL is unset, but the redirect URL construction at lines 154, 162, 168, 176 does not validate. If undefined, these produce malformed URLs like "undefined/convex/...".

In Convex production this is always set, but for consistency with the validation in protectedResourceMetadata, consider extracting the validation:

💡 Suggested approach
+const getConvexSiteUrl = () => {
+  const siteUrl = process.env.CONVEX_SITE_URL;
+  if (!siteUrl) {
+    throw new Error("CONVEX_SITE_URL is not set");
+  }
+  return siteUrl;
+};
+
 const protectedResourceMetadata = (resourcePath: string) => {
-  const siteUrl = process.env.CONVEX_SITE_URL;
-  if (!siteUrl) {
-    throw new Error("CONVEX_SITE_URL is not set");
-  }
+  const siteUrl = getConvexSiteUrl();
   // ...
 };

 const registerWellKnownRoutes = (http: HttpRouter, path: string) => {
+  const siteUrl = getConvexSiteUrl();
   // Use siteUrl instead of process.env.CONVEX_SITE_URL in redirect handlers
 };
🤖 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 `@src/client/create-client.ts` around lines 149 - 200, The redirect URLs in
registerWellKnownRoutes build strings using process.env.CONVEX_SITE_URL without
validation (see registerWellKnownRoutes, routeIfMissing, Response.redirect),
which can produce malformed "undefined/..." URLs; update registerWellKnownRoutes
to use the same validated helper that protectedResourceMetadata uses (or add a
small getValidatedConvexSiteUrl helper) to read and validate CONVEX_SITE_URL,
then build redirect URLs from that validated base before calling
Response.redirect or jsonResponse so all well-known routes consistently error or
redirect only when a valid CONVEX_SITE_URL is present.
🤖 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 `@docs/content/docs/framework-guides/sveltekit.mdx`:
- Around line 218-231: Update the install instruction so the documented package
versions match the oauthProvider configuration shown in the Convex plugin block
(the convex({ authConfig, oauthProvider: { ... loginPage, consentPage,
allowDynamicClientRegistration, pairwiseSecret }}) code). Replace the current
install command that pins better-auth@1.5.3 with a command that installs
better-auth@1.6.11 and `@better-auth/oauth-provider`@1.6.11 (save-exact) so the
oauth-provider features and pairwiseSecret support used in the oauthProvider
config are present.

In `@docs/content/docs/migrations/migrate-to-oauth-provider.mdx`:
- Around line 17-19: Update the npm install command shown as "npm install
better-auth@1.6.11 `@better-auth/oauth-provider`@1.6.11" to include the
--save-exact flag so the exact versions are persisted (i.e., change that install
line to include --save-exact).

In `@src/client/create-client.test.ts`:
- Around line 138-198: The test "registerRoutes exposes OAuth protected resource
metadata" mutates process.env.CONVEX_SITE_URL and doesn't restore it; capture
the original value of process.env.CONVEX_SITE_URL before setting it in that test
and restore it after the test completes (use a try/finally inside the it block
or an afterEach hook) so other tests aren't affected; ensure this cleanup
surrounds the code that calls createClient(component) and
client.registerRoutes(...) so the environment is reliably reset.

In `@src/plugins/convex/index.test.ts`:
- Around line 108-191: The tests modify process.env.CONVEX_SITE_URL without
restoring it; update the tests around convex(...) / getOAuthServerConfig to save
const _orig = process.env.CONVEX_SITE_URL before setting
process.env.CONVEX_SITE_URL, and restore it after the test (e.g., in a finally
block or an afterEach that sets process.env.CONVEX_SITE_URL = _orig or deletes
it if _orig === undefined) so the global env state is not leaked between tests;
apply this change to the two tests that call convex(...) and reference
getOAuthServerConfig.

In `@src/plugins/mcp/index.ts`:
- Around line 17-31: The exposeAuthenticateHeader function currently returns
early if Access-Control-Expose-Headers is present, which prevents adding
WWW-Authenticate; change it to read the existing Access-Control-Expose-Headers
value, append "WWW-Authenticate" if it's not already listed (preserving existing
values and avoiding duplicates), and set the merged string back onto the headers
before returning the new Response (keep using response.body, response.status,
response.statusText, and a new Headers instance); reference the
exposeAuthenticateHeader function and the header names "WWW-Authenticate" and
"Access-Control-Expose-Headers".

---

Nitpick comments:
In `@src/client/create-client.ts`:
- Around line 149-200: The redirect URLs in registerWellKnownRoutes build
strings using process.env.CONVEX_SITE_URL without validation (see
registerWellKnownRoutes, routeIfMissing, Response.redirect), which can produce
malformed "undefined/..." URLs; update registerWellKnownRoutes to use the same
validated helper that protectedResourceMetadata uses (or add a small
getValidatedConvexSiteUrl helper) to read and validate CONVEX_SITE_URL, then
build redirect URLs from that validated base before calling Response.redirect or
jsonResponse so all well-known routes consistently error or redirect only when a
valid CONVEX_SITE_URL is present.
🪄 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: Repository: get-convex/coderabbit/.coderabbit.yaml

Review profile: CHILL

Plan: Pro

Run ID: 2ababae6-7bd5-40d6-aa51-9c69ea5cb6c8

📥 Commits

Reviewing files that changed from the base of the PR and between be73010 and 5c6588b.

⛔ Files ignored due to path filters (5)
  • examples/next/package-lock.json is excluded by !**/package-lock.json
  • examples/react/package-lock.json is excluded by !**/package-lock.json
  • examples/tanstack/package-lock.json is excluded by !**/package-lock.json
  • package-lock.json is excluded by !**/package-lock.json
  • src/component/_generated/component.ts is excluded by !**/_generated/**
📒 Files selected for processing (21)
  • CHANGELOG.md
  • MIGRATION_PLAN.md
  • README.md
  • docs/content/docs/framework-guides/sveltekit.mdx
  • docs/content/docs/migrations/meta.json
  • docs/content/docs/migrations/migrate-to-oauth-provider.mdx
  • e2e/backendHarness.js
  • e2e/run-e2e-tests.sh
  • package.json
  • src/auth-options.ts
  • src/client/adapter.ts
  • src/client/create-client.test.ts
  • src/client/create-client.ts
  • src/client/create-schema.test.ts
  • src/client/create-schema.ts
  • src/component/schema.ts
  • src/plugins/convex/index.test.ts
  • src/plugins/convex/index.ts
  • src/plugins/index.ts
  • src/plugins/mcp/index.test.ts
  • src/plugins/mcp/index.ts
💤 Files with no reviewable changes (1)
  • src/auth-options.ts

Comment thread docs/content/docs/framework-guides/sveltekit.mdx
Comment thread docs/content/docs/migrations/migrate-to-oauth-provider.mdx
Comment thread src/client/create-client.test.ts
Comment thread src/plugins/convex/index.test.ts
Comment thread src/plugins/mcp/index.ts
@smart

smart commented May 20, 2026

Copy link
Copy Markdown
Author

working on these

cursoragent and others added 2 commits May 20, 2026 00:26
Co-authored-by: Steve Martocci <smart@users.noreply.github.com>
Co-authored-by: Cursor Agent <cursoragent@cursor.com>

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

Caution

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

⚠️ Outside diff range comments (1)
package.json (1)

105-105: ⚠️ Potential issue | 🟠 Major | ⚡ Quick win

Avoid exact version pinning in peerDependencies—use a semver range instead.

Pinning better-auth to exactly 1.6.11 forces all consumers to use that precise version. npm's official documentation explicitly discourages exact patch pins in peerDependencies, stating: "make sure your plugin requirement is as broad as possible, and not to lock it down to specific patch versions." This approach prevents consumers from receiving patch fixes and causes peer dependency conflicts when they have any other version.

Use a semver range to express compatibility while allowing patch updates:

Suggested fix
-    "better-auth": "1.6.11",
+    "better-auth": "^1.6.11",

The ^ range allows any version from 1.6.11 up to (but not including) 2.0.0, which is appropriate for semver compatibility. Alternatively, use ~1.6.11 if you need stricter control (patch updates only: 1.6.12, 1.6.13, etc.).

🤖 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 `@package.json` at line 105, The peerDependency entry for "better-auth" is
pinned to an exact version ("1.6.11"); change it to a semver range to allow
compatible patch/minor updates (for example replace "1.6.11" with "^1.6.11" or
"~1.6.11") in the package.json peerDependencies section so consumers can use
compatible versions without being forced to that exact patch; update the
"better-auth" value accordingly.
🧹 Nitpick comments (1)
package.json (1)

23-23: ⚡ Quick win

Use prepublishOnly instead of prepare for clearer intent.

The prepare script runs during local development after npm install, not during consumer installation from npm registry. However, prepare is unconventional for this use case. Since preversion (line 33) already builds before publishing, use prepublishOnly to explicitly indicate the script should only run during npm publish preparation:

-    "prepare": "npm run build",
+    "prepublishOnly": "npm run build",

Alternatively, if preversion fully covers your pre-publish needs (which it appears to, since it runs build:clean), the script may be redundant and can be removed entirely.

🤖 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 `@package.json` at line 23, The package.json currently defines a "prepare"
script which runs during local installs; replace or remove it to match
publishing intent: either rename the "prepare" script to "prepublishOnly" so
"npm publish" triggers the build, or remove the "prepare" entry entirely if the
existing "preversion" script already runs the necessary build steps (e.g.,
"build:clean" and "build"); update the scripts section to use "prepublishOnly"
or delete "prepare" accordingly to avoid unintended local-run builds.
🤖 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.

Outside diff comments:
In `@package.json`:
- Line 105: The peerDependency entry for "better-auth" is pinned to an exact
version ("1.6.11"); change it to a semver range to allow compatible patch/minor
updates (for example replace "1.6.11" with "^1.6.11" or "~1.6.11") in the
package.json peerDependencies section so consumers can use compatible versions
without being forced to that exact patch; update the "better-auth" value
accordingly.

---

Nitpick comments:
In `@package.json`:
- Line 23: The package.json currently defines a "prepare" script which runs
during local installs; replace or remove it to match publishing intent: either
rename the "prepare" script to "prepublishOnly" so "npm publish" triggers the
build, or remove the "prepare" entry entirely if the existing "preversion"
script already runs the necessary build steps (e.g., "build:clean" and "build");
update the scripts section to use "prepublishOnly" or delete "prepare"
accordingly to avoid unintended local-run builds.

ℹ️ Review info
⚙️ Run configuration

Configuration used: Repository: get-convex/coderabbit/.coderabbit.yaml

Review profile: CHILL

Plan: Pro

Run ID: 487b4b5c-b80d-4fb6-9a41-917c11b75da8

📥 Commits

Reviewing files that changed from the base of the PR and between 6e8a4df and 3a66f86.

📒 Files selected for processing (1)
  • package.json

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.

2 participants