import { TokenProviderError } from "@smithy/property-provider"; import { getProfileName, getSSOTokenFromFile, loadSsoSessionData, parseKnownFiles, } from "@smithy/shared-ini-file-loader"; import { EXPIRE_WINDOW_MS, REFRESH_MESSAGE } from "./constants"; import { getNewSsoOidcToken } from "./getNewSsoOidcToken"; import { validateTokenExpiry } from "./validateTokenExpiry"; import { validateTokenKey } from "./validateTokenKey"; import { writeSSOTokenToFile } from "./writeSSOTokenToFile"; const lastRefreshAttemptTime = new Date(0); export const fromSso = (init = {}) => async () => { init.logger?.debug("@aws-sdk/token-providers", "fromSso"); const profiles = await parseKnownFiles(init); const profileName = getProfileName(init); const profile = profiles[profileName]; if (!profile) { throw new TokenProviderError(`Profile '${profileName}' could not be found in shared credentials file.`, false); } else if (!profile["sso_session"]) { throw new TokenProviderError(`Profile '${profileName}' is missing required property 'sso_session'.`); } const ssoSessionName = profile["sso_session"]; const ssoSessions = await loadSsoSessionData(init); const ssoSession = ssoSessions[ssoSessionName]; if (!ssoSession) { throw new TokenProviderError(`Sso session '${ssoSessionName}' could not be found in shared credentials file.`, false); } for (const ssoSessionRequiredKey of ["sso_start_url", "sso_region"]) { if (!ssoSession[ssoSessionRequiredKey]) { throw new TokenProviderError(`Sso session '${ssoSessionName}' is missing required property '${ssoSessionRequiredKey}'.`, false); } } const ssoStartUrl = ssoSession["sso_start_url"]; const ssoRegion = ssoSession["sso_region"]; let ssoToken; try { ssoToken = await getSSOTokenFromFile(ssoSessionName); } catch (e) { throw new TokenProviderError(`The SSO session token associated with profile=${profileName} was not found or is invalid. ${REFRESH_MESSAGE}`, false); } validateTokenKey("accessToken", ssoToken.accessToken); validateTokenKey("expiresAt", ssoToken.expiresAt); const { accessToken, expiresAt } = ssoToken; const existingToken = { token: accessToken, expiration: new Date(expiresAt) }; if (existingToken.expiration.getTime() - Date.now() > EXPIRE_WINDOW_MS) { return existingToken; } if (Date.now() - lastRefreshAttemptTime.getTime() < 30 * 1000) { validateTokenExpiry(existingToken); return existingToken; } validateTokenKey("clientId", ssoToken.clientId, true); validateTokenKey("clientSecret", ssoToken.clientSecret, true); validateTokenKey("refreshToken", ssoToken.refreshToken, true); try { lastRefreshAttemptTime.setTime(Date.now()); const newSsoOidcToken = await getNewSsoOidcToken(ssoToken, ssoRegion); validateTokenKey("accessToken", newSsoOidcToken.accessToken); validateTokenKey("expiresIn", newSsoOidcToken.expiresIn); const newTokenExpiration = new Date(Date.now() + newSsoOidcToken.expiresIn * 1000); try { await writeSSOTokenToFile(ssoSessionName, { ...ssoToken, accessToken: newSsoOidcToken.accessToken, expiresAt: newTokenExpiration.toISOString(), refreshToken: newSsoOidcToken.refreshToken, }); } catch (error) { } return { token: newSsoOidcToken.accessToken, expiration: newTokenExpiration, }; } catch (error) { validateTokenExpiry(existingToken); return existingToken; } };