@redocly/cli
Version:
[@Redocly](https://redocly.com) CLI is your all-in-one OpenAPI utility. It builds, manages, improves, and quality-checks your OpenAPI descriptions, all of which comes in handy for various phases of the API Lifecycle. Create your own rulesets to make API g
134 lines (133 loc) • 5.25 kB
JavaScript
;
Object.defineProperty(exports, "__esModule", { value: true });
exports.RedoclyOAuthDeviceFlow = void 0;
const colorette_1 = require("colorette");
const childProcess = require("child_process");
const api_client_1 = require("../reunite/api/api-client");
class RedoclyOAuthDeviceFlow {
constructor(baseUrl, clientName, version) {
this.baseUrl = baseUrl;
this.clientName = clientName;
this.version = version;
this.apiClient = new api_client_1.ReuniteApiClient(this.version, 'login');
}
async run() {
const code = await this.getDeviceCode();
process.stdout.write('Attempting to automatically open the SSO authorization page in your default browser.\n');
process.stdout.write('If the browser does not open or you wish to use a different device to authorize this request, open the following URL:\n\n');
process.stdout.write((0, colorette_1.blue)(code.verificationUri));
process.stdout.write(`\n\n`);
process.stdout.write(`Then enter the code:\n\n`);
process.stdout.write((0, colorette_1.blue)(code.userCode));
process.stdout.write(`\n\n`);
this.openBrowser(code.verificationUriComplete);
const accessToken = await this.pollingAccessToken(code.deviceCode, code.interval, code.expiresIn);
process.stdout.write((0, colorette_1.green)('✅ Logged in\n\n'));
return accessToken;
}
openBrowser(url) {
try {
const cmd = process.platform === 'win32'
? `start ${url}`
: process.platform === 'darwin'
? `open ${url}`
: `xdg-open ${url}`;
childProcess.execSync(cmd);
}
catch {
// silently fail if browser cannot be opened
}
}
async verifyToken(accessToken) {
try {
const response = await this.sendRequest('/session', 'GET', undefined, {
Cookie: `accessToken=${accessToken};`,
});
return !!response.user;
}
catch {
return false;
}
}
async verifyApiKey(apiKey) {
try {
const response = await this.sendRequest('/api-keys-verify', 'POST', {
apiKey,
});
return !!response.success;
}
catch {
return false;
}
}
async refreshToken(refreshToken) {
const response = await this.sendRequest(`/device-rotate-token`, 'POST', {
grant_type: 'refresh_token',
client_name: this.clientName,
refresh_token: refreshToken,
});
if (!response.access_token) {
throw new Error('Failed to refresh token');
}
return {
access_token: response.access_token,
refresh_token: response.refresh_token,
expires_in: response.expires_in,
};
}
async pollingAccessToken(deviceCode, interval, expiresIn) {
return new Promise((resolve, reject) => {
const intervalId = setInterval(async () => {
const response = await this.getAccessToken(deviceCode);
if (response.access_token) {
clearInterval(intervalId);
clearTimeout(timeoutId);
resolve(response);
}
if (response.error && response.error !== 'authorization_pending') {
clearInterval(intervalId);
clearTimeout(timeoutId);
reject(response.error_description);
}
}, interval * 1000);
const timeoutId = setTimeout(async () => {
clearInterval(intervalId);
clearTimeout(timeoutId);
reject('Authorization has expired. Please try again.');
}, expiresIn * 1000);
});
}
async getAccessToken(deviceCode) {
return await this.sendRequest('/device-token', 'POST', {
client_name: this.clientName,
device_code: deviceCode,
grant_type: 'urn:ietf:params:oauth:grant-type:device_code',
});
}
async getDeviceCode() {
const { device_code: deviceCode, user_code: userCode, verification_uri: verificationUri, verification_uri_complete: verificationUriComplete, interval = 10, expires_in: expiresIn = 300, } = await this.sendRequest('/device-authorize', 'POST', {
client_name: this.clientName,
});
return {
deviceCode,
userCode,
verificationUri,
verificationUriComplete,
interval,
expiresIn,
};
}
async sendRequest(url, method = 'GET', body = undefined, headers = {}) {
url = `${this.baseUrl}${url}`;
const response = await this.apiClient.request(url, {
body: body ? JSON.stringify(body) : body,
method,
headers: { 'Content-Type': 'application/json', ...headers },
});
if (response.status === 204) {
return { success: true };
}
return await response.json();
}
}
exports.RedoclyOAuthDeviceFlow = RedoclyOAuthDeviceFlow;