Adjustment to API authentication with fallback in case the cookie is invalid or missing

This commit is contained in:
2026-09-02 14:04:33 -03:00
parent dfc84c64ee
commit 9cb58809c8
6 changed files with 951 additions and 204 deletions

View File

@@ -502,8 +502,98 @@
}
async function requestTokenRelay() {
const xsrfTokenSource = await getXsrfToken();
const requestEntry = logRequestStart({
let refreshResult = null;
let refreshAttempted = false;
while (true) {
const xsrfTokenSource = await getXsrfToken();
if (!xsrfTokenSource.token) {
if (refreshAttempted) {
const requestEntry = createTokenRelayRequestEntry(xsrfTokenSource, refreshResult);
logRequestFailure(requestEntry, "XSRF token cookie not found after automatic refresh.");
throw new Error("XSRF token cookie not found after automatic refresh.");
}
refreshResult = await refreshXsrfCookie("missing-cookie");
refreshAttempted = true;
continue;
}
const requestEntry = createTokenRelayRequestEntry(xsrfTokenSource, refreshResult);
try {
const response = await fetch(TOKEN_RELAY_URL, {
method: "GET",
credentials: "include",
headers: {
"x-xsrf-token": xsrfTokenSource.token
}
});
const responseBody = await readResponseBodyForDebug(response);
if (!response.ok) {
logRequestSuccess(requestEntry, response, responseBody);
if (!refreshAttempted && isInvalidXsrfStatus(response.status)) {
refreshResult = await refreshXsrfCookie(`tokenrelay-${response.status}`);
refreshAttempted = true;
requestEntry.metadata.xsrfRefresh = formatXsrfRefreshResult(refreshResult);
renderDebugPanel();
continue;
}
throw new Error(`Token relay failed with status ${response.status}.`);
}
let responseData;
try {
responseData = await response.json();
} catch (error) {
logRequestSuccess(requestEntry, response, responseBody);
if (!refreshAttempted) {
refreshResult = await refreshXsrfCookie("tokenrelay-invalid-response");
refreshAttempted = true;
requestEntry.metadata.xsrfRefresh = formatXsrfRefreshResult(refreshResult);
renderDebugPanel();
continue;
}
throw error;
}
if (!responseData || typeof responseData.access_token !== "string" || !responseData.access_token) {
logRequestSuccess(requestEntry, response, responseBody);
if (!refreshAttempted) {
refreshResult = await refreshXsrfCookie("tokenrelay-missing-access-token");
refreshAttempted = true;
requestEntry.metadata.xsrfRefresh = formatXsrfRefreshResult(refreshResult);
renderDebugPanel();
continue;
}
throw new Error("Token relay response does not contain access_token.");
}
logRequestSuccess(requestEntry, response, createDebugBody(JSON.stringify({
...responseData,
access_token: maskToken(responseData.access_token)
}, null, 2)));
return responseData.access_token;
} catch (error) {
logRequestFailure(requestEntry, error.message || "Request failed.");
throw error;
}
}
}
function createTokenRelayRequestEntry(xsrfTokenSource, refreshResult) {
return logRequestStart({
name: "requestTokenRelay",
method: "GET",
url: TOKEN_RELAY_URL,
@@ -519,56 +609,25 @@
xsrfLookupDetails: xsrfTokenSource.lookupDetails && xsrfTokenSource.lookupDetails.length
? xsrfTokenSource.lookupDetails.join(" | ")
: "(none)",
xsrfTokenError: xsrfTokenSource.error || ""
xsrfTokenError: xsrfTokenSource.error || "",
xsrfRefresh: formatXsrfRefreshResult(refreshResult)
}
});
}
if (!xsrfTokenSource.token) {
logRequestFailure(requestEntry, "XSRF token cookie not found.");
throw new Error("XSRF token cookie not found.");
function isInvalidXsrfStatus(status) {
return status === 400 || status === 401 || status === 403;
}
function formatXsrfRefreshResult(result) {
if (!result) {
return "not-requested";
}
try {
const response = await fetch(TOKEN_RELAY_URL, {
method: "GET",
credentials: "include",
headers: {
"x-xsrf-token": xsrfTokenSource.token
}
});
const responseBody = await readResponseBodyForDebug(response);
if (!response.ok) {
logRequestSuccess(requestEntry, response, responseBody);
throw new Error(`Token relay failed with status ${response.status}.`);
}
let responseData;
try {
responseData = await response.json();
} catch (error) {
logRequestSuccess(requestEntry, response, responseBody);
throw error;
}
if (!responseData || typeof responseData.access_token !== "string" || !responseData.access_token) {
logRequestSuccess(requestEntry, response, responseBody);
throw new Error("Token relay response does not contain access_token.");
}
logRequestSuccess(requestEntry, response, createDebugBody(JSON.stringify({
...responseData,
access_token: maskToken(responseData.access_token)
}, null, 2)));
return responseData.access_token;
} catch (error) {
logRequestFailure(requestEntry, error.message || "Request failed.");
throw error;
}
const outcome = result.refreshed ? "refreshed" : "not-refreshed";
const cookieName = result.cookieName || "(none)";
const duration = Number.isFinite(result.durationMs) ? `${result.durationMs}ms` : "unknown";
return `${result.reason}; ${outcome}; cookie=${cookieName}; duration=${duration}; ${result.error || ""}`.trim();
}
async function requestOpportunitiesForPeriod(period) {
@@ -2106,6 +2165,52 @@
});
}
function refreshXsrfCookie(reason) {
const runtimeApi = typeof browser !== "undefined" ? browser : chrome;
if (!runtimeApi || !runtimeApi.runtime || !runtimeApi.runtime.sendMessage) {
return Promise.resolve({
ok: false,
refreshed: false,
cookieName: "",
durationMs: 0,
reason,
error: "Runtime messaging API unavailable."
});
}
return new Promise((resolve) => {
runtimeApi.runtime.sendMessage({
type: "opportunitiesExtension.refreshXsrfCookie"
}, (response) => {
const lastError = runtimeApi.runtime.lastError;
if (lastError) {
resolve({
ok: false,
refreshed: false,
cookieName: "",
durationMs: 0,
reason,
error: lastError.message
});
return;
}
resolve({
...(response || {
ok: false,
refreshed: false,
cookieName: "",
durationMs: 0,
error: "Empty refresh response."
}),
reason
});
});
});
}
function updateAuthBadge(status) {
const badge = document.querySelector(`#${MODAL_ID} .opportunities-extension-auth-badge`);