General source code refactoring
This commit is contained in:
222
src/auth/consumer-session.js
Normal file
222
src/auth/consumer-session.js
Normal file
@@ -0,0 +1,222 @@
|
||||
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
|
||||
},
|
||||
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 debugEntry = debugRequests[debugRequests.length - 1];
|
||||
|
||||
if (!isHtmlFallback || attempt >= CUSTOMER_TOKEN_MAX_ATTEMPTS) {
|
||||
if (debugEntry) {
|
||||
debugEntry.metadata.attempt = String(attempt);
|
||||
if (isHtmlFallback) {
|
||||
debugEntry.metadata.retryLimitReached = "true";
|
||||
debugEntry.error = `requestConsumerTokenService returned invalid JSON 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 = "HTTP 200 response did not contain valid JSON";
|
||||
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 };
|
||||
}
|
||||
Reference in New Issue
Block a user