fix(expo): skip cookie/expo-origin headers for ID token requests (#7069)

Co-authored-by: Bereket Engida <86073083+Bekacru@users.noreply.github.com>
This commit is contained in:
kimchi-developer
2026-03-01 08:44:05 +09:00
committed by GitHub
parent 711528674e
commit fefbde28fe
2 changed files with 80 additions and 2 deletions

View File

@@ -415,9 +415,26 @@ export const expoClient = (opts: ExpoClientOptions) => {
};
}
options = options || {};
const storedCookie = storage.getItem(cookieName);
const cookie = getCookie(storedCookie || "{}");
options.credentials = "omit";
/**
* ID token flow (native sign-in) doesn't need cookie-based auth.
* The ID token itself is cryptographically signed by the provider
* and validated server-side, so no session cookies or origin
* validation is required.
*
* Sending cookie/expo-origin headers for ID token requests triggers
* unnecessary origin checks that fail for custom URL schemes.
*/
const isIdTokenRequest = options.body?.idToken !== undefined;
if (isIdTokenRequest) {
options.headers = {
...options.headers,
"x-skip-oauth-proxy": "true",
};
} else {
const storedCookie = storage.getItem(cookieName);
const cookie = getCookie(storedCookie || "{}");
options.headers = {
...options.headers,
...(cookie ? { cookie } : {}),

View File

@@ -417,6 +417,67 @@ describe("expo", async () => {
expect(origin).toBe(null);
});
it("should not send cookie or expo-origin headers for ID token requests", async () => {
let cookieHeader: string | null | undefined = null;
let expoOriginHeader: string | null | undefined = null;
let originHeader: string | null | undefined = null;
const storage = new Map<string, string>();
// Pre-populate storage with a cookie to verify it's NOT sent
storage.set(
"better-auth_cookie",
JSON.stringify({
"better-auth.session_token": {
value: "existing-token",
expires: new Date(Date.now() + 1000 * 60 * 60).toISOString(),
},
}),
);
const { client } = await getTestInstance(
{
hooks: {
before: createAuthMiddleware(async (ctx) => {
cookieHeader = ctx.request?.headers.get("cookie");
expoOriginHeader = ctx.request?.headers.get("expo-origin");
originHeader = ctx.request?.headers.get("origin");
}),
},
socialProviders: {
google: {
clientId: "test",
clientSecret: "test",
},
},
plugins: [expo()],
},
{
clientOptions: {
plugins: [
expoClient({
storage: {
getItem: (key) => storage.get(key) || null,
setItem: async (key, value) => storage.set(key, value),
},
}),
],
},
},
);
// ID token request - should NOT have cookie or expo-origin headers
await client.signIn.social({
provider: "google",
idToken: {
token: "fake-id-token",
},
});
expect(cookieHeader).toBeNull();
expect(expoOriginHeader).toBeNull();
expect(originHeader).toBeNull();
});
it("should preserve existing cookies on link-social", async () => {
await client.signIn.email({
email: testUser.email,