@sap/cli-core
Version:
Command-Line Interface (CLI) Core Module
111 lines (110 loc) • 4.93 kB
JavaScript
import { createServer } from "http";
import fs from "fs-extra";
import { get } from "../../../../../logger/index.js";
import { isExpired, readToken } from "../utils.js";
import { updateAuthorization } from "./setAuthorization.js";
import { OPTION_CODE, PATH_TO_ERROR_HTML, PATH_TO_SUCCESS_HTML, } from "../../../../../constants.js";
import { getOptionValueFromConfig } from "../../../../../utils/options.js";
import { SecretsStorageSingleton } from "../../../../../cache/secrets/SecretsStorageSingleton.js";
import { openUrlInBrowser } from "../../../../../utils/openUtils.js";
import { logVerbose } from "../../../../../logger/utils.js";
import { GrantType } from "../../../../../types.js";
const getLogger = () => get("commands.handler.authentication.oauth.tokenProvider.utils.refreshToken");
export const refreshToken = async (forceRefresh = false) => {
const logger = getLogger();
const secrets = await SecretsStorageSingleton.SINGLETON.getDefaultSecret();
if ((!forceRefresh && !secrets.expires_after) ||
(secrets.authorization_flow === GrantType.authorization_code &&
!secrets.refresh_token)) {
logVerbose(logger, "Access token cannot be refreshed. Is the refresh token available?");
throw new Error("invalid secrets information");
}
logger.info("checking token expiry date");
if (forceRefresh || isExpired(secrets.expires_after)) {
logger.debug("access token is expired, refreshing token");
if (secrets.authorization_flow === GrantType.client_credentials) {
await readToken({ grant_type: secrets.authorization_flow });
}
else {
await readToken({
refresh_token: secrets.refresh_token,
grant_type: GrantType.refresh_token,
});
}
await updateAuthorization();
}
else {
logger.debug("access token is not expired");
}
};
export const retrieveCode = async () => new Promise((resolve, reject) => {
void (async () => {
const { debug, error, output } = getLogger();
try {
const secrets = await SecretsStorageSingleton.SINGLETON.getDefaultSecret();
const defaultPort = secrets.customClient ? "8080" : "65000";
const PORT = parseInt(process.env.CLI_HTTP_PORT ?? defaultPort, 10);
let timeout;
const server = createServer((req, res) => {
void (async () => {
clearTimeout(timeout);
const code = new URL(req.url
? `http://localhost:${PORT}${req.url}`
: "http://no-code.damn").searchParams.get("code");
let file;
try {
file = await fs.readFile(code ? PATH_TO_SUCCESS_HTML : PATH_TO_ERROR_HTML, "utf8");
}
catch (err) {
debug("failed to read file for code", code, err);
file =
"<html><body>Ops, something went wrong! Please try again.</body></html>";
}
res.writeHead(200, {
"Content-Type": "text/html",
"Content-Security-Policy": "frame-ancestors 'none'",
"X-Frame-Options": "DENY",
});
res.end(file, "utf8");
server.close();
if (code) {
debug(`code received: ${code}`);
resolve(code);
}
else {
const message = "no code found in callback URI";
error(message);
reject(new Error(message));
}
})();
});
timeout = setTimeout(() => {
server.close();
const message = `Did not receive a code within 30 seconds. Did you maintain the redirect URI for the OAuth client as http://localhost:${PORT} in SAP Datasphere?`;
output(message);
reject(new Error(message));
}, 30 * 1000);
server.listen(PORT);
debug(`started http server at localhost:${PORT}`);
}
catch (err) {
error("failed to instantiate server", err);
reject(new Error("failed to instantiate server", { cause: err }));
}
})();
});
export const getCode = async (authorizeUrl, clientId) => {
const { debug } = getLogger();
try {
return getOptionValueFromConfig(OPTION_CODE);
}
catch (err) {
debug("failed to retrieve code from options", err);
const code = retrieveCode();
void openUrlInBrowser(authorizeUrl, {
response_type: "code",
client_id: clientId,
});
return code;
}
};