authenticating to website that connects to remote better-auth service #2332

Open
opened 2026-03-13 09:44:38 -05:00 by GiteaMirror · 8 comments
Owner

Originally created by @saleemkheralden on GitHub (Nov 18, 2025).

Is this suited for github?

  • Yes, this is suited for github

To Reproduce

i have remote better-auth service running in prod (it's working if im using a pc), when i enter a nextjs website that should run the authentication on the remote service from a phone (doesn't matter if prod of locally running website), and i run the login logic i get nextjs error

Runtime TypeError
Load failed

heres the nextjs component code:

const [credentials, setCredentials] = useState({username: "", password: ""});
const handleSignin = async (e: React.FormEvent) => {
    e.preventDefault();
    const button = "signin-button";
    setLoading(button);

    const { data, error } = await authClient.signIn.email({
      ...credentials,
      callbackURL: "/home",
    });

    const {
      data: session,
      error: sessionError,
    } = await authClient.getSession();

    alert(data?.token);

    console.log(session, sessionError);
    console.log(data);

    console.log("Credentials:", credentials);
    console.log("Signin response:", { data, error });
  };

this is the authClient config

export const authClient = createAuthClient({ 
  baseURL: process.env.NEXT_PUBLIC_AUTH_BASE_URL,
  fetchOptions: {
    credentials: "include",
    auth: {
      type: "Bearer",
      token: () => localStorage.getItem("bearer_token") || "",
    },
    onSuccess: (ctx) => {
      const authToken = ctx.response.headers.get("set-auth-token");
      if (authToken) localStorage.setItem("bearer_token", authToken);
    },
  },
});

this is the backend better-auth service code

export const auth = betterAuth({
  database: mysql.createPool({
    host: process.env.DB_HOST!,
    user: process.env.DB_USER!,
    password: process.env.DB_PASSWORD!,
    port: process.env.DB_PORT ? Number(process.env.DB_PORT) : 3306,
    database: process.env.DB_SCHEMA!,
    timezone: 'Z',
  }),
  plugins: [bearer()],
  baseURL: process.env.BETTER_AUTH_URL!,
  trustedOrigins: [
    process.env.BETTER_AUTH_URL!,
    process.env.FRONT_END_URL
  ],
  emailAndPassword: {
    enabled: true,
  },
  socialProviders: {
    google: {
      prompt: 'select_account',
      clientId: process.env.GOOGLE_ID || '',
      clientSecret: process.env.GOOGLE_SECRET || '',
      redirectURI: `${process.env.BETTER_AUTH_URL!}/api/auth/callback/google`,
    },
  },

  advanced: {
    defaultCookieAttributes: {
      sameSite: process.env.ENV === 'prod' ? 'none' : 'lax',
      secure: process.env.ENV === 'prod',
      httpOnly: true,
      path: '/',
      domain: process.env.BETTER_AUTH_URL!.split('//')[1].split(':')[0],
    },
  },
});

i tried the code with and without the bearer plugin, same result.

Current vs. Expected behavior

After some reading i found out that iphone browsers block 3rd party cookies, and so i switched to bearer logic, i expect it to work, but for now all im getting once the authClient.signIn.email function gets executed is the error in the section above.

What version of Better Auth are you using?

1.3.34

System info

{
  "system": {
    "platform": "win32",
    "arch": "x64",
    "version": "Windows 10 Home",
    "release": "10.0.19045",
    "cpuCount": 12,
    "cpuModel": "Intel(R) Core(TM) i5-10400F CPU @ 2.90GHz",
    "totalMemory": "15.87 GB",
    "freeMemory": "5.61 GB"
  },
  "node": {
    "version": "v22.14.0",
    "env": "development"
  },
  "packageManager": {
    "name": "npm",
    "version": "10.9.2"
  },
  "frameworks": [
    {
      "name": "express",
      "version": "^5.1.0"
    }
  ],
  "databases": [
    {
      "name": "mysql2",
      "version": "^3.12.0"
    }
  ],
  "betterAuth": {
    "version": "^1.3.34",
    "config": null
  }
}

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

Client, Backend

Auth config (if applicable)

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

Additional context

No response

Originally created by @saleemkheralden on GitHub (Nov 18, 2025). ### Is this suited for github? - [ ] Yes, this is suited for github ### To Reproduce i have remote better-auth service running in prod (it's working if im using a pc), when i enter a nextjs website that should run the authentication on the remote service from a phone (doesn't matter if prod of locally running website), and i run the login logic i get nextjs error ``` Runtime TypeError Load failed ``` heres the nextjs component code: ``` const [credentials, setCredentials] = useState({username: "", password: ""}); const handleSignin = async (e: React.FormEvent) => { e.preventDefault(); const button = "signin-button"; setLoading(button); const { data, error } = await authClient.signIn.email({ ...credentials, callbackURL: "/home", }); const { data: session, error: sessionError, } = await authClient.getSession(); alert(data?.token); console.log(session, sessionError); console.log(data); console.log("Credentials:", credentials); console.log("Signin response:", { data, error }); }; ``` this is the authClient config ``` export const authClient = createAuthClient({ baseURL: process.env.NEXT_PUBLIC_AUTH_BASE_URL, fetchOptions: { credentials: "include", auth: { type: "Bearer", token: () => localStorage.getItem("bearer_token") || "", }, onSuccess: (ctx) => { const authToken = ctx.response.headers.get("set-auth-token"); if (authToken) localStorage.setItem("bearer_token", authToken); }, }, }); ``` this is the backend better-auth service code ``` export const auth = betterAuth({ database: mysql.createPool({ host: process.env.DB_HOST!, user: process.env.DB_USER!, password: process.env.DB_PASSWORD!, port: process.env.DB_PORT ? Number(process.env.DB_PORT) : 3306, database: process.env.DB_SCHEMA!, timezone: 'Z', }), plugins: [bearer()], baseURL: process.env.BETTER_AUTH_URL!, trustedOrigins: [ process.env.BETTER_AUTH_URL!, process.env.FRONT_END_URL ], emailAndPassword: { enabled: true, }, socialProviders: { google: { prompt: 'select_account', clientId: process.env.GOOGLE_ID || '', clientSecret: process.env.GOOGLE_SECRET || '', redirectURI: `${process.env.BETTER_AUTH_URL!}/api/auth/callback/google`, }, }, advanced: { defaultCookieAttributes: { sameSite: process.env.ENV === 'prod' ? 'none' : 'lax', secure: process.env.ENV === 'prod', httpOnly: true, path: '/', domain: process.env.BETTER_AUTH_URL!.split('//')[1].split(':')[0], }, }, }); ``` i tried the code with and without the bearer plugin, same result. ### Current vs. Expected behavior After some reading i found out that iphone browsers block 3rd party cookies, and so i switched to bearer logic, i expect it to work, but for now all im getting once the authClient.signIn.email function gets executed is the error in the section above. ### What version of Better Auth are you using? 1.3.34 ### System info ```bash { "system": { "platform": "win32", "arch": "x64", "version": "Windows 10 Home", "release": "10.0.19045", "cpuCount": 12, "cpuModel": "Intel(R) Core(TM) i5-10400F CPU @ 2.90GHz", "totalMemory": "15.87 GB", "freeMemory": "5.61 GB" }, "node": { "version": "v22.14.0", "env": "development" }, "packageManager": { "name": "npm", "version": "10.9.2" }, "frameworks": [ { "name": "express", "version": "^5.1.0" } ], "databases": [ { "name": "mysql2", "version": "^3.12.0" } ], "betterAuth": { "version": "^1.3.34", "config": null } } ``` ### Which area(s) are affected? (Select all that apply) Client, Backend ### Auth config (if applicable) ```typescript import { betterAuth } from "better-auth" export const auth = betterAuth({ emailAndPassword: { enabled: true }, }); ``` ### Additional context _No response_
GiteaMirror added the nextjs label 2026-03-13 09:44:38 -05:00
Author
Owner

@better-auth-agent[bot] commented on GitHub (Nov 18, 2025):

(tag @better-auth-agent if you would like an answer from the Agent)

DiagramDiscordGitHub

Diagram Join Star

@better-auth-agent[bot] commented on GitHub (Nov 18, 2025): (tag @better-auth-agent if you would like an answer from the Agent) <!-- bot:webhook reply v1 --> [Diagram](https://repodiagrams.s3.eu-north-1.amazonaws.com/better-auth_ultra_detailed_interactive.html) • [Discord](https://discord.gg/better-auth) • [GitHub](https://github.com/better-auth/better-auth) [![Diagram](https://img.shields.io/badge/Diagram-2b3137?style=flat-square)](https://repodiagrams.s3.eu-north-1.amazonaws.com/better-auth_ultra_detailed_interactive.html) [![Join](https://img.shields.io/badge/join-5865F2?logo=discord&logoColor=white&style=flat-square)](https://discord.gg/better-auth) [![Star](https://img.shields.io/badge/star-181717?logo=github&logoColor=white&style=flat-square)](https://github.com/better-auth/better-auth)
Author
Owner

@dosubot[bot] commented on GitHub (Nov 18, 2025):

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

This is suited for GitHub, as it involves cross-origin authentication and mobile browser compatibility with Better Auth.

Your issue is likely caused by a combination of CORS and bearer token handling across origins, especially on mobile browsers that block third-party cookies. Here are the most important things to check and try:

  1. Make sure your Express CORS middleware includes 'set-auth-token' in the exposedHeaders array so the client can read the token from the response header. Example:

    app.use(
      cors({
        origin: true,
        credentials: true,
        exposedHeaders: ['set-auth-token'],
      })
    );
    

    Reference

  2. Confirm that trustedOrigins is set at the top level of your Better Auth config (not inside advanced). If it's misplaced, requests from your frontend will be rejected with a 403 or missing headers. Reference

  3. Switch from onSuccess to onResponse in your fetchOptions to reliably capture the bearer token header:

    fetchOptions: {
      onResponse: (ctx) => {
        const authToken = ctx.response.headers.get("set-auth-token");
        if (authToken) localStorage.setItem("bearer_token", decodeURIComponent(authToken));
      },
      // ...
    }
    

    Reference

  4. The bearer token is URL-encoded by the backend, so always decode it before storing or using it. Reference

  5. If you still get the error, inspect the network tab on your mobile device (using remote debugging) to check for CORS errors, missing headers, or failed requests. Compare with desktop behavior to spot differences.

If these steps don't resolve the issue, please share the full error stack trace and any relevant backend logs to help narrow down the root cause.

To reply, just mention @dosu.


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

@dosubot[bot] commented on GitHub (Nov 18, 2025): <!-- Greeting --> Hi @saleemkheralden! I'm [Dosu](https://go.dosu.dev/dosubot) and I’m helping the better-auth team. <!-- Answer --> This is suited for GitHub, as it involves cross-origin authentication and mobile browser compatibility with Better Auth. Your issue is likely caused by a combination of CORS and bearer token handling across origins, especially on mobile browsers that block third-party cookies. Here are the most important things to check and try: 1. Make sure your Express CORS middleware includes `'set-auth-token'` in the `exposedHeaders` array so the client can read the token from the response header. Example: ```js app.use( cors({ origin: true, credentials: true, exposedHeaders: ['set-auth-token'], }) ); ``` [Reference](https://github.com/better-auth/better-auth/issues/917) 2. Confirm that `trustedOrigins` is set at the top level of your Better Auth config (not inside `advanced`). If it's misplaced, requests from your frontend will be rejected with a 403 or missing headers. [Reference](https://github.com/better-auth/better-auth/issues/1585) 3. Switch from `onSuccess` to `onResponse` in your `fetchOptions` to reliably capture the bearer token header: ```js fetchOptions: { onResponse: (ctx) => { const authToken = ctx.response.headers.get("set-auth-token"); if (authToken) localStorage.setItem("bearer_token", decodeURIComponent(authToken)); }, // ... } ``` [Reference](https://github.com/better-auth/better-auth/issues/1631) 4. The bearer token is URL-encoded by the backend, so always decode it before storing or using it. [Reference](https://github.com/better-auth/better-auth/issues/5930) 5. If you still get the error, inspect the network tab on your mobile device (using remote debugging) to check for CORS errors, missing headers, or failed requests. Compare with desktop behavior to spot differences. If these steps don't resolve the issue, please share the full error stack trace and any relevant backend logs to help narrow down the root cause. <!-- 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/c18b0859-b189-4d36-84bc-911171b6caea?feedback_type=great_response) | [Irrelevant](https://app.dosu.dev/response-feedback/c18b0859-b189-4d36-84bc-911171b6caea?feedback_type=irrelevant_answer) | [Incorrect](https://app.dosu.dev/response-feedback/c18b0859-b189-4d36-84bc-911171b6caea?feedback_type=incorrect_sources) | [Verbose](https://app.dosu.dev/response-feedback/c18b0859-b189-4d36-84bc-911171b6caea?feedback_type=too_verbose) | [Hallucination](https://app.dosu.dev/response-feedback/c18b0859-b189-4d36-84bc-911171b6caea?feedback_type=hallucination) | [Report 🐛](https://app.dosu.dev/response-feedback/c18b0859-b189-4d36-84bc-911171b6caea?feedback_type=bug_report) | [Other](https://app.dosu.dev/response-feedback/c18b0859-b189-4d36-84bc-911171b6caea?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/6079)
Author
Owner

@rovertrack commented on GitHub (Nov 19, 2025):

is this solved?

@rovertrack commented on GitHub (Nov 19, 2025): is this solved?
Author
Owner

@saleemkheralden commented on GitHub (Nov 19, 2025):

is this solved?

Not yet, I'm still working this out.

@saleemkheralden commented on GitHub (Nov 19, 2025): > is this solved? > > Not yet, I'm still working this out.
Author
Owner

@thereis commented on GitHub (Nov 28, 2025):

Any updates?

@thereis commented on GitHub (Nov 28, 2025): Any updates?
Author
Owner

@saleemkheralden commented on GitHub (Nov 28, 2025):

I put that on hold, still didn't figure out how to solve this.
if there's a solution for this i would be happy if ya'll share it with me, and i'll update in the future if i fix this.

@saleemkheralden commented on GitHub (Nov 28, 2025): I put that on hold, still didn't figure out how to solve this. if there's a solution for this i would be happy if ya'll share it with me, and i'll update in the future if i fix this.
Author
Owner

@thereis commented on GitHub (Nov 29, 2025):

|@saleemkheralden I downgraded to 1.3.34 and the issue is not there.

The issue only happens for iOS devices when you try to set a cookie from a different domain, for example:

frontend: example.com
api: api.example.com

The best scenario is to put the auth on the same BFF as the frontend and the issue will be gone, but I hate this idea of hosting the auth on the same instance as my frontend.

@thereis commented on GitHub (Nov 29, 2025): |@saleemkheralden I downgraded to 1.3.34 and the issue is not there. The issue only happens for iOS devices when you try to set a cookie from a different domain, for example: frontend: example.com api: api.example.com The best scenario is to put the auth on the same BFF as the frontend and the issue will be gone, but I hate this idea of hosting the auth on the same instance as my frontend.
Author
Owner

@Paola3stefania commented on GitHub (Jan 9, 2026):

@saleemkheralden @thereis @rovertrack are you still with this issue in last version?

@Paola3stefania commented on GitHub (Jan 9, 2026): @saleemkheralden @thereis @rovertrack are you still with this issue in last version?
Sign in to join this conversation.
1 Participants
Notifications
Due Date
No due date set.
Dependencies

No dependencies set.

Reference: github-starred/better-auth#2332