function createAuthConsumerSession({ CUSTOMER_TOKEN_MAX_ATTEMPTS, CUSTOMER_TOKEN_RETRY_BASE_DELAY_MS, CUSTOMER_TOKEN_RETRY_MAX_DELAY_MS, CUSTOMER_TOKEN_SERVICE_URL, CUSTOMER_TOKEN_SESSION_URL, extractTokenServiceAuthorization, fetchJson, supportsFirefoxCookieStoreTabs, tabsCreate, tabsRemove, wait }) { async function requestConsumerTokenService(sender) { const debugRequests = []; try { const tokenResponse = await fetchConsumerTokenServiceUntilJson(sender, debugRequests); const accountSummaryAuthorization = extractTokenServiceAuthorization( tokenResponse, "ACCP-TS", "account-ti-authorization" ); const accountPlansAuthorization = extractTokenServiceAuthorization( tokenResponse, "OCICONS-TS", "g2m-authorization" ); const tokenRequestDebugEntry = debugRequests[debugRequests.length - 1]; if (tokenRequestDebugEntry) { tokenRequestDebugEntry.metadata = { ...tokenRequestDebugEntry.metadata, accountSummaryTokenService: "ACCP-TS", accountSummaryAuthorizationHeader: accountSummaryAuthorization.name || "(missing)", accountSummaryAuthorizationValue: accountSummaryAuthorization.value ? `(present, ${accountSummaryAuthorization.value.length} characters)` : "(missing)", accountSummaryAuthorizationExpires: accountSummaryAuthorization.expires || "(not provided)", accountPlansTokenService: "OCICONS-TS", accountPlansAuthorizationHeader: accountPlansAuthorization.name || "(missing)", accountPlansAuthorizationValue: accountPlansAuthorization.value ? `(present, ${accountPlansAuthorization.value.length} characters)` : "(missing)", accountPlansAuthorizationExpires: accountPlansAuthorization.expires || "(not provided)" }; } if (!accountSummaryAuthorization.value) { throw new Error("ACCP-TS uiaasHeader.value was not found in requestConsumerTokenService."); } if (!accountPlansAuthorization.value) { throw new Error("OCICONS-TS uiaasHeader.value was not found in requestConsumerTokenService."); } return { ok: true, authorizations: { accountSummary: accountSummaryAuthorization, accountPlans: accountPlansAuthorization, installBase: extractTokenServiceAuthorization(tokenResponse, "SAIB-TS", ""), customerServiceRequests: extractTokenServiceAuthorization(tokenResponse, "SPA-ORANGE", ""), workloadActions: extractTokenServiceAuthorization(tokenResponse, "WWB-TS", "Wwb-Provider-Authorization") }, debugRequests }; } catch (error) { error.debugRequests = debugRequests; throw error; } } async function fetchConsumerTokenServiceUntilJson(sender, debugRequests) { let attempt = 0; let sessionTab = null; let receivedValidJson = false; try { while (attempt < CUSTOMER_TOKEN_MAX_ATTEMPTS) { attempt += 1; try { const tokenResponse = await fetchJson(CUSTOMER_TOKEN_SERVICE_URL, { method: "GET", credentials: "include", cache: "no-store", headers: { Accept: "application/json", "Cache-Control": "no-cache, no-store, max-age=0", Pragma: "no-cache" } }, "requestConsumerTokenService", debugRequests); const debugEntry = debugRequests[debugRequests.length - 1]; if (debugEntry) { debugEntry.metadata.attempt = String(attempt); } receivedValidJson = true; return tokenResponse; } catch (error) { const isHtmlFallback = error && error.code === "INVALID_JSON_RESPONSE" && error.httpStatus === 200; const isSessionUnauthorized = error && (error.httpStatus === 401 || error.httpStatus === 403); const needsSessionRecovery = isHtmlFallback || isSessionUnauthorized; const retryReason = isSessionUnauthorized ? `HTTP ${error.httpStatus} response requires SPA session recovery` : "HTTP 200 response did not contain valid JSON"; const debugEntry = debugRequests[debugRequests.length - 1]; if (!needsSessionRecovery || attempt >= CUSTOMER_TOKEN_MAX_ATTEMPTS) { if (debugEntry) { debugEntry.metadata.attempt = String(attempt); if (needsSessionRecovery) { debugEntry.metadata.retryLimitReached = "true"; debugEntry.metadata.retryReason = retryReason; debugEntry.error = isHtmlFallback ? `requestConsumerTokenService returned invalid JSON after ${CUSTOMER_TOKEN_MAX_ATTEMPTS} attempts.` : `requestConsumerTokenService failed with status ${error.httpStatus} after ${CUSTOMER_TOKEN_MAX_ATTEMPTS} attempts.`; error.message = debugEntry.error; } } throw error; } if (!sessionTab) { try { sessionTab = await openConsumerTokenSessionTabWithRetry(sender, debugEntry); if (debugEntry) { debugEntry.metadata.sessionBootstrapUrl = CUSTOMER_TOKEN_SESSION_URL; debugEntry.metadata.sessionBootstrapState = "opened-inactive-tab"; } } catch (sessionError) { if (debugEntry) { debugEntry.metadata.sessionBootstrapUrl = CUSTOMER_TOKEN_SESSION_URL; debugEntry.metadata.sessionBootstrapState = `tab-open-failed: ${sessionError.message || "unknown error"}`; } } } else if (debugEntry) { debugEntry.metadata.sessionBootstrapState = "waiting-for-valid-session"; } const retryDelayMs = getConsumerTokenRetryDelay(attempt); if (debugEntry) { debugEntry.metadata.attempt = String(attempt); debugEntry.metadata.retryReason = retryReason; debugEntry.metadata.nextRetryDelayMs = String(retryDelayMs); } await wait(retryDelayMs); } } throw new Error("requestConsumerTokenService retry loop ended unexpectedly."); } finally { if (sessionTab && Number.isInteger(sessionTab.id)) { const debugEntry = debugRequests[debugRequests.length - 1]; try { await tabsRemove(sessionTab.id); if (debugEntry) { debugEntry.metadata.sessionBootstrapState = receivedValidJson ? "closed-after-valid-json" : "closed-after-retry-finished"; } } catch (error) { if (debugEntry) { debugEntry.metadata.sessionBootstrapState = `tab-close-failed: ${error.message || "unknown error"}`; } } } } } function getConsumerTokenRetryDelay(attempt) { return Math.min( CUSTOMER_TOKEN_RETRY_BASE_DELAY_MS * (2 ** Math.max(attempt - 1, 0)), CUSTOMER_TOKEN_RETRY_MAX_DELAY_MS ); } async function openConsumerTokenSessionTabWithRetry(sender, debugEntry) { let attempt = 0; // Opening failures must not consume the token service's five attempts. while (true) { attempt += 1; if (debugEntry) { debugEntry.metadata.sessionBootstrapUrl = CUSTOMER_TOKEN_SESSION_URL; debugEntry.metadata.sessionTabOpenAttempt = String(attempt); } try { const tab = await openConsumerTokenSessionTab(sender); if (debugEntry) { debugEntry.metadata.sessionBootstrapState = "opened-inactive-tab"; delete debugEntry.metadata.sessionTabNextRetryDelayMs; } return tab; } catch (error) { const retryDelayMs = getConsumerTokenRetryDelay(Math.min(attempt, 5)); if (debugEntry) { debugEntry.metadata.sessionBootstrapState = "retrying-tab-open"; debugEntry.metadata.sessionTabLastOpenError = error.message || "unknown error"; debugEntry.metadata.sessionTabNextRetryDelayMs = String(retryDelayMs); } await wait(retryDelayMs); } } } function openConsumerTokenSessionTab(sender) { const details = { url: CUSTOMER_TOKEN_SESSION_URL, active: false }; if (sender && sender.tab && Number.isInteger(sender.tab.windowId)) { details.windowId = sender.tab.windowId; } if (supportsFirefoxCookieStoreTabs() && sender && sender.tab && sender.tab.cookieStoreId) { details.cookieStoreId = sender.tab.cookieStoreId; } return tabsCreate(details); } return { requestConsumerTokenService, fetchConsumerTokenServiceUntilJson, getConsumerTokenRetryDelay, openConsumerTokenSessionTabWithRetry, openConsumerTokenSessionTab }; }