Invalid Origin error with BetterAuth on Vercel preview deployments #1019

Closed
opened 2026-03-13 08:19:00 -05:00 by GiteaMirror · 10 comments
Owner

Originally created by @RicSala on GitHub (Apr 9, 2025).

Is this suited for github?

  • Yes, this is suited for github

To Reproduce

Set up BetterAuth in a Next.js application
Create a pull request that triggers a Vercel preview deployment
Try to authenticate in the preview deployment
Receive "Invalid Origin" error when attempting to authenticat

Current vs. Expected behavior

When using BetterAuth with Vercel preview deployments, authentication attempts fail with an "Invalid Origin" error. This happens because Vercel generates random URLs for preview deployments, which aren't whitelisted in the BetterAuth CORS configuration.

Expected behavior: BetterAuth should work correctly on Vercel preview deployments, either by supporting dynamic origins or by providing a way to configure wildcard/pattern-based origins for preview environments.

What version of Better Auth are you using?

1.2.5

Provide environment information

OS: macOS
Browser: Chrome, Safari, Firefox (issue happens in all browsers)

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

Backend, Client

Auth config (if applicable)

import { betterAuth } from 'better-auth';
import { nextCookies } from 'better-auth/next-js';
import { organization } from 'better-auth/plugins';
import { prismaAdapter } from 'better-auth/adapters/prisma';
import { db } from '@/db/prisma';

// First define our auth instance
export const auth = betterAuth({
  user: {
    modelName: 'User',
  },
  basePath: '/api/auth',
  emailAndPassword: {
    enabled: true,
  },
  hooks: {},
  advanced: {
    generateId: false,
  },
  emailVerification: {},
  database: prismaAdapter(db, {
    provider: 'postgresql',
  }),
  plugins: [nextCookies(), organization()],
  //...add more options here
});

export { toNextJsHandler } from 'better-auth/next-js';

Additional context

No response

Originally created by @RicSala on GitHub (Apr 9, 2025). ### Is this suited for github? - [x] Yes, this is suited for github ### To Reproduce Set up BetterAuth in a Next.js application Create a pull request that triggers a Vercel preview deployment Try to authenticate in the preview deployment Receive "Invalid Origin" error when attempting to authenticat ### Current vs. Expected behavior When using BetterAuth with Vercel preview deployments, authentication attempts fail with an "Invalid Origin" error. This happens because Vercel generates random URLs for preview deployments, which aren't whitelisted in the BetterAuth CORS configuration. Expected behavior: BetterAuth should work correctly on Vercel preview deployments, either by supporting dynamic origins or by providing a way to configure wildcard/pattern-based origins for preview environments. ### What version of Better Auth are you using? 1.2.5 ### Provide environment information ```bash OS: macOS Browser: Chrome, Safari, Firefox (issue happens in all browsers) ``` ### Which area(s) are affected? (Select all that apply) Backend, Client ### Auth config (if applicable) ```typescript import { betterAuth } from 'better-auth'; import { nextCookies } from 'better-auth/next-js'; import { organization } from 'better-auth/plugins'; import { prismaAdapter } from 'better-auth/adapters/prisma'; import { db } from '@/db/prisma'; // First define our auth instance export const auth = betterAuth({ user: { modelName: 'User', }, basePath: '/api/auth', emailAndPassword: { enabled: true, }, hooks: {}, advanced: { generateId: false, }, emailVerification: {}, database: prismaAdapter(db, { provider: 'postgresql', }), plugins: [nextCookies(), organization()], //...add more options here }); export { toNextJsHandler } from 'better-auth/next-js'; ``` ### Additional context _No response_
Author
Owner

@Kinfe123 commented on GitHub (Apr 10, 2025):

Why don't you try to not allow trustedOrigin on prod only I mean your prod origin only since it is not actually necessary to do guarding on the staging either on non prod envs

@Kinfe123 commented on GitHub (Apr 10, 2025): Why don't you try to not allow trustedOrigin on prod only I mean your prod origin only since it is not actually necessary to do guarding on the staging either on non prod envs
Author
Owner

@Bekacru commented on GitHub (Apr 10, 2025):

Try removing BETTER_AUTH_URL from env for preview deployments. By default, it only considers the base url as a trusted origin.

@Bekacru commented on GitHub (Apr 10, 2025): Try removing `BETTER_AUTH_URL` from env for preview deployments. By default, it only considers the base url as a trusted origin.
Author
Owner

@rena0157 commented on GitHub (Apr 10, 2025):

I am also getting the same error. I do not have the BETTER_AUTH_URL in my env for any environments, but locally.

import 'server-only'
import { betterAuth } from 'better-auth'
import { drizzleAdapter } from 'better-auth/adapters/drizzle'
import { admin, oAuthProxy } from 'better-auth/plugins'
import { headers } from 'next/headers'
import { redirect } from 'next/navigation'
import { env } from '~/env'
import { db } from '~/server/db'

export const auth = betterAuth({
  database: drizzleAdapter(db, {
    provider: 'pg',
  }),
  plugins: [oAuthProxy(), admin()],
  socialProviders: {
    microsoft: {
      clientId: env.BETTER_AUTH_MICROSOFT_ID,
      clientSecret: env.BETTER_AUTH_MICROSOFT_SECRET,
      tenantId: env.BETTER_AUTH_MICROSOFT_TENANT,
      redirectURI: env.BETTER_AUTH_MICROSOFT_REDIRECT_URI,
    },
  },
})
@rena0157 commented on GitHub (Apr 10, 2025): I am also getting the same error. I do not have the `BETTER_AUTH_URL` in my env for any environments, but locally. ```ts import 'server-only' import { betterAuth } from 'better-auth' import { drizzleAdapter } from 'better-auth/adapters/drizzle' import { admin, oAuthProxy } from 'better-auth/plugins' import { headers } from 'next/headers' import { redirect } from 'next/navigation' import { env } from '~/env' import { db } from '~/server/db' export const auth = betterAuth({ database: drizzleAdapter(db, { provider: 'pg', }), plugins: [oAuthProxy(), admin()], socialProviders: { microsoft: { clientId: env.BETTER_AUTH_MICROSOFT_ID, clientSecret: env.BETTER_AUTH_MICROSOFT_SECRET, tenantId: env.BETTER_AUTH_MICROSOFT_TENANT, redirectURI: env.BETTER_AUTH_MICROSOFT_REDIRECT_URI, }, }, }) ```
Author
Owner

@rena0157 commented on GitHub (Apr 10, 2025):

This might be a bit of a hack? But it fixed it:

export const auth = betterAuth({
  database: drizzleAdapter(db, {
    provider: 'pg',
  }),
  trustedOrigins: [process.env.VERCEL_URL!],
  plugins: [oAuthProxy(), admin()],
  socialProviders: {
    microsoft: {
      clientId: env.BETTER_AUTH_MICROSOFT_ID,
      clientSecret: env.BETTER_AUTH_MICROSOFT_SECRET,
      tenantId: env.BETTER_AUTH_MICROSOFT_TENANT,
      redirectURI: env.BETTER_AUTH_MICROSOFT_REDIRECT_URI,
    },
  },
})

I just added

  trustedOrigins: [process.env.VERCEL_URL!],
@rena0157 commented on GitHub (Apr 10, 2025): This might be a bit of a hack? But it fixed it: ```ts export const auth = betterAuth({ database: drizzleAdapter(db, { provider: 'pg', }), trustedOrigins: [process.env.VERCEL_URL!], plugins: [oAuthProxy(), admin()], socialProviders: { microsoft: { clientId: env.BETTER_AUTH_MICROSOFT_ID, clientSecret: env.BETTER_AUTH_MICROSOFT_SECRET, tenantId: env.BETTER_AUTH_MICROSOFT_TENANT, redirectURI: env.BETTER_AUTH_MICROSOFT_REDIRECT_URI, }, }, }) ``` I just added ```ts trustedOrigins: [process.env.VERCEL_URL!], ```
Author
Owner

@iamshadow-xyz commented on GitHub (May 8, 2025):

bro just remove the base url form the auth-client

@iamshadow-xyz commented on GitHub (May 8, 2025): bro just remove the base url form the auth-client
Author
Owner

@dizlee commented on GitHub (Jun 9, 2025):

bro just remove the base url form the auth-client

I accidentally had base url hard-coded in auth-client, thanks!

@dizlee commented on GitHub (Jun 9, 2025): > bro just remove the base url form the auth-client I accidentally had base url hard-coded in auth-client, thanks!
Author
Owner

@iatomic1 commented on GitHub (Jul 29, 2025):

This might be a bit of a hack? But it fixed it:

export const auth = betterAuth({
database: drizzleAdapter(db, {
provider: 'pg',
}),
trustedOrigins: [process.env.VERCEL_URL!],
plugins: [oAuthProxy(), admin()],
socialProviders: {
microsoft: {
clientId: env.BETTER_AUTH_MICROSOFT_ID,
clientSecret: env.BETTER_AUTH_MICROSOFT_SECRET,
tenantId: env.BETTER_AUTH_MICROSOFT_TENANT,
redirectURI: env.BETTER_AUTH_MICROSOFT_REDIRECT_URI,
},
},
})
I just added

trustedOrigins: [process.env.VERCEL_URL!],

Hey @rena0157 is vercel auto setting this in every deployment?

@iatomic1 commented on GitHub (Jul 29, 2025): > This might be a bit of a hack? But it fixed it: > > export const auth = betterAuth({ > database: drizzleAdapter(db, { > provider: 'pg', > }), > trustedOrigins: [process.env.VERCEL_URL!], > plugins: [oAuthProxy(), admin()], > socialProviders: { > microsoft: { > clientId: env.BETTER_AUTH_MICROSOFT_ID, > clientSecret: env.BETTER_AUTH_MICROSOFT_SECRET, > tenantId: env.BETTER_AUTH_MICROSOFT_TENANT, > redirectURI: env.BETTER_AUTH_MICROSOFT_REDIRECT_URI, > }, > }, > }) > I just added > > trustedOrigins: [process.env.VERCEL_URL!], Hey @rena0157 is vercel auto setting this in every deployment?
Author
Owner

@Metaphysics0 commented on GitHub (Aug 26, 2025):

VERCEL_URL

Yeah bro check
https://vercel.com/docs/environment-variables/system-environment-variables#VERCEL_URL.

the gotcha is that the env does not have the https:// prefix.

in the end this was what fixed it for me:

export const auth = betterAuth({
  trustedOrigins: [
    "http://localhost:5173", 
    ...(process.env.VERCEL_URL ? [`https://${process.env.VERCEL_URL}`] : [])
  ],
...

and in my auth-client:

import { createAuthClient } from "better-auth/svelte";
import { browser } from "$app/environment";

export const authClient = createAuthClient({
  baseURL: browser ? window.location.origin : undefined,
});

I am using sveltekit, but most JS frameworks should have something similar to this ^

@Metaphysics0 commented on GitHub (Aug 26, 2025): > VERCEL_URL Yeah bro check https://vercel.com/docs/environment-variables/system-environment-variables#VERCEL_URL. the gotcha is that the env does not have the `https://` prefix. in the end this was what fixed it for me: ``` export const auth = betterAuth({ trustedOrigins: [ "http://localhost:5173", ...(process.env.VERCEL_URL ? [`https://${process.env.VERCEL_URL}`] : []) ], ... ``` and in my auth-client: ``` import { createAuthClient } from "better-auth/svelte"; import { browser } from "$app/environment"; export const authClient = createAuthClient({ baseURL: browser ? window.location.origin : undefined, }); ``` I am using sveltekit, but most JS frameworks should have something similar to this ^
Author
Owner

@MatheusCanuto07 commented on GitHub (Sep 7, 2025):

Metaphysics0 thank you so much! I do what you say and everything went well

@MatheusCanuto07 commented on GitHub (Sep 7, 2025): Metaphysics0 thank you so much! I do what you say and everything went well
Author
Owner

@dakdevs commented on GitHub (Dec 19, 2025):

@Metaphysics0 how did you get around the OAuth provider needing to have the redirect uri on their end?

@dakdevs commented on GitHub (Dec 19, 2025): @Metaphysics0 how did you get around the OAuth provider needing to have the redirect uri on their end?
Sign in to join this conversation.
1 Participants
Notifications
Due Date
No due date set.
Dependencies

No dependencies set.

Reference: github-starred/better-auth#1019