[GH-ISSUE #3062] Google Signin throwing error #9455

Closed
opened 2026-04-13 04:55:28 -05:00 by GiteaMirror · 10 comments
Owner

Originally created by @venkadeshshiva on GitHub (Jun 17, 2025).
Original GitHub issue: https://github.com/better-auth/better-auth/issues/3062

Is this suited for github?

  • Yes, this is suited for github

To Reproduce

I have nextjs app that is throwing an error while authenticating with google.

In the auth.ts I am using below code for social login

socialProviders: { github: { clientId: process.env.GITHUB_CLIENT_ID || "", clientSecret: process.env.GITHUB_CLIENT_SECRET || "", disableSignUp: process.env.GITHUB_DISABLE_SIGNUP === "true", disableImplicitSignUp: process.env.GITHUB_DISABLE_IMPLICIT_SIGNUP === "true", }, google: { clientId: process.env.NEXT_PUBLIC_GOOGLE_CLIENT_ID || "", clientSecret: process.env.GOOGLE_CLIENT_SECRET || "", disableSignUp: process.env.GOOGLE_DISABLE_SIGNUP === "true", disableImplicitSignUp: process.env.GOOGLE_DISABLE_IMPLICIT_SIGNUP === "true", }, },
For login I am using below code

`
const signInGoogle = async () => {

    await signIn.social({
        provider: "google",
        callbackURL: "/dashboard",
    },
        {
            onSuccess: (data) => {
                console.log("Login successful", data);
                router.push("/dashboard");
                toast.success("Login successful");
                setLoading(false);
            },
            onError: ({ error }) => {
                console.log("Login failed", error);
                toast.error("Login failed", {
                    description: error.message,
                });
                setLoading(false);
            },
        }).catch(({ error }) => {
            console.error("Error signing in:", error);
            toast.error("Something went wrong", {
                description: error.message,
            });
            setLoading(false);
        });
}

`

I am getting below issue after login the google

Image

Backend I got below error,

ERROR [Better Auth]: State Mismatch. Verification not found

Current vs. Expected behavior

I shouldn't get the error

What version of Better Auth are you using?

1.2.9

Provide environment information

- OS [Windows, Linux]
- Browser [Edge]

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

Package, Backend, Client

Auth config (if applicable)

import { betterAuth } from "better-auth";
import {
	bearer,
	admin as adminPlugin,
	multiSession,
	organization,
	twoFactor,
	oneTap,
	oAuthProxy,
	openAPI,
	oidcProvider,
	customSession,
	apiKey,
} from "better-auth/plugins";
import { nextCookies } from "better-auth/next-js";
import { passkey } from "better-auth/plugins/passkey";

import { prismaAdapter } from "better-auth/adapters/prisma";
import { PrismaClient } from "../../generated/prisma/client";
import { nodemailer } from "./email/nodemailer";
import { render } from "@react-email/components";
import { reactResetPasswordEmail } from "./email/reset-password";
import { reactInvitationEmail } from "./email/invitation";
import { ac, admin, myCustomRole, user } from "@/lib/permissions"

const from = process.env.BETTER_AUTH_EMAIL || "delivered@resend.dev";
const to = process.env.TEST_EMAIL || "";


const prisma = new PrismaClient();

export const auth = betterAuth({
	appName: "Better Auth Demo",
	database: prismaAdapter(prisma, {
		provider: "mongodb", // or "mysql", "postgresql", ...etc
	}),
	emailVerification: {
		async sendVerificationEmail({ user, url }) {
			const res = await nodemailer.sendMail({
				from,
				to: to || user.email,
				subject: "Verify your email address",
				html: `<a href="${url}">Verify your email address</a>`,
			});
			console.log(res, user.email);
		},
		disableSignUp: process.env.EMAIL_DISABLE_SIGNUP === "true",
	},
	account: {
		accountLinking: {
			trustedProviders: ["google", "github"],
		},
	},
	emailAndPassword: {
		enabled: true,
		async sendResetPassword({ user, url }) {
			await nodemailer.sendMail({
				from,
				to: user.email,
				subject: "Reset your password",
				html: await render(reactResetPasswordEmail({
					username: user.email,
					resetLink: url,
				}))
			});
		},
		requireEmailVerification: true,
	},
	socialProviders: {
		github: {
			clientId: process.env.GITHUB_CLIENT_ID || "",
			clientSecret: process.env.GITHUB_CLIENT_SECRET || "",
			disableSignUp: process.env.GITHUB_DISABLE_SIGNUP === "true",
			disableImplicitSignUp: process.env.GITHUB_DISABLE_IMPLICIT_SIGNUP === "true",
		},
		google: {
			clientId: process.env.NEXT_PUBLIC_GOOGLE_CLIENT_ID || "",
			clientSecret: process.env.GOOGLE_CLIENT_SECRET || "",
			disableSignUp: process.env.GOOGLE_DISABLE_SIGNUP === "true",
			disableImplicitSignUp: process.env.GOOGLE_DISABLE_IMPLICIT_SIGNUP === "true",
		},
	},
	plugins: [
		organization({
			teams: {
				enabled: true,
			},
			async sendInvitationEmail(data) {
				await nodemailer.sendMail({
					from,
					to: data.email,
					subject: "You've been invited to join an organization",
					html: await render(reactInvitationEmail({
						username: data.email,
						invitedByUsername: data.inviter.user.name,
						invitedByEmail: data.inviter.user.email,
						teamName: data.organization.name,
						inviteLink:
							process.env.NODE_ENV === "development"
								? `http://localhost:3000/accept-invitation/${data.id}`
								: `${process.env.BETTER_AUTH_URL ||
								"https://demo.better-auth.com"
								}/accept-invitation/${data.id}`,
					}))
				});
			},
		}),
		twoFactor({
			otpOptions: {
				async sendOTP({ user, otp }) {
					await nodemailer.sendMail({
						from,
						to: user.email,
						subject: "Your OTP",
						html: `Your OTP is ${otp}`,
					});
				},
			},
		}),
		passkey(),
		openAPI(),
		bearer(),
		adminPlugin({
			adminUserIds: ["FdWnemJlgm9y8sNyiNrUwBKSmkK84EeH"],
			ac,
			roles: {
				admin,
				user,
				myCustomRole
			}
		}),
		multiSession(),
		oAuthProxy(),
		nextCookies(),
		oidcProvider({
			loginPage: "/auth",
		}),
		oneTap(),
		apiKey({
			customAPIKeyGetter(ctx) {
				if (!ctx.request || !ctx.request.headers) return null;
				const has = ctx.request.headers.has("x-api-key");
				if (!has) return null;
				return ctx.request.headers.get("x-api-key");
			},
		}),
		customSession(async (session) => {
			return {
				...session,
				user: {
					...session.user,
					dd: "test",
				},
			};
		}),

	],
	trustedOrigins: [],
});

Additional context

No response

Originally created by @venkadeshshiva on GitHub (Jun 17, 2025). Original GitHub issue: https://github.com/better-auth/better-auth/issues/3062 ### Is this suited for github? - [x] Yes, this is suited for github ### To Reproduce I have nextjs app that is throwing an error while authenticating with google. In the auth.ts I am using below code for social login ` socialProviders: { github: { clientId: process.env.GITHUB_CLIENT_ID || "", clientSecret: process.env.GITHUB_CLIENT_SECRET || "", disableSignUp: process.env.GITHUB_DISABLE_SIGNUP === "true", disableImplicitSignUp: process.env.GITHUB_DISABLE_IMPLICIT_SIGNUP === "true", }, google: { clientId: process.env.NEXT_PUBLIC_GOOGLE_CLIENT_ID || "", clientSecret: process.env.GOOGLE_CLIENT_SECRET || "", disableSignUp: process.env.GOOGLE_DISABLE_SIGNUP === "true", disableImplicitSignUp: process.env.GOOGLE_DISABLE_IMPLICIT_SIGNUP === "true", }, }, ` For login I am using below code ` const signInGoogle = async () => { await signIn.social({ provider: "google", callbackURL: "/dashboard", }, { onSuccess: (data) => { console.log("Login successful", data); router.push("/dashboard"); toast.success("Login successful"); setLoading(false); }, onError: ({ error }) => { console.log("Login failed", error); toast.error("Login failed", { description: error.message, }); setLoading(false); }, }).catch(({ error }) => { console.error("Error signing in:", error); toast.error("Something went wrong", { description: error.message, }); setLoading(false); }); } ` I am getting below issue after login the google ![Image](https://github.com/user-attachments/assets/b1dfc1f9-2303-4a18-b4c0-699b5ebb617e) Backend I got below error, ERROR [Better Auth]: State Mismatch. Verification not found ### Current vs. Expected behavior I shouldn't get the error ### What version of Better Auth are you using? 1.2.9 ### Provide environment information ```bash - OS [Windows, Linux] - Browser [Edge] ``` ### Which area(s) are affected? (Select all that apply) Package, Backend, Client ### Auth config (if applicable) ```typescript import { betterAuth } from "better-auth"; import { bearer, admin as adminPlugin, multiSession, organization, twoFactor, oneTap, oAuthProxy, openAPI, oidcProvider, customSession, apiKey, } from "better-auth/plugins"; import { nextCookies } from "better-auth/next-js"; import { passkey } from "better-auth/plugins/passkey"; import { prismaAdapter } from "better-auth/adapters/prisma"; import { PrismaClient } from "../../generated/prisma/client"; import { nodemailer } from "./email/nodemailer"; import { render } from "@react-email/components"; import { reactResetPasswordEmail } from "./email/reset-password"; import { reactInvitationEmail } from "./email/invitation"; import { ac, admin, myCustomRole, user } from "@/lib/permissions" const from = process.env.BETTER_AUTH_EMAIL || "delivered@resend.dev"; const to = process.env.TEST_EMAIL || ""; const prisma = new PrismaClient(); export const auth = betterAuth({ appName: "Better Auth Demo", database: prismaAdapter(prisma, { provider: "mongodb", // or "mysql", "postgresql", ...etc }), emailVerification: { async sendVerificationEmail({ user, url }) { const res = await nodemailer.sendMail({ from, to: to || user.email, subject: "Verify your email address", html: `<a href="${url}">Verify your email address</a>`, }); console.log(res, user.email); }, disableSignUp: process.env.EMAIL_DISABLE_SIGNUP === "true", }, account: { accountLinking: { trustedProviders: ["google", "github"], }, }, emailAndPassword: { enabled: true, async sendResetPassword({ user, url }) { await nodemailer.sendMail({ from, to: user.email, subject: "Reset your password", html: await render(reactResetPasswordEmail({ username: user.email, resetLink: url, })) }); }, requireEmailVerification: true, }, socialProviders: { github: { clientId: process.env.GITHUB_CLIENT_ID || "", clientSecret: process.env.GITHUB_CLIENT_SECRET || "", disableSignUp: process.env.GITHUB_DISABLE_SIGNUP === "true", disableImplicitSignUp: process.env.GITHUB_DISABLE_IMPLICIT_SIGNUP === "true", }, google: { clientId: process.env.NEXT_PUBLIC_GOOGLE_CLIENT_ID || "", clientSecret: process.env.GOOGLE_CLIENT_SECRET || "", disableSignUp: process.env.GOOGLE_DISABLE_SIGNUP === "true", disableImplicitSignUp: process.env.GOOGLE_DISABLE_IMPLICIT_SIGNUP === "true", }, }, plugins: [ organization({ teams: { enabled: true, }, async sendInvitationEmail(data) { await nodemailer.sendMail({ from, to: data.email, subject: "You've been invited to join an organization", html: await render(reactInvitationEmail({ username: data.email, invitedByUsername: data.inviter.user.name, invitedByEmail: data.inviter.user.email, teamName: data.organization.name, inviteLink: process.env.NODE_ENV === "development" ? `http://localhost:3000/accept-invitation/${data.id}` : `${process.env.BETTER_AUTH_URL || "https://demo.better-auth.com" }/accept-invitation/${data.id}`, })) }); }, }), twoFactor({ otpOptions: { async sendOTP({ user, otp }) { await nodemailer.sendMail({ from, to: user.email, subject: "Your OTP", html: `Your OTP is ${otp}`, }); }, }, }), passkey(), openAPI(), bearer(), adminPlugin({ adminUserIds: ["FdWnemJlgm9y8sNyiNrUwBKSmkK84EeH"], ac, roles: { admin, user, myCustomRole } }), multiSession(), oAuthProxy(), nextCookies(), oidcProvider({ loginPage: "/auth", }), oneTap(), apiKey({ customAPIKeyGetter(ctx) { if (!ctx.request || !ctx.request.headers) return null; const has = ctx.request.headers.has("x-api-key"); if (!has) return null; return ctx.request.headers.get("x-api-key"); }, }), customSession(async (session) => { return { ...session, user: { ...session.user, dd: "test", }, }; }), ], trustedOrigins: [], }); ``` ### Additional context _No response_
GiteaMirror added the lockedbug labels 2026-04-13 04:55:28 -05:00
Author
Owner

@Kinfe123 commented on GitHub (Jun 18, 2025):

can you please adjust the your trusted origin option to your trusted origin - trustedOrigins: ["http://localhost:3000"]

<!-- gh-comment-id:2982156390 --> @Kinfe123 commented on GitHub (Jun 18, 2025): can you please adjust the your trusted origin option to your trusted origin - `trustedOrigins: ["http://localhost:3000"] `
Author
Owner

@venkadeshshiva commented on GitHub (Jun 18, 2025):

can you please adjust the your trusted origin option to your trusted origin - trustedOrigins: ["http://localhost:3000"]

Added http://localhost:3000 in the trustedOrigins. but still facing same issue.

<!-- gh-comment-id:2983309505 --> @venkadeshshiva commented on GitHub (Jun 18, 2025): > can you please adjust the your trusted origin option to your trusted origin - `trustedOrigins: ["http://localhost:3000"] ` Added http://localhost:3000 in the trustedOrigins. but still facing same issue.
Author
Owner

@dagmawibabi commented on GitHub (Jul 10, 2025):

@venkadeshshiva have you checked your Google Cloud Console

In the Google Cloud Console > Credentials > Authorized redirect URIs, make sure to set the redirect URL to http://localhost:3000/api/auth/callback/google for local development. For production, make sure to set the redirect URL as your application domain, e.g. https://example.com/api/auth/callback/google. If you change the base path of the auth routes, you should update the redirect URL accordingly.

<!-- gh-comment-id:3058218241 --> @dagmawibabi commented on GitHub (Jul 10, 2025): @venkadeshshiva have you checked your [Google Cloud Console](https://console.cloud.google.com/apis/dashboard) In the Google Cloud Console > Credentials > Authorized redirect URIs, make sure to set the redirect URL to http://localhost:3000/api/auth/callback/google for local development. For production, make sure to set the redirect URL as your application domain, e.g. https://example.com/api/auth/callback/google. If you change the base path of the auth routes, you should update the redirect URL accordingly.
Author
Owner

@darrencarlin commented on GitHub (Jul 10, 2025):

I am facing the same issue in my production environment, I do not get the error locally...

Vercel logs show the same error as above

ERROR [Better Auth]: State Mismatch. Verification not found
  • Next JS 15.3.5
  • Chrome, MacOS
  • Redirects are setup correctly in Google Cloud Console

In my scenario I have imported users from supabase -> neon.

Here is my auth.ts

import bcrypt from "bcryptjs";
import { betterAuth } from "better-auth";
import { drizzleAdapter } from "better-auth/adapters/drizzle";
import { v4 as uuidv4 } from "uuid";
import { db } from "../db";
import * as schema from "@/lib/db/schema";
import { sendEmailVerificationEmail, sendPasswordResetEmail } from "../queries";

export const auth = betterAuth({
  appName: "The Guinness Map",
  database: drizzleAdapter(db, {
    provider: "pg",
    schema: {
      user: schema.user,
      account: schema.account,
      session: schema.session,
      verification: schema.verification,
    },
  }),
  trustedOrigins: [
    "http://localhost:3000",
    "https://my-project-iota-nine-36.vercel.app",
  ],
  // We used UUID's in the previous setup on the old website for ID's
  advanced: {
    database: {
      generateId: () => uuidv4(),
    },
  },
  // We are using a custom password setup to match the previous setup on the old website.
  // Better auth defaults to scrypt but we used bcrypt previously.
  emailAndPassword: {
    enabled: true,
    sendResetPassword: async ({ user, token }) => {
      await sendPasswordResetEmail({
        to: user.email,
        subject: "Reset your password",
        token,
      });
    },
    requireEmailVerification: true,
    password: {
      hash: async (password) => {
        const ROUNDS = 10;
        const hashedPassword = await bcrypt.hash(password, ROUNDS);
        return hashedPassword;
      },
      verify: async ({ hash, password }) => {
        const isMatch = await bcrypt.compare(password, hash);
        return isMatch;
      },
    },
  },
  emailVerification: {
    sendOnSignUp: true,
    autoSignInAfterVerification: true,
    sendVerificationEmail: async ({ user, url }) => {
      await sendEmailVerificationEmail({
        to: user.email,
        subject: "Verify your email address",
        url: url,
      });
    },
  },
  socialProviders: {
    google: {
      prompt: "select_account",
      clientId: process.env.GOOGLE_CLIENT_ID as string,
      clientSecret: process.env.GOOGLE_CLIENT_SECRET as string,
    },
    twitter: {
      clientId: process.env.TWITTER_CLIENT_ID as string,
      clientSecret: process.env.TWITTER_CLIENT_SECRET as string,
    },
  },
  // Allow users to sign-in via multiple types of accounts, google email/password etc
  account: {
    accountLinking: {
      enabled: true,
      trustedProviders: ["google", "twitter"],
      allowDifferentEmails: false,
    },
  },
  user: {
    additionalFields: {
      username: {
        type: "string",
      },
      url: {
        type: "string",
      },
      bio: {
        type: "string",
      },
      badges: {
        type: "string[]",
      },
      publicProfile: {
        type: "boolean",
      },
      reviewCount: {
        type: "number",
      },
    },
  },
});
<!-- gh-comment-id:3058826275 --> @darrencarlin commented on GitHub (Jul 10, 2025): I am facing the same issue in my production environment, I do not get the error locally... Vercel logs show the same error as above ``` ERROR [Better Auth]: State Mismatch. Verification not found ``` - Next JS 15.3.5 - Chrome, MacOS - Redirects are setup correctly in Google Cloud Console In my scenario I have imported users from supabase -> neon. Here is my `auth.ts` ``` import bcrypt from "bcryptjs"; import { betterAuth } from "better-auth"; import { drizzleAdapter } from "better-auth/adapters/drizzle"; import { v4 as uuidv4 } from "uuid"; import { db } from "../db"; import * as schema from "@/lib/db/schema"; import { sendEmailVerificationEmail, sendPasswordResetEmail } from "../queries"; export const auth = betterAuth({ appName: "The Guinness Map", database: drizzleAdapter(db, { provider: "pg", schema: { user: schema.user, account: schema.account, session: schema.session, verification: schema.verification, }, }), trustedOrigins: [ "http://localhost:3000", "https://my-project-iota-nine-36.vercel.app", ], // We used UUID's in the previous setup on the old website for ID's advanced: { database: { generateId: () => uuidv4(), }, }, // We are using a custom password setup to match the previous setup on the old website. // Better auth defaults to scrypt but we used bcrypt previously. emailAndPassword: { enabled: true, sendResetPassword: async ({ user, token }) => { await sendPasswordResetEmail({ to: user.email, subject: "Reset your password", token, }); }, requireEmailVerification: true, password: { hash: async (password) => { const ROUNDS = 10; const hashedPassword = await bcrypt.hash(password, ROUNDS); return hashedPassword; }, verify: async ({ hash, password }) => { const isMatch = await bcrypt.compare(password, hash); return isMatch; }, }, }, emailVerification: { sendOnSignUp: true, autoSignInAfterVerification: true, sendVerificationEmail: async ({ user, url }) => { await sendEmailVerificationEmail({ to: user.email, subject: "Verify your email address", url: url, }); }, }, socialProviders: { google: { prompt: "select_account", clientId: process.env.GOOGLE_CLIENT_ID as string, clientSecret: process.env.GOOGLE_CLIENT_SECRET as string, }, twitter: { clientId: process.env.TWITTER_CLIENT_ID as string, clientSecret: process.env.TWITTER_CLIENT_SECRET as string, }, }, // Allow users to sign-in via multiple types of accounts, google email/password etc account: { accountLinking: { enabled: true, trustedProviders: ["google", "twitter"], allowDifferentEmails: false, }, }, user: { additionalFields: { username: { type: "string", }, url: { type: "string", }, bio: { type: "string", }, badges: { type: "string[]", }, publicProfile: { type: "boolean", }, reviewCount: { type: "number", }, }, }, }); ```
Author
Owner

@Debbl commented on GitHub (Jul 11, 2025):

If you use serwist, you might want to try this: https://github.com/serwist/serwist/pull/277

<!-- gh-comment-id:3059881017 --> @Debbl commented on GitHub (Jul 11, 2025): If you use serwist, you might want to try this: https://github.com/serwist/serwist/pull/277
Author
Owner

@darrencarlin commented on GitHub (Jul 12, 2025):

@Debbl I came back to mention this was my issue! Thank you.

<!-- gh-comment-id:3064788752 --> @darrencarlin commented on GitHub (Jul 12, 2025): @Debbl I came back to mention this was my issue! Thank you.
Author
Owner

@dagmawibabi commented on GitHub (Jul 12, 2025):

@darrencarlin all resolved?

<!-- gh-comment-id:3066200213 --> @dagmawibabi commented on GitHub (Jul 12, 2025): @darrencarlin all resolved?
Author
Owner

@darrencarlin commented on GitHub (Jul 12, 2025):

@dagmawibabi yeah, all good! Be curious to know if it was something similar for the person who created the issue.

<!-- gh-comment-id:3066203052 --> @darrencarlin commented on GitHub (Jul 12, 2025): @dagmawibabi yeah, all good! Be curious to know if it was something similar for the person who created the issue.
Author
Owner

@dagmawibabi commented on GitHub (Jul 12, 2025):

@venkadeshshiva can you checkout this thread

<!-- gh-comment-id:3066211541 --> @dagmawibabi commented on GitHub (Jul 12, 2025): @venkadeshshiva can you checkout this thread
Author
Owner

@venkadeshshiva commented on GitHub (Jul 13, 2025):

Thanks a lot everyone. It works for me as well.

I am also using serwist in my nextjs 15 application. that thread helped me a lot.

<!-- gh-comment-id:3066600813 --> @venkadeshshiva commented on GitHub (Jul 13, 2025): Thanks a lot everyone. It works for me as well. I am also using serwist in my nextjs 15 application. that thread helped me a lot.
Sign in to join this conversation.
1 Participants
Notifications
Due Date
No due date set.
Dependencies

No dependencies set.

Reference: github-starred/better-auth#9455