[GH-ISSUE #2364] loginEndpoint for /sign-in/google bypass the sign-in page without registering the session #9165

Closed
opened 2026-04-13 04:31:55 -05:00 by GiteaMirror · 6 comments
Owner

Originally created by @rittamdebnath on GitHub (Apr 19, 2025).
Original GitHub issue: https://github.com/better-auth/better-auth/issues/2364

Originally assigned to: @ping-maxwell on GitHub.

Is this suited for github?

  • Yes, this is suited for github

To Reproduce

Main issue is that i am able to login via email and password but then, when i try google login it just bypass without getting the session/token

Server is hosted on a seperate domain and client is in vercel but for this demo i am using localhost:3000 (Client) and server(localhost:8000)

Auth.ts

import { sendEmail } from "@/config/email";
import {
  BETTER_AUTH_SECRET,
  BETTER_AUTH_URL,
  GOOGLE_CLIENT_ID,
  GOOGLE_CLIENT_SECRET,
} from "@/security/secrets/env";
import { findUserRole } from "@/services/user.service";
import EmailVerification from "@/templates/emails/EmailVerification";
import ResetPasswordEmail from "@/templates/emails/ResetPassword";
import { betterAuth } from "better-auth";
import { drizzleAdapter } from "better-auth/adapters/drizzle";
import { APIError } from "better-auth/api";
import { customSession } from "better-auth/plugins";
import { organization } from "better-auth/plugins";
import type { PgTable } from "drizzle-orm/pg-core";
import { db } from "../../db";
import * as schema from "../../models";
import { Users } from "../../models/User";
import { LoginPlugin } from "./plugin";

import type { User } from "better-auth"; // Import the default User type

// Extend the User type to include the 'provider' property
interface ExtendedUser extends User {
  provider?: string;
}

const getAdditionalFields = (schema: PgTable) => {
  // biome-ignore lint/suspicious/noExplicitAny: <explanation>
  const fields: Record<string, any> = {};
  const defaultFields = [
    "id",
    "email",
    "image",
    "emailVerified",
    "createdAt",
    "updatedAt",
  ];

  for (const [key, value] of Object.entries(schema)) {
    if (!defaultFields.includes(key)) {
      fields[key] = {
        type: value.config?.type === "integer" ? "number" : "string",
        required: value.notNull ?? false,
      };
    }
  }

  return fields;
};

const additionalField = getAdditionalFields(Users);

export const auth = betterAuth({
  appName: "Rentesy",
  user: {
    modelName: "users",
    fields: {
      name: "firstName",
    },
    additionalFields: additionalField,
  },

  emailVerification: {
    sendVerificationEmail: async ({ user, url }, _request) => {
      await sendEmail(
        user.email,
        "Verify your email",
        EmailVerification({ emailAddress: user.email, verificationUrl: url })
      );
    },
  },
  // Add all possible origins including development, staging and production environments
  trustedOrigins: env.TRUSTED_ORIGIN,
  databaseHooks: {
    user: {
      create: {
        before: async (data: ExtendedUser) => {
          // Allow sign-up via Google but continue to restrict email sign-ups
          if (data.provider === "google") {
            console.log("[AUTH] Allowing Google user creation");
            return { data };
          }

          throw new APIError("BAD_REQUEST", {
            message: "Signup is disabled for email registration",
          });
        },
      },
    },
  },

  plugins: [
    LoginPlugin(),
    organization({
      allowUserToCreateOrganization: false,
    }),
    customSession(async ({ user, session }) => {
      const role = await findUserRole(Number(session.userId));
      return {
        user: {
          ...user,
          role,
        },
        session,
      };
    }),
  ],
  secret: BETTER_AUTH_SECRET,
  advanced: {
    generateId: false,
    // crossSubDomainCookies: {
    //   enabled: false,
    //   domain: ".rentesy.com",
    // },
    // useSecureCookies: true,
    defaultCookieAttributes: {
      sameSite: "lax",
      secure: false,
      httpOnly: true,
      partitioned: true,
    },
  },
  database: drizzleAdapter(db, {
    provider: "pg",
    schema: {
      ...schema,
      users: schema.Users,
      account: schema.Account,
      session: schema.Session,
      verification: schema.Verification,
      organization: schema.Organization,
    },
  }),
  emailAndPassword: {
    enabled: true,
    autoSignIn: true,
    minPasswordLength: 8,
    maxPasswordLength: 20,
    requireEmailVerification: true,
    sendResetPassword: async ({ user, url }, _request) => {
      await sendEmail(
        user.email,
        "Reset your password",
        ResetPasswordEmail({ emailAddress: user.email, resetPasswordLink: url })
      );
    },
  },
  socialProviders: {
    google: {
      clientId: GOOGLE_CLIENT_ID,
      clientSecret: GOOGLE_CLIENT_SECRET,
      redirectURI: `${BETTER_AUTH_URL}/api/auth/callback/google`,
      scope: ["profile", "email"],
      mapProfileToUser: async (profile) => {
        return {
          email: profile.email,
          firstName: profile.given_name,
          lastName: profile.family_name,
          image: profile.picture,
          provider: "google",
        };
      },
      // Allow sign up via Google
      disableSignUp: false,
    },
  },
  account: {
    accountLinking: {
      enabled: true,
      trustedProviders: ["google"],
    },
  },
  // Only disable email sign-up but allow Google sign-up
  disabledPaths: ["/sign-up/email"],
});

Client side Middleware.ts

in the matcher if i put - "/((?!sign-in|sign-up|forgot-password|api/public|_next/static|favicon.ico).*)", the email-password doesn't work

import { betterFetch } from "@better-fetch/fetch";
import type { Session } from "better-auth/types";
import { type NextRequest, NextResponse } from "next/server";

const SESSION_ENDPOINT = "http://localhost:8000/api/auth/get-session"; 
export async function middleware(request: NextRequest) {
  try {
    const response = await betterFetch<Session>(SESSION_ENDPOINT, {
      headers: {
        cookie: request.headers.get("Cookie") || "",
      },
      method: "GET", // Use GET method for fetching session data
    });

    const session = response?.data || null;

    if (!session) {
      console.log("session from middleware if failed\n\n", session);
      return NextResponse.redirect(new URL("/sign-in", request.url));
    }
    console.log("Session data from middleware if success:\n\n", session);
    return NextResponse.next();
  } catch (error) {
    console.error("Auth verification failed:", error);
    return NextResponse.redirect(new URL("/sign-in", request.url));
  }
}

export const config = {
  matcher: ["/"],
};

Code for my auth-client.ts

import { createAuthClient } from "better-auth/react";
import { customSessionClient } from "better-auth/client/plugins";
// import { env } from '@/security/env';

const plugins = [customSessionClient()];
  
export const authClient = createAuthClient({
  baseURL: "http://localhost:8000",
  plugins,
});
export const { useSession } = authClient;
export async function getUserToken(){
  try {
    const { data: session } = await authClient.getSession();
    
    return session?.session?.token || null;
  } catch (err) {
    console.error("Error fetching user token:", err);
    return null;
  }
}

export async function getSession(){
  try {
    const { data, error } = await authClient.getSession();
    
    if (error) {
      console.error("Session error:", error);
      return null;
    }
    
    return data;
  } catch (err) {
    console.error("Failed to get session:", err);
    return null;
  }
}

/**
 * Lists all active sessions for the current user
 * @returns Array of session objects or null if an error occurs
 */
export async function listSessions(){
  try {
    const { data, error } = await authClient.listSessions();
    
    if (error) {
      console.error("Session error:", error);
      return null;
    }
    
    return data;
  } catch (err) {
    console.error("Failed to list sessions:", err);
    return null;
  }
}

/**
 * Revokes a specific session by its token
 * @param token The session token to revoke
 * @returns The response data or null if an error occurs
 */
export async function revokeSession(token: string){
  try {
    const { data, error } = await authClient.revokeSession({
      token
    });
    
    if (error) {
      console.error("Session revocation error:", error);
      return null;
    }
    
    console.log(`Revoked session ${token} successfully`);
    return data;
  } catch (err) {
    console.error(`Failed to revoke session ${token}:`, err);
    return null;
  }
}

/**
 * Revokes all sessions for the current user except the current session
 * @returns The response data or null if an error occurs
 */
export async function revokeOtherSessions(){
  try {
    const { data, error } = await authClient.revokeOtherSessions();
    
    if (error) {
      console.error("Session revocation error:", error);
      return null;
    }
    
    return data;
  } catch (err) {
    console.error("Failed to revoke other sessions:", err);
    return null;
  }
}

/**
 * Revokes all sessions for the current user
 * @returns The response data or null if an error occurs
 */
export async function revokeSessions(){
  try {
    const { data, error } = await authClient.revokeSessions();
    
    if (error) {
      console.error("Session revocation error:", error);
      return null;
    } else {
      console.log("Revoked all sessions successfully");
    }
    
    return data;
  } catch (err) {
    console.error("Failed to revoke sessions:", err);
    return null;
  }
}

server-side plugin.ts


import { userExistByEmail } from "@/services/user.service";
import type { BetterAuthPlugin, HookEndpointContext } from "better-auth";
import { APIError, createAuthMiddleware } from "better-auth/api";

const loginEndpoints = [
  "/sign-in/email",
  "/forget-password",
  "/send-verification-email",
];

// List of valid oauth providers
const validProviders = ["google", "github", "facebook", "twitter"];

export const LoginPlugin = () => {
  return {
    hooks: {
      before: [
        // General hook for all login endpoints
        {
          matcher: (ctx) => loginEndpoints.includes(ctx.path),
          handler: createAuthMiddleware(async (ctx) => {
            const isExist = await userExistByEmail(ctx.body.email);

            if (!isExist) {
              throw new APIError("BAD_REQUEST", {
                message: "No user found with this email",
              });
            }
          }),
        },
        // Specific hook for social login endpoint
        {
          matcher: (ctx: HookEndpointContext) => ctx.path === "/sign-in/social",
          handler: createAuthMiddleware(async (ctx) => {
            // Validate provider param is present and valid
            const { provider } = ctx.body;

            if (!provider) {
              throw new APIError("BAD_REQUEST", {
                message: "Provider is required for social sign-in",
              });
            }

            if (
              typeof provider !== "string" ||
              !validProviders.includes(provider.toLowerCase())
            ) {
              throw new APIError("BAD_REQUEST", {
                message: `Invalid provider. Must be one of: ${validProviders.join(", ")}`,
              });
            }
          }),
        },
      ],
      after: [
        {
          matcher: (ctx) => loginEndpoints.includes(ctx.path),
          handler: createAuthMiddleware(async (ctx) => {
            if (ctx.body.status === 200) {
              ctx.body = {
                message: "Login successful",
              };
            }
          }),
        },
      ],
    },

    // Add rate limiting for login endpoints - Rule: Security
    rateLimit: [
      // {
      //   pathMatcher: (path) => path === "/sign-in/social",
      //   max: 5, // 5 attempts
      //   window: 60, // in 60 seconds
      // },
      {
        pathMatcher: (path) => path === "/forget-password",
        max: 10, // 10 attempts
        window: 60, // in 60 seconds
      },
    ],
    id: "Login-Plugin",
  } satisfies BetterAuthPlugin;
};

Current vs. Expected behavior

Image

Image

DB SCHEMA

Image

Image

in the backend if i try to update the better-auth it throws an error

0|manager-be  | error: invalid input syntax for type integer: "M8XaOoJ8zGkGkvRIemSqVfH2cdGFegLO"
0|manager-be  |      length: 175,
0|manager-be  |    severity: "ERROR",
0|manager-be  |      detail: undefined,
0|manager-be  |        hint: undefined,
0|manager-be  |    position: undefined,
0|manager-be  |  internalPosition: undefined,
0|manager-be  |  internalQuery: undefined,
0|manager-be  |       where: "unnamed portal parameter $1 = '...'",
0|manager-be  |      schema: undefined,
0|manager-be  |       table: undefined,
0|manager-be  |    dataType: undefined,
0|manager-be  |  constraint: undefined,
0|manager-be  |        file: "numutils.c",
0|manager-be  |     routine: "pg_strtoint32_safe",
0|manager-be  |        code: "22P02"
0|manager-be  |       at <anonymous> (/home/ec2-user/api/manager-be/node_modules/pg/lib/client.js:545:17)
0|manager-be  | # SERVER_ERROR:  540 |         result = new this._Promise((resolve, reject) => {
0|manager-be  | 541 |           query.callback = (err, res) => (err ? reject(err) : resolve(res))
0|manager-be  | 542 |         }).catch((err) => {
0|manager-be  | 543 |           // replace the stack trace that leads to TCP.onStreamRead with one that leads back to the
0|manager-be  | 544 |           // application that created the query
0|manager-be  | 545 |           Error.captureStackTrace(err)
0|manager-be  |                       ^
0|manager-be  | error: invalid input syntax for type integer: "ZPIDRw8JHmRAYhlnqrFxZMng7mbJVwr0"
0|manager-be  |      length: 175,
0|manager-be  |    severity: "ERROR",
0|manager-be  |      detail: undefined,
0|manager-be  |        hint: undefined,
0|manager-be  |    position: undefined,
0|manager-be  |  internalPosition: undefined,
0|manager-be  |  internalQuery: undefined,
0|manager-be  |       where: "unnamed portal parameter $1 = '...'",
0|manager-be  |      schema: undefined,
0|manager-be  |       table: undefined,
0|manager-be  |    dataType: undefined,
0|manager-be  |  constraint: undefined,
0|manager-be  |        file: "numutils.c",
0|manager-be  |     routine: "pg_strtoint32_safe",
0|manager-be  |        code: "22P02"

What version of Better Auth are you using?

1.2.5

Provide environment information

- Browser - firefox / Zen / Safari 
- Mac OS Latest

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

Backend

Auth config (if applicable)

import { betterAuth } from "better-auth"
export const auth = betterAuth({
  emailAndPassword: {  
    enabled: true
  },
});

Additional context

server side - Better-auth: 1.2.5
client side - Better-auth: 1.2.7
client side - Better-fetch/fetch": "^1.1.18",

Originally created by @rittamdebnath on GitHub (Apr 19, 2025). Original GitHub issue: https://github.com/better-auth/better-auth/issues/2364 Originally assigned to: @ping-maxwell on GitHub. ### Is this suited for github? - [ ] Yes, this is suited for github ### To Reproduce ## Main issue is that i am able to login via email and password but then, when i try google login it just bypass without getting the session/token Server is hosted on a seperate domain and client is in vercel but for this demo i am using `localhost:3000 (Client)` and server`(localhost:8000)` ### Auth.ts ```ts import { sendEmail } from "@/config/email"; import { BETTER_AUTH_SECRET, BETTER_AUTH_URL, GOOGLE_CLIENT_ID, GOOGLE_CLIENT_SECRET, } from "@/security/secrets/env"; import { findUserRole } from "@/services/user.service"; import EmailVerification from "@/templates/emails/EmailVerification"; import ResetPasswordEmail from "@/templates/emails/ResetPassword"; import { betterAuth } from "better-auth"; import { drizzleAdapter } from "better-auth/adapters/drizzle"; import { APIError } from "better-auth/api"; import { customSession } from "better-auth/plugins"; import { organization } from "better-auth/plugins"; import type { PgTable } from "drizzle-orm/pg-core"; import { db } from "../../db"; import * as schema from "../../models"; import { Users } from "../../models/User"; import { LoginPlugin } from "./plugin"; import type { User } from "better-auth"; // Import the default User type // Extend the User type to include the 'provider' property interface ExtendedUser extends User { provider?: string; } const getAdditionalFields = (schema: PgTable) => { // biome-ignore lint/suspicious/noExplicitAny: <explanation> const fields: Record<string, any> = {}; const defaultFields = [ "id", "email", "image", "emailVerified", "createdAt", "updatedAt", ]; for (const [key, value] of Object.entries(schema)) { if (!defaultFields.includes(key)) { fields[key] = { type: value.config?.type === "integer" ? "number" : "string", required: value.notNull ?? false, }; } } return fields; }; const additionalField = getAdditionalFields(Users); export const auth = betterAuth({ appName: "Rentesy", user: { modelName: "users", fields: { name: "firstName", }, additionalFields: additionalField, }, emailVerification: { sendVerificationEmail: async ({ user, url }, _request) => { await sendEmail( user.email, "Verify your email", EmailVerification({ emailAddress: user.email, verificationUrl: url }) ); }, }, // Add all possible origins including development, staging and production environments trustedOrigins: env.TRUSTED_ORIGIN, databaseHooks: { user: { create: { before: async (data: ExtendedUser) => { // Allow sign-up via Google but continue to restrict email sign-ups if (data.provider === "google") { console.log("[AUTH] Allowing Google user creation"); return { data }; } throw new APIError("BAD_REQUEST", { message: "Signup is disabled for email registration", }); }, }, }, }, plugins: [ LoginPlugin(), organization({ allowUserToCreateOrganization: false, }), customSession(async ({ user, session }) => { const role = await findUserRole(Number(session.userId)); return { user: { ...user, role, }, session, }; }), ], secret: BETTER_AUTH_SECRET, advanced: { generateId: false, // crossSubDomainCookies: { // enabled: false, // domain: ".rentesy.com", // }, // useSecureCookies: true, defaultCookieAttributes: { sameSite: "lax", secure: false, httpOnly: true, partitioned: true, }, }, database: drizzleAdapter(db, { provider: "pg", schema: { ...schema, users: schema.Users, account: schema.Account, session: schema.Session, verification: schema.Verification, organization: schema.Organization, }, }), emailAndPassword: { enabled: true, autoSignIn: true, minPasswordLength: 8, maxPasswordLength: 20, requireEmailVerification: true, sendResetPassword: async ({ user, url }, _request) => { await sendEmail( user.email, "Reset your password", ResetPasswordEmail({ emailAddress: user.email, resetPasswordLink: url }) ); }, }, socialProviders: { google: { clientId: GOOGLE_CLIENT_ID, clientSecret: GOOGLE_CLIENT_SECRET, redirectURI: `${BETTER_AUTH_URL}/api/auth/callback/google`, scope: ["profile", "email"], mapProfileToUser: async (profile) => { return { email: profile.email, firstName: profile.given_name, lastName: profile.family_name, image: profile.picture, provider: "google", }; }, // Allow sign up via Google disableSignUp: false, }, }, account: { accountLinking: { enabled: true, trustedProviders: ["google"], }, }, // Only disable email sign-up but allow Google sign-up disabledPaths: ["/sign-up/email"], }); ``` ### Client side Middleware.ts in the matcher if i put - "/((?!sign-in|sign-up|forgot-password|api/public|_next/static|favicon.ico).*)", the email-password doesn't work ```ts import { betterFetch } from "@better-fetch/fetch"; import type { Session } from "better-auth/types"; import { type NextRequest, NextResponse } from "next/server"; const SESSION_ENDPOINT = "http://localhost:8000/api/auth/get-session"; export async function middleware(request: NextRequest) { try { const response = await betterFetch<Session>(SESSION_ENDPOINT, { headers: { cookie: request.headers.get("Cookie") || "", }, method: "GET", // Use GET method for fetching session data }); const session = response?.data || null; if (!session) { console.log("session from middleware if failed\n\n", session); return NextResponse.redirect(new URL("/sign-in", request.url)); } console.log("Session data from middleware if success:\n\n", session); return NextResponse.next(); } catch (error) { console.error("Auth verification failed:", error); return NextResponse.redirect(new URL("/sign-in", request.url)); } } export const config = { matcher: ["/"], }; ``` ## Code for my auth-client.ts ```ts import { createAuthClient } from "better-auth/react"; import { customSessionClient } from "better-auth/client/plugins"; // import { env } from '@/security/env'; const plugins = [customSessionClient()]; export const authClient = createAuthClient({ baseURL: "http://localhost:8000", plugins, }); export const { useSession } = authClient; export async function getUserToken(){ try { const { data: session } = await authClient.getSession(); return session?.session?.token || null; } catch (err) { console.error("Error fetching user token:", err); return null; } } export async function getSession(){ try { const { data, error } = await authClient.getSession(); if (error) { console.error("Session error:", error); return null; } return data; } catch (err) { console.error("Failed to get session:", err); return null; } } /** * Lists all active sessions for the current user * @returns Array of session objects or null if an error occurs */ export async function listSessions(){ try { const { data, error } = await authClient.listSessions(); if (error) { console.error("Session error:", error); return null; } return data; } catch (err) { console.error("Failed to list sessions:", err); return null; } } /** * Revokes a specific session by its token * @param token The session token to revoke * @returns The response data or null if an error occurs */ export async function revokeSession(token: string){ try { const { data, error } = await authClient.revokeSession({ token }); if (error) { console.error("Session revocation error:", error); return null; } console.log(`Revoked session ${token} successfully`); return data; } catch (err) { console.error(`Failed to revoke session ${token}:`, err); return null; } } /** * Revokes all sessions for the current user except the current session * @returns The response data or null if an error occurs */ export async function revokeOtherSessions(){ try { const { data, error } = await authClient.revokeOtherSessions(); if (error) { console.error("Session revocation error:", error); return null; } return data; } catch (err) { console.error("Failed to revoke other sessions:", err); return null; } } /** * Revokes all sessions for the current user * @returns The response data or null if an error occurs */ export async function revokeSessions(){ try { const { data, error } = await authClient.revokeSessions(); if (error) { console.error("Session revocation error:", error); return null; } else { console.log("Revoked all sessions successfully"); } return data; } catch (err) { console.error("Failed to revoke sessions:", err); return null; } } ``` ### server-side plugin.ts ```ts import { userExistByEmail } from "@/services/user.service"; import type { BetterAuthPlugin, HookEndpointContext } from "better-auth"; import { APIError, createAuthMiddleware } from "better-auth/api"; const loginEndpoints = [ "/sign-in/email", "/forget-password", "/send-verification-email", ]; // List of valid oauth providers const validProviders = ["google", "github", "facebook", "twitter"]; export const LoginPlugin = () => { return { hooks: { before: [ // General hook for all login endpoints { matcher: (ctx) => loginEndpoints.includes(ctx.path), handler: createAuthMiddleware(async (ctx) => { const isExist = await userExistByEmail(ctx.body.email); if (!isExist) { throw new APIError("BAD_REQUEST", { message: "No user found with this email", }); } }), }, // Specific hook for social login endpoint { matcher: (ctx: HookEndpointContext) => ctx.path === "/sign-in/social", handler: createAuthMiddleware(async (ctx) => { // Validate provider param is present and valid const { provider } = ctx.body; if (!provider) { throw new APIError("BAD_REQUEST", { message: "Provider is required for social sign-in", }); } if ( typeof provider !== "string" || !validProviders.includes(provider.toLowerCase()) ) { throw new APIError("BAD_REQUEST", { message: `Invalid provider. Must be one of: ${validProviders.join(", ")}`, }); } }), }, ], after: [ { matcher: (ctx) => loginEndpoints.includes(ctx.path), handler: createAuthMiddleware(async (ctx) => { if (ctx.body.status === 200) { ctx.body = { message: "Login successful", }; } }), }, ], }, // Add rate limiting for login endpoints - Rule: Security rateLimit: [ // { // pathMatcher: (path) => path === "/sign-in/social", // max: 5, // 5 attempts // window: 60, // in 60 seconds // }, { pathMatcher: (path) => path === "/forget-password", max: 10, // 10 attempts window: 60, // in 60 seconds }, ], id: "Login-Plugin", } satisfies BetterAuthPlugin; }; ``` ### Current vs. Expected behavior ![Image](https://github.com/user-attachments/assets/38a233cc-50d0-42a6-98cb-84851348bad3) ![Image](https://github.com/user-attachments/assets/a7908f92-c3d0-46c0-94f4-6d115a68fe65) DB SCHEMA ![Image](https://github.com/user-attachments/assets/a822174b-5ca6-43fe-8948-5e938ec33d49) ![Image](https://github.com/user-attachments/assets/4e59ca43-8e73-406c-b5ce-4f428e8f7767) in the backend if i try to update the better-auth it throws an error ```ts 0|manager-be | error: invalid input syntax for type integer: "M8XaOoJ8zGkGkvRIemSqVfH2cdGFegLO" 0|manager-be | length: 175, 0|manager-be | severity: "ERROR", 0|manager-be | detail: undefined, 0|manager-be | hint: undefined, 0|manager-be | position: undefined, 0|manager-be | internalPosition: undefined, 0|manager-be | internalQuery: undefined, 0|manager-be | where: "unnamed portal parameter $1 = '...'", 0|manager-be | schema: undefined, 0|manager-be | table: undefined, 0|manager-be | dataType: undefined, 0|manager-be | constraint: undefined, 0|manager-be | file: "numutils.c", 0|manager-be | routine: "pg_strtoint32_safe", 0|manager-be | code: "22P02" 0|manager-be | at <anonymous> (/home/ec2-user/api/manager-be/node_modules/pg/lib/client.js:545:17) 0|manager-be | # SERVER_ERROR: 540 | result = new this._Promise((resolve, reject) => { 0|manager-be | 541 | query.callback = (err, res) => (err ? reject(err) : resolve(res)) 0|manager-be | 542 | }).catch((err) => { 0|manager-be | 543 | // replace the stack trace that leads to TCP.onStreamRead with one that leads back to the 0|manager-be | 544 | // application that created the query 0|manager-be | 545 | Error.captureStackTrace(err) 0|manager-be | ^ 0|manager-be | error: invalid input syntax for type integer: "ZPIDRw8JHmRAYhlnqrFxZMng7mbJVwr0" 0|manager-be | length: 175, 0|manager-be | severity: "ERROR", 0|manager-be | detail: undefined, 0|manager-be | hint: undefined, 0|manager-be | position: undefined, 0|manager-be | internalPosition: undefined, 0|manager-be | internalQuery: undefined, 0|manager-be | where: "unnamed portal parameter $1 = '...'", 0|manager-be | schema: undefined, 0|manager-be | table: undefined, 0|manager-be | dataType: undefined, 0|manager-be | constraint: undefined, 0|manager-be | file: "numutils.c", 0|manager-be | routine: "pg_strtoint32_safe", 0|manager-be | code: "22P02" ``` ### What version of Better Auth are you using? 1.2.5 ### Provide environment information ```bash - Browser - firefox / Zen / Safari - Mac OS Latest ``` ### Which area(s) are affected? (Select all that apply) Backend ### Auth config (if applicable) ```typescript import { betterAuth } from "better-auth" export const auth = betterAuth({ emailAndPassword: { enabled: true }, }); ``` ### Additional context server side - Better-auth: 1.2.5 client side - Better-auth: 1.2.7 client side - Better-fetch/fetch": "^1.1.18",
GiteaMirror added the lockedbug labels 2026-04-13 04:31:55 -05:00
Author
Owner

@ping-maxwell commented on GitHub (Apr 20, 2025):

Hey, if you don't mind me asking a few questions:

  1. What does bypass mean?

Main issue is that i am able to login via email and password but then, when i try google login it just bypass without getting the session/token

I assume it just means it was a successful sign-in via google, but just doesn't have a session?

  1. in the backend if i try to update the better-auth it throws an error
    Updating your better-auth version throws an error?
<!-- gh-comment-id:2817034756 --> @ping-maxwell commented on GitHub (Apr 20, 2025): Hey, if you don't mind me asking a few questions: 1. What does bypass mean? > Main issue is that i am able to login via email and password but then, when i try google login it just bypass without getting the session/token I assume it just means it was a successful sign-in via google, but just doesn't have a session? 2. in the backend if i try to update the better-auth it throws an error Updating your better-auth version throws an error?
Author
Owner

@rittamdebnath commented on GitHub (Apr 21, 2025):

  1. bypass means yes you are right, unlike the email and password, for google sign-in it doesn't show the session as you can see in console (in the screenshot: the avatar component and the first name is empty for google login)

  2. yes, in the backend if i try to upgrade the ver to 1.2.7. it shows that the column type has changed to string. However, in our case we use integer for user.id for which i have the attached screenshot of our user schema.

What's interesting is that, In local env, when we the keep the backend ver 1.2.5 and the client 1.2.7 and try to login email/pass - it logs the session, we see the session in the console and then fetches the user data from our backend, everything is good. it goes the same with google but only in local env.

but if we try to switch to prod env just by changing api links as https://qa.manager.rentesy.com the email/pass still works fine. however the google doesn't behave the same.

then we thought, hold on may be its the version thing, so lets upgrade to 1.2.7 to match the client side version.
then we see the error on our BE server console.

Image

<!-- gh-comment-id:2817840619 --> @rittamdebnath commented on GitHub (Apr 21, 2025): 1. bypass means yes you are right, unlike the email and password, for google sign-in it doesn't show the session as you can see in console (in the screenshot: the avatar component and the first name is empty for google login) 2. yes, in the backend if i try to upgrade the ver to 1.2.7. it shows that the column type has changed to string. However, in our case we use integer for user.id for which i have the attached screenshot of our user schema. What's interesting is that, In local env, when we the keep the backend ver 1.2.5 and the client 1.2.7 and try to login email/pass - it logs the session, we see the session in the console and then fetches the user data from our backend, everything is good. it goes the same with google but only in local env. but if we try to switch to prod env just by changing api links as https://qa.manager.rentesy.com the email/pass still works fine. however the google doesn't behave the same. then we thought, hold on may be its the version thing, so lets upgrade to 1.2.7 to match the client side version. then we see the error on our BE server console. ![Image](https://github.com/user-attachments/assets/0821aca5-691e-40a6-9ec2-4c4348e03d13)
Author
Owner

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

I'm not sure if I understand the issue here but if it's related to

  • hooks not being called: you're not handling social logins inside the hooks in your above auth config
<!-- gh-comment-id:2820613892 --> @Bekacru commented on GitHub (Apr 22, 2025): I'm not sure if I understand the issue here but if it's related to - hooks not being called: you're not handling social logins inside the hooks in your above auth config
Author
Owner

@rittamdebnath commented on GitHub (Apr 22, 2025):

can you give a better way to implement hooks, tried all the different ways from the documentation.

<!-- gh-comment-id:2822224550 --> @rittamdebnath commented on GitHub (Apr 22, 2025): can you give a better way to implement hooks, tried all the different ways from the documentation.
Author
Owner

@rittamdebnath commented on GitHub (May 4, 2025):

@Bekacru @ping-maxwell any update on this ticket?

<!-- gh-comment-id:2849328056 --> @rittamdebnath commented on GitHub (May 4, 2025): @Bekacru @ping-maxwell any update on this ticket?
Author
Owner

@dosubot[bot] commented on GitHub (Aug 13, 2025):

Hi, @rittamdebnath. I'm Dosu, and I'm helping the better-auth team manage their backlog and am marking this issue as stale.

Issue Summary:

  • You reported that Google login bypasses the sign-in page without creating a session or token, unlike email/password login, in your BetterAuth setup with separate client and server domains.
  • Upgrading the BetterAuth backend to version 1.2.7 caused a database schema error due to a user ID type mismatch.
  • The Google login issue only occurs in the production environment.
  • Maintainer ping-maxwell requested clarification, and Bekacru suggested the issue might be due to missing social login handling in hooks.
  • You asked for guidance on properly implementing hooks but have not yet received a resolution.

Next Steps:

  • Please confirm if this issue is still relevant with the latest version of better-auth and if you need further assistance by commenting on this issue.
  • If there is no response within 7 days, I will automatically close this issue.

Thank you for your understanding and contribution!

<!-- gh-comment-id:3184514934 --> @dosubot[bot] commented on GitHub (Aug 13, 2025): Hi, @rittamdebnath. I'm [Dosu](https://dosu.dev), and I'm helping the better-auth team manage their backlog and am marking this issue as stale. **Issue Summary:** - You reported that Google login bypasses the sign-in page without creating a session or token, unlike email/password login, in your BetterAuth setup with separate client and server domains. - Upgrading the BetterAuth backend to version 1.2.7 caused a database schema error due to a user ID type mismatch. - The Google login issue only occurs in the production environment. - Maintainer ping-maxwell requested clarification, and Bekacru suggested the issue might be due to missing social login handling in hooks. - You asked for guidance on properly implementing hooks but have not yet received a resolution. **Next Steps:** - Please confirm if this issue is still relevant with the latest version of better-auth and if you need further assistance by commenting on this issue. - If there is no response within 7 days, I will automatically close this issue. Thank you for your understanding and contribution!
Sign in to join this conversation.
1 Participants
Notifications
Due Date
No due date set.
Dependencies

No dependencies set.

Reference: github-starred/better-auth#9165