ctx.response.headers.get("set-auth-token") is null #415

Closed
opened 2026-03-13 07:44:55 -05:00 by GiteaMirror · 23 comments
Owner

Originally created by @zhaopengme on GitHub (Dec 17, 2024).

Is this suited for github?

  • Yes, this is suited for github

To Reproduce

image

Current vs. Expected behavior

ctx.response.headers.get("set-auth-token") is null.

What version of Better Auth are you using?

1.0.22

Provide environment information

-OS mac
-chrome

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

Client

Auth config (if applicable)

import { betterAuth } from "better-auth"

export const auth = betterAuth({
  trustedOrigins: ["http://", "https://", "file://"],
  database: drizzleAdapter(db, {
    schema: {
      user: user,
      account: account,
      session: session,
      verification: verification,
    },
    provider: "pg", // or "mysql", "sqlite"
  }),
  emailAndPassword: {
    enabled: true
  },
  plugins: [
    bearer()
  ]
});

Additional context

No response

Originally created by @zhaopengme on GitHub (Dec 17, 2024). ### Is this suited for github? - [ ] Yes, this is suited for github ### To Reproduce <img width="553" alt="image" src="https://github.com/user-attachments/assets/e313742c-60ea-45da-aaab-eb96dd4df968" /> ### Current vs. Expected behavior ctx.response.headers.get("set-auth-token") is null. ### What version of Better Auth are you using? 1.0.22 ### Provide environment information ```bash -OS mac -chrome ``` ### Which area(s) are affected? (Select all that apply) Client ### Auth config (if applicable) ```typescript import { betterAuth } from "better-auth" export const auth = betterAuth({ trustedOrigins: ["http://", "https://", "file://"], database: drizzleAdapter(db, { schema: { user: user, account: account, session: session, verification: verification, }, provider: "pg", // or "mysql", "sqlite" }), emailAndPassword: { enabled: true }, plugins: [ bearer() ] }); ``` ### Additional context _No response_
GiteaMirror added the bug label 2026-03-13 07:44:55 -05:00
Author
Owner

@daveycodez commented on GitHub (Dec 18, 2024):

@Bekacru

Yea this isn't working for me either.


import {
    createAuthClient
} from "better-auth/react"

export const authClient = createAuthClient({
    baseURL: "http://127.0.0.1:3000",
    fetchOptions: {
        auth: {
            type: "Bearer",
            token: () => localStorage.getItem("bearer_token") || "" // get the token from localStorage
        },
        onSuccess: (ctx) => {
            console.log("onSuccess")
            const authToken = ctx.response.headers.get("set-auth-token") // get the token from the response headers
            // Store the token securely (e.g., in localStorage)
            if (authToken) {
                localStorage.setItem("bearer_token", authToken)
            }
        }
    }
})

export const {
    signIn,
    signOut,
    signUp,
    useSession
} = authClient

I'm just testing authenticating to 127.0.0.1 from localhost, I need to be able to authenticate over CORS with Bearer to use Capacitor. First issue is, onSuccess is never being called on the createAuthClient, we should be able to just set it there only and ALL auth methods (sign up, login etc) will all call it. As of right now, onSuccess never gets called.

Then there's the signIn...

   const { error } = await signIn.email({ email, password }, {
            onSuccess: (ctx) => {
                const authToken = ctx.response.headers.get("set-auth-token") // get the token from the response headers
                console.log("headers", Object.fromEntries(ctx.response.headers.entries()))
                console.log("got bearer token", authToken)

                // Store the token securely (e.g., in localStorage)
                localStorage.setItem("bearer_token", authToken!)
            }
        })

headers only includes content-type, no set-auth-token

So sadly, can't use Bearer at all :(

EDIT

Seems like the Bearer token is present on signIn ctx if your currentUrl is equal to baseUrl, but if the baseUrl is not the currentUrl, that header is null. The main reason you would need Bearer though is if you can't use cookies because you're authenticating from another domain or from a local app (Capacitor)

@daveycodez commented on GitHub (Dec 18, 2024): @Bekacru Yea this isn't working for me either. ```ts import { createAuthClient } from "better-auth/react" export const authClient = createAuthClient({ baseURL: "http://127.0.0.1:3000", fetchOptions: { auth: { type: "Bearer", token: () => localStorage.getItem("bearer_token") || "" // get the token from localStorage }, onSuccess: (ctx) => { console.log("onSuccess") const authToken = ctx.response.headers.get("set-auth-token") // get the token from the response headers // Store the token securely (e.g., in localStorage) if (authToken) { localStorage.setItem("bearer_token", authToken) } } } }) export const { signIn, signOut, signUp, useSession } = authClient ``` I'm just testing authenticating to 127.0.0.1 from localhost, I need to be able to authenticate over CORS with Bearer to use Capacitor. First issue is, onSuccess is never being called on the createAuthClient, we should be able to just set it there only and ALL auth methods (sign up, login etc) will all call it. As of right now, onSuccess never gets called. Then there's the signIn... ```ts const { error } = await signIn.email({ email, password }, { onSuccess: (ctx) => { const authToken = ctx.response.headers.get("set-auth-token") // get the token from the response headers console.log("headers", Object.fromEntries(ctx.response.headers.entries())) console.log("got bearer token", authToken) // Store the token securely (e.g., in localStorage) localStorage.setItem("bearer_token", authToken!) } }) ``` headers only includes content-type, no set-auth-token So sadly, can't use Bearer at all :( **EDIT** Seems like the Bearer token is present on signIn ctx if your currentUrl is equal to baseUrl, but if the baseUrl is not the currentUrl, that header is null. The main reason you would need Bearer though is if you can't use cookies because you're authenticating from another domain or from a local app (Capacitor)
Author
Owner

@Bekacru commented on GitHub (Dec 18, 2024):

@daveycodez
the url doesn't directly affect the bearer plugin. is your capacitor url added in trusteed origins list?

@Bekacru commented on GitHub (Dec 18, 2024): @daveycodez the url doesn't directly affect the bearer plugin. is your capacitor url added in trusteed origins list?
Author
Owner

@daveycodez commented on GitHub (Dec 18, 2024):

@Bekacru ahhh I wasn't aware of the trustedOrigins option. That seems to do the trick for me, I'm getting it from signIn

My main issue now though is that the fetchOptions onSuccess is never getting called. It would be ideal to have onSuccess be called by all authentication methods whenever a user logs in, signs up, or the session gets refreshed so we can always have a fresh Bearer token to use

@daveycodez commented on GitHub (Dec 18, 2024): @Bekacru ahhh I wasn't aware of the trustedOrigins option. That seems to do the trick for me, I'm getting it from signIn My main issue now though is that the fetchOptions onSuccess is never getting called. It would be ideal to have onSuccess be called by all authentication methods whenever a user logs in, signs up, or the session gets refreshed so we can always have a fresh Bearer token to use
Author
Owner

@Bekacru commented on GitHub (Dec 18, 2024):

@Bekacru ahhh I wasn't aware of the trustedOrigins option. That seems to do the trick for me, I'm getting it from signIn

My main issue now though is that the fetchOptions onSuccess is never getting called. It would be ideal to have onSuccess be called by all authentication methods whenever a user logs in, signs up, or the session gets refreshed so we can always have a fresh Bearer token to use

until the global onSuccess is fixed, you can use onResponse instead.

@Bekacru commented on GitHub (Dec 18, 2024): > @Bekacru ahhh I wasn't aware of the trustedOrigins option. That seems to do the trick for me, I'm getting it from signIn > > My main issue now though is that the fetchOptions onSuccess is never getting called. It would be ideal to have onSuccess be called by all authentication methods whenever a user logs in, signs up, or the session gets refreshed so we can always have a fresh Bearer token to use until the global onSuccess is fixed, you can use `onResponse` instead.
Author
Owner

@daveycodez commented on GitHub (Dec 18, 2024):

@Bekacru

export const authClient = createAuthClient({
    baseURL: getURL(),
    fetchOptions: {
        auth: {
            type: "Bearer",
            token: () => localStorage.getItem("bearer_token") || "" // get the token from localStorage
        },
        onResponse: (ctx) => {
            console.log(Object.fromEntries(ctx.response.headers.entries()))
            const authToken = ctx.response.headers.get("set-auth-token") // get the token from the response headers
            // Store the token securely (e.g., in localStorage)
            if (authToken) {
                console.log("got an auth token")
                localStorage.setItem("bearer_token", authToken)
            }
        }
    }
})

Just tried that, I'm not receiving the set-auth-token header in the global onResponse jsyk

Also NextJS is complaining about this

 unhandledRejection:  ReferenceError: localStorage is not defined
    at token (src/lib/auth-client.ts:11:46)
   9 |         auth: {
  10 |             type: "Bearer",
> 11 |             token: () => localStorage.getItem("bearer_token") || "" // get the token from localStorage
@daveycodez commented on GitHub (Dec 18, 2024): @Bekacru ```ts export const authClient = createAuthClient({ baseURL: getURL(), fetchOptions: { auth: { type: "Bearer", token: () => localStorage.getItem("bearer_token") || "" // get the token from localStorage }, onResponse: (ctx) => { console.log(Object.fromEntries(ctx.response.headers.entries())) const authToken = ctx.response.headers.get("set-auth-token") // get the token from the response headers // Store the token securely (e.g., in localStorage) if (authToken) { console.log("got an auth token") localStorage.setItem("bearer_token", authToken) } } } }) ``` Just tried that, I'm not receiving the set-auth-token header in the global onResponse jsyk Also NextJS is complaining about this ```shell unhandledRejection: ReferenceError: localStorage is not defined at token (src/lib/auth-client.ts:11:46) 9 | auth: { 10 | type: "Bearer", > 11 | token: () => localStorage.getItem("bearer_token") || "" // get the token from localStorage ```
Author
Owner

@daveycodez commented on GitHub (Dec 18, 2024):

@Bekacru

Not sure what happened but it stopped working randomly, I'm not getting bearer token anymore. I have http://localhost:3000 added to trustedOrigins. Login fetch requests are getting 200's and I am getting the "set-auth-token" header when I inspect the requests in Chrome, but it is null in the signIn onSuccess and onResponse, and then get-session is failing with 404.

@daveycodez commented on GitHub (Dec 18, 2024): @Bekacru Not sure what happened but it stopped working randomly, I'm not getting bearer token anymore. I have http://localhost:3000 added to trustedOrigins. Login fetch requests are getting 200's and I am getting the "set-auth-token" header when I inspect the requests in Chrome, but it is null in the signIn onSuccess and onResponse, and then get-session is failing with 404.
Author
Owner

@cnbrown04 commented on GitHub (Dec 21, 2024):

This seems to be fixed in the newest update.

@cnbrown04 commented on GitHub (Dec 21, 2024): This seems to be fixed in the newest update.
Author
Owner

@a1um1 commented on GitHub (Dec 22, 2024):

I having the same issues here
It's appear that onSuccess or onResponse only give you

  • content-length
  • content-type
@a1um1 commented on GitHub (Dec 22, 2024): I having the same issues here It's appear that onSuccess or onResponse only give you - content-length - content-type
Author
Owner

@cnbrown04 commented on GitHub (Dec 22, 2024):

@a1um1 What version are you on?

@cnbrown04 commented on GitHub (Dec 22, 2024): @a1um1 What version are you on?
Author
Owner

@a1um1 commented on GitHub (Dec 22, 2024):

@a1um1 What version are you on?

Mine version is 1.1.1

@a1um1 commented on GitHub (Dec 22, 2024): > @a1um1 What version are you on? Mine version is 1.1.1
Author
Owner

@cnbrown04 commented on GitHub (Dec 22, 2024):

@a1um1 What version are you on?

Mine version is 1.1.1

Can you post your code?

@cnbrown04 commented on GitHub (Dec 22, 2024): > > @a1um1 What version are you on? > > Mine version is 1.1.1 Can you post your code?
Author
Owner

@a1um1 commented on GitHub (Dec 22, 2024):

@a1um1 What version are you on?

Mine version is 1.1.1

Can you post your code?

import { createAuthClient } from "better-auth/react";
import { envClient } from "./envClient";

export const authClient = createAuthClient({
	baseURL: envClient.NEXT_PUBLIC_API_URL,
	fetchOptions: {
		onResponse: (ctx) => {
			for (const [key, value] of ctx.response.headers.values()) {
				console.log(`${key}: ${value}`);
			}
			const authToken = ctx.response.headers.get("set-auth-token");
			if (authToken) {
				localStorage.setItem("bearer_token", authToken);
			}
		},
		auth: {
			type: "Bearer",
			token: () => localStorage.getItem("bearer_token") || "",
		},
	},
});
@a1um1 commented on GitHub (Dec 22, 2024): > > > @a1um1 What version are you on? > > > > > > Mine version is 1.1.1 > > Can you post your code? ```js import { createAuthClient } from "better-auth/react"; import { envClient } from "./envClient"; export const authClient = createAuthClient({ baseURL: envClient.NEXT_PUBLIC_API_URL, fetchOptions: { onResponse: (ctx) => { for (const [key, value] of ctx.response.headers.values()) { console.log(`${key}: ${value}`); } const authToken = ctx.response.headers.get("set-auth-token"); if (authToken) { localStorage.setItem("bearer_token", authToken); } }, auth: { type: "Bearer", token: () => localStorage.getItem("bearer_token") || "", }, }, }); ```
Author
Owner

@Makisuo commented on GitHub (Dec 24, 2024):

Also facing this issue, dont seem to receive any headers in either onResponse or onSuccess, other than content-type.

trustedOrigin is setup. Version 1.1.3

@Makisuo commented on GitHub (Dec 24, 2024): Also facing this issue, dont seem to receive any headers in either onResponse or onSuccess, other than content-type. trustedOrigin is setup. Version 1.1.3
Author
Owner

@SirMatGuy commented on GitHub (Jan 7, 2025):

Hi!
I'm also facing this issue, i'm using generic oauth and the headers in the context of onSuccess method is empty :(
Version1.1.6

@SirMatGuy commented on GitHub (Jan 7, 2025): Hi! I'm also facing this issue, i'm using generic oauth and the headers in the context of `onSuccess` method is empty :( Version1.1.6
Author
Owner

@jacoborus commented on GitHub (Jan 7, 2025):

@Bekacru this is not a configuration or bearer plugin problem, it's a bug either in Better-Auth client, or Better-Fetch, something removes all the response headers that are not the 'content-type'.

Easy to check with this code:

export const authClient = createAuthClient({
  baseURL: "your_url",
  fetchOptions: {
    onResponse: (ctx) => {
      console.log(JSON.stringify([...ctx.response.headers.entries()]));
    },
  },
});

Then you sign in but it just outputs [["content-type","application/json"]], but if I check the browser dev tools I can see the response headers:

image

Note: using Better-Auth v1.1.10, and the vue client

@jacoborus commented on GitHub (Jan 7, 2025): @Bekacru this is not a configuration or bearer plugin problem, it's a bug either in Better-Auth client, or Better-Fetch, something removes all the response headers that are not the 'content-type'. Easy to check with this code: ```js export const authClient = createAuthClient({ baseURL: "your_url", fetchOptions: { onResponse: (ctx) => { console.log(JSON.stringify([...ctx.response.headers.entries()])); }, }, }); ``` Then you sign in but it just outputs `[["content-type","application/json"]]`, but if I check the browser dev tools I can see the response headers: ![image](https://github.com/user-attachments/assets/582df0d1-48c9-4b0c-8ddf-c80ca266cd5c) Note: using Better-Auth v1.1.10, and the vue client
Author
Owner

@lucaslevin commented on GitHub (Jan 8, 2025):

I'm facing the same issue. Headers are just empty.

@lucaslevin commented on GitHub (Jan 8, 2025): I'm facing the same issue. Headers are just empty.
Author
Owner

@reslear commented on GitHub (Jan 9, 2025):

onResponse:

CleanShot 2025-01-09 at 09 28 41@2x

@reslear commented on GitHub (Jan 9, 2025): `onResponse`: ![CleanShot 2025-01-09 at 09 28 41@2x](https://github.com/user-attachments/assets/baf535bf-537f-4f5b-ac73-9157974106c3)
Author
Owner

@lucaslevin commented on GitHub (Jan 9, 2025):

image

@lucaslevin commented on GitHub (Jan 9, 2025): ![image](https://github.com/user-attachments/assets/975ddff3-5ea4-4805-aa7b-d45bf9c52169)
Author
Owner

@jacoborus commented on GitHub (Jan 10, 2025):

It's a CORS restriction that only allows you to read a few standard headers. See: https://web.dev/articles/introduction-to-fetch#response-types

@jacoborus commented on GitHub (Jan 10, 2025): It's a CORS restriction that only allows you to read a few standard headers. See: https://web.dev/articles/introduction-to-fetch#response-types
Author
Owner

@reslear commented on GitHub (Jan 11, 2025):

so i found solution for express:

app.use(
  cors({
    origin: true,
    credentials: true,
    exposedHeaders: [
      'x-request-id',
      'authorization',
      'x-platform',
      'x-forwarded-for',
      'set-auth-token',  // add this line 
    ],
  })
)

onResponse return header, but onSuccess don't

@reslear commented on GitHub (Jan 11, 2025): so i found solution for express: ```ts app.use( cors({ origin: true, credentials: true, exposedHeaders: [ 'x-request-id', 'authorization', 'x-platform', 'x-forwarded-for', 'set-auth-token', // add this line ], }) ) ``` onResponse return header, but onSuccess don't
Author
Owner

@ShubhamVsCode commented on GitHub (Apr 19, 2025):

I am still not able to get the "set-auth-token" with onResponse and onSuccess both, added 'set-auth-token' in exposedHeaders also i am using itty-router

Image
@ShubhamVsCode commented on GitHub (Apr 19, 2025): I am still not able to get the "set-auth-token" with onResponse and onSuccess both, added 'set-auth-token' in exposedHeaders also i am using itty-router <img width="551" alt="Image" src="https://github.com/user-attachments/assets/9eabb517-8df9-46d0-9580-5f4b4b36c069" />
Author
Owner

@BahaaZidan commented on GitHub (Aug 1, 2025):

Same behaviour. Why is this closed ?

@BahaaZidan commented on GitHub (Aug 1, 2025): Same behaviour. Why is this closed ?
Author
Owner

@jacoborus commented on GitHub (Aug 3, 2025):

@ShubhamVsCode that's a problem with itty-router, it only exposes headers when the HTTP method is 'OPTIONS': 539f54cd2c/src/cors.ts (L56) .

@jacoborus commented on GitHub (Aug 3, 2025): @ShubhamVsCode that's a problem with itty-router, it only exposes headers when the HTTP method is 'OPTIONS': https://github.com/kwhitley/itty-router/blob/539f54cd2c5ec485c4b5cc84c79a35480d3a521c/src/cors.ts#L56 .
Sign in to join this conversation.
1 Participants
Notifications
Due Date
No due date set.
Dependencies

No dependencies set.

Reference: github-starred/better-auth#415