mirror of
https://github.com/better-auth/better-auth.git
synced 2026-07-24 08:58:45 -05:00
fix(electron): forward each Set-Cookie from /electron/init-oauth-proxy individually (#9672)
This commit is contained in:
@@ -0,0 +1,5 @@
|
||||
---
|
||||
"@better-auth/electron": patch
|
||||
---
|
||||
|
||||
`/electron/init-oauth-proxy` now forwards each Set-Cookie from the inner sign-in response separately. The previous `Headers.get("set-cookie")` returned them as one comma-joined string, so the browser silently dropped the transfer-token cookie that the desktop deep-link handoff needs.
|
||||
@@ -8,7 +8,11 @@ import { base64Url } from "@better-auth/utils/base64";
|
||||
import { createHash } from "@better-auth/utils/hash";
|
||||
import { betterFetch } from "@better-fetch/fetch";
|
||||
import { createAuthEndpoint, sessionMiddleware } from "better-auth/api";
|
||||
import { setSessionCookie } from "better-auth/cookies";
|
||||
import {
|
||||
parseSetCookieHeader,
|
||||
setSessionCookie,
|
||||
toCookieOptions,
|
||||
} from "better-auth/cookies";
|
||||
import type { User } from "better-auth/db";
|
||||
import { parseUserOutput } from "better-auth/db";
|
||||
import * as z from "zod";
|
||||
@@ -216,7 +220,7 @@ export const electronInitOAuthProxy = (opts: ElectronOptions) =>
|
||||
|
||||
const headers = new Headers(ctx.request?.headers);
|
||||
headers.set("origin", new URL(ctx.context.baseURL).origin);
|
||||
let setCookie: string | null = null;
|
||||
let setCookies: string[] = [];
|
||||
const searchParams = new URLSearchParams();
|
||||
searchParams.set("client_id", opts.clientID || "electron");
|
||||
searchParams.set("code_challenge", ctx.query.code_challenge);
|
||||
@@ -235,9 +239,8 @@ export const electronInitOAuthProxy = (opts: ElectronOptions) =>
|
||||
body: {
|
||||
provider: ctx.query.provider,
|
||||
},
|
||||
onResponse: (ctx) => {
|
||||
const headers = ctx.response.headers;
|
||||
setCookie = headers.get("set-cookie") ?? null;
|
||||
onResponse: (innerCtx) => {
|
||||
setCookies = innerCtx.response.headers.getSetCookie();
|
||||
},
|
||||
headers,
|
||||
},
|
||||
@@ -249,9 +252,13 @@ export const electronInitOAuthProxy = (opts: ElectronOptions) =>
|
||||
});
|
||||
}
|
||||
|
||||
if (setCookie) {
|
||||
ctx.setHeader("set-cookie", setCookie);
|
||||
for (const cookieStr of setCookies) {
|
||||
const parsed = parseSetCookieHeader(cookieStr);
|
||||
parsed.forEach((attrs, name) => {
|
||||
ctx.setCookie(name, attrs.value, toCookieOptions(attrs));
|
||||
});
|
||||
}
|
||||
|
||||
if (res.data.url && res.data.redirect) {
|
||||
ctx.setHeader("Location", res.data.url);
|
||||
ctx.setStatus(302);
|
||||
|
||||
@@ -179,6 +179,36 @@ describe("Electron", () => {
|
||||
expect(data!.electron_authorization_code).toBeTypeOf("string");
|
||||
});
|
||||
|
||||
it("/electron/init-oauth-proxy should forward each Set-Cookie from the inner sign-in response as a separate header", async () => {
|
||||
const innerHeaders = new Headers();
|
||||
innerHeaders.append("set-cookie", "first.cookie=one; Path=/; HttpOnly");
|
||||
innerHeaders.append("set-cookie", "second.cookie=two; Path=/; HttpOnly");
|
||||
vi.spyOn(globalThis, "fetch").mockResolvedValue(
|
||||
new Response(
|
||||
JSON.stringify({
|
||||
url: "https://provider.example/oauth/authorize",
|
||||
redirect: true,
|
||||
}),
|
||||
{ status: 200, headers: innerHeaders },
|
||||
),
|
||||
);
|
||||
|
||||
const res = (await (auth.api as any).electronInitOAuthProxy({
|
||||
query: {
|
||||
provider: "google",
|
||||
state: "x",
|
||||
code_challenge: "y",
|
||||
code_challenge_method: "S256",
|
||||
},
|
||||
asResponse: true,
|
||||
})) as Response;
|
||||
|
||||
const setCookies = res.headers.getSetCookie();
|
||||
expect(setCookies).toHaveLength(2);
|
||||
expect(setCookies[0]).toMatch(/^first\.cookie=one/);
|
||||
expect(setCookies[1]).toMatch(/^second\.cookie=two/);
|
||||
});
|
||||
|
||||
it("should exchange token", async ({ setProcessType }) => {
|
||||
setProcessType("browser");
|
||||
|
||||
|
||||
Reference in New Issue
Block a user