This commit is contained in:
2025-01-04 00:34:03 +01:00
parent 41829408dc
commit 0ca14bbc19
18111 changed files with 1871397 additions and 0 deletions

View File

@@ -0,0 +1,2 @@
export const EXPIRE_WINDOW_MS = 5 * 60 * 1000;
export const REFRESH_MESSAGE = `To refresh this SSO session run 'aws sso login' with the corresponding profile.`;

View File

@@ -0,0 +1,79 @@
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;
}
};

View File

@@ -0,0 +1,8 @@
import { TokenProviderError } from "@smithy/property-provider";
export const fromStatic = ({ token, logger }) => async () => {
logger?.debug("@aws-sdk/token-providers", "fromStatic");
if (!token || !token.token) {
throw new TokenProviderError(`Please pass a valid token to fromStatic`, false);
}
return token;
};

View File

@@ -0,0 +1,11 @@
import { getSsoOidcClient } from "./getSsoOidcClient";
export const getNewSsoOidcToken = async (ssoToken, ssoRegion) => {
const { CreateTokenCommand } = await import("@aws-sdk/client-sso-oidc");
const ssoOidcClient = await getSsoOidcClient(ssoRegion);
return ssoOidcClient.send(new CreateTokenCommand({
clientId: ssoToken.clientId,
clientSecret: ssoToken.clientSecret,
refreshToken: ssoToken.refreshToken,
grantType: "refresh_token",
}));
};

View File

@@ -0,0 +1,10 @@
const ssoOidcClientsHash = {};
export const getSsoOidcClient = async (ssoRegion) => {
const { SSOOIDCClient } = await import("@aws-sdk/client-sso-oidc");
if (ssoOidcClientsHash[ssoRegion]) {
return ssoOidcClientsHash[ssoRegion];
}
const ssoOidcClient = new SSOOIDCClient({ region: ssoRegion });
ssoOidcClientsHash[ssoRegion] = ssoOidcClient;
return ssoOidcClient;
};

View File

@@ -0,0 +1,3 @@
export * from "./fromSso";
export * from "./fromStatic";
export * from "./nodeProvider";

View File

@@ -0,0 +1,5 @@
import { chain, memoize, TokenProviderError } from "@smithy/property-provider";
import { fromSso } from "./fromSso";
export const nodeProvider = (init = {}) => memoize(chain(fromSso(init), async () => {
throw new TokenProviderError("Could not load token from any providers", false);
}), (token) => token.expiration !== undefined && token.expiration.getTime() - Date.now() < 300000, (token) => token.expiration !== undefined);

View File

@@ -0,0 +1,7 @@
import { TokenProviderError } from "@smithy/property-provider";
import { REFRESH_MESSAGE } from "./constants";
export const validateTokenExpiry = (token) => {
if (token.expiration && token.expiration.getTime() < Date.now()) {
throw new TokenProviderError(`Token is expired. ${REFRESH_MESSAGE}`, false);
}
};

View File

@@ -0,0 +1,7 @@
import { TokenProviderError } from "@smithy/property-provider";
import { REFRESH_MESSAGE } from "./constants";
export const validateTokenKey = (key, value, forRefresh = false) => {
if (typeof value === "undefined") {
throw new TokenProviderError(`Value not present for '${key}' in SSO Token${forRefresh ? ". Cannot refresh" : ""}. ${REFRESH_MESSAGE}`, false);
}
};

View File

@@ -0,0 +1,8 @@
import { getSSOTokenFilepath } from "@smithy/shared-ini-file-loader";
import { promises as fsPromises } from "fs";
const { writeFile } = fsPromises;
export const writeSSOTokenToFile = (id, ssoToken) => {
const tokenFilepath = getSSOTokenFilepath(id);
const tokenString = JSON.stringify(ssoToken, null, 2);
return writeFile(tokenFilepath, tokenString);
};