UNPKG

@sap/cli-core

Version:

Command-Line Interface (CLI) Core Module

111 lines (110 loc) 4.93 kB
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; } };