Skip to content

Commit 980d5b8

Browse files
committed
fix: capture fresh Set-Cookie headers from NotebookLM homepage
Google rotates SIDCC, __Secure-*PSIDCC, and NID cookies on every page load. Python's httpx captures these automatically via its cookie jar, but Node.js fetch() does not. Without the fresh cookies, all RPC calls return error code [5] (null result). Now parses Set-Cookie headers from the homepage GET response and merges them into the cookie record before building the Cookie header for subsequent RPC POST requests.
1 parent e1ca775 commit 980d5b8

File tree

1 file changed

+32
-1
lines changed

1 file changed

+32
-1
lines changed

lib/services/notebooklm/auth.ts

Lines changed: 32 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -174,6 +174,11 @@ export async function initAuth(): Promise<AuthTokens> {
174174
);
175175

176176
// Step 2: Fetch NotebookLM homepage to get CSRF and session tokens
177+
// IMPORTANT: The homepage response sets fresh session cookies (SIDCC, NID,
178+
// __Secure-*PSIDCC) via Set-Cookie headers. These MUST be captured and
179+
// included in subsequent RPC calls, otherwise the API returns error code [5].
180+
// Python's httpx handles this automatically via its cookie jar; we must do
181+
// it manually since Node.js fetch() doesn't merge Set-Cookie into requests.
177182
let html: string;
178183
let finalUrl: string;
179184

@@ -194,6 +199,29 @@ export async function initAuth(): Promise<AuthTokens> {
194199

195200
finalUrl = response.url;
196201
html = await response.text();
202+
203+
// Capture Set-Cookie headers and merge into our cookies
204+
// Google rotates SIDCC, __Secure-*PSIDCC, and NID on every page load
205+
const setCookieHeaders = response.headers.getSetCookie?.() ?? [];
206+
let freshCookieCount = 0;
207+
for (const setCookie of setCookieHeaders) {
208+
// Parse "NAME=VALUE; ..." format
209+
const eqIdx = setCookie.indexOf('=');
210+
if (eqIdx === -1) continue;
211+
const name = setCookie.substring(0, eqIdx);
212+
const rest = setCookie.substring(eqIdx + 1);
213+
const semiIdx = rest.indexOf(';');
214+
const value = semiIdx === -1 ? rest : rest.substring(0, semiIdx);
215+
if (name && value) {
216+
cookies[name] = value;
217+
freshCookieCount++;
218+
}
219+
}
220+
if (freshCookieCount > 0) {
221+
console.log(
222+
`[NotebookLM] Captured ${freshCookieCount} fresh cookies from homepage response`
223+
);
224+
}
197225
} catch (error: unknown) {
198226
if (error instanceof Error && error.name === 'AbortError') {
199227
throw new Error(
@@ -238,11 +266,14 @@ export async function initAuth(): Promise<AuthTokens> {
238266
}
239267
const sessionId = sessionMatch[1];
240268

269+
// Rebuild cookie header with fresh cookies from homepage response
270+
const updatedCookieHeader = buildCookieHeader(cookies);
271+
241272
console.log('[NotebookLM] Authentication initialized successfully');
242273

243274
return {
244275
cookies,
245-
cookieHeader,
276+
cookieHeader: updatedCookieHeader,
246277
csrfToken,
247278
sessionId,
248279
};

0 commit comments

Comments
 (0)