wgc
Version:
The official CLI tool to manage the GraphQL Federation Platform Cosmo
140 lines • 6.17 kB
JavaScript
import { program } from 'commander';
import pc from 'picocolors';
import { config } from '../../core/config.js';
import { readConfigFile, updateConfigFile } from '../../utils.js';
export const performDeviceAuth = async () => {
const headers = new Headers();
headers.append('Content-Type', 'application/x-www-form-urlencoded');
const requestBody = new URLSearchParams();
requestBody.append('scope', 'openid offline_access');
requestBody.append('client_id', config.kcClientId);
const response = await fetch(`${config.kcApiURL}/realms/${config.kcRealm}/protocol/openid-connect/auth/device`, {
method: 'POST',
headers,
body: requestBody,
});
if (response.status !== 200) {
return {
success: false,
errorMessage: 'Could not perform device authentication.',
response: {
deviceCode: '',
userCode: '',
verificationURI: '',
interval: 0,
},
};
}
const body = await response.json();
return {
success: true,
response: {
deviceCode: body.device_code,
userCode: body.user_code,
verificationURI: body.verification_uri_complete,
interval: body.interval,
},
};
};
export const startPollingForAccessToken = async ({ deviceCode, interval, }) => {
const headers = new Headers();
headers.append('Content-Type', 'application/x-www-form-urlencoded');
const requestBody = new URLSearchParams();
requestBody.append('client_id', config.kcClientId);
requestBody.append('grant_type', 'urn:ietf:params:oauth:grant-type:device_code');
requestBody.append('device_code', deviceCode);
// Request an offline token https://wjw465150.gitbooks.io/keycloak-documentation/content/server_admin/topics/sessions/offline.html
requestBody.append('scope', 'openid offline_access');
while (true) {
const response = await fetch(`${config.kcApiURL}/realms/${config.kcRealm}/protocol/openid-connect/token`, {
method: 'POST',
headers,
body: requestBody,
});
if (response.status === 400) {
// Sleep for the retry interval and print a dot for each second.
for (let i = 0; i < interval; i++) {
await new Promise((resolve) => setTimeout(resolve, 1000));
}
continue;
}
if (response.status !== 200) {
return {
success: false,
errorMessage: 'Could not fetch the access token.',
};
}
const body = await response.json();
const present = new Date();
return {
success: true,
response: {
accessToken: body.access_token,
refreshToken: body.refresh_token,
expiresAt: new Date(new Date().setSeconds(present.getSeconds() + body.expires_in)),
refreshExpiresAt: new Date(new Date().setSeconds(present.getSeconds() + body.refresh_expires_in)),
},
};
}
};
// checks if either of access token or api key are present
// if not, it will try to refresh the access token
export async function checkAuth() {
const userConfig = readConfigFile();
if (config.apiKey && userConfig.accessToken) {
console.error(`${pc.yellow('Warning')} ${pc.dim('Both COSMO_API_KEY and login credentials found. Environment variable has precedence.\n')}`);
}
// API Key is present and assumed to be valid
if (config.apiKey) {
return;
}
if (!userConfig.organizationSlug) {
program.error(pc.red('Organization slug is not set. Please run `wgc auth login` to set the organization slug.'));
}
// Access token is present and does not expire in the next 60 seconds
if ((userConfig === null || userConfig === void 0 ? void 0 : userConfig.accessToken) &&
(userConfig === null || userConfig === void 0 ? void 0 : userConfig.expiresAt) &&
new Date(userConfig.expiresAt) > new Date(Date.now() - 60 * 1000)) {
// Update the api key to the current valid access token
config.apiKey = userConfig.accessToken;
return;
}
// Check if refresh token is expired
if ((userConfig === null || userConfig === void 0 ? void 0 : userConfig.refreshToken) && (userConfig === null || userConfig === void 0 ? void 0 : userConfig.refreshExpiresAt) && new Date(userConfig.refreshExpiresAt) < new Date()) {
program.error(pc.red('Refresh token has expired. Please login again with `wgc auth login`'));
}
// Refresh tokens with the offline token
const resp = await fetch(`${config.kcApiURL}/realms/${config.kcRealm}/protocol/openid-connect/token`, {
method: 'POST',
headers: {
'Content-Type': 'application/x-www-form-urlencoded',
},
body: new URLSearchParams({
grant_type: 'refresh_token',
client_id: config.kcClientId,
refresh_token: userConfig.refreshToken,
}),
});
if (resp.status !== 200) {
throw new Error('Failed to refresh access token. If the issue persists, please contact support. StatusCode: ' + resp.status);
}
try {
const data = await resp.json();
const present = new Date();
// Update the config file with the new access token and refresh token
updateConfigFile({
accessToken: data.access_token,
refreshToken: data.refresh_token,
expiresAt: new Date(new Date().setSeconds(present.getSeconds() + data.expires_in)),
refreshExpiresAt: new Date(new Date().setSeconds(present.getSeconds() + data.refresh_expires_in)),
organizationSlug: userConfig.organizationSlug,
});
// Update the api key with the new access token
config.apiKey = data.access_token;
}
catch (e) {
throw new Error('Failed to parse the response from the identity server. If the issue persists, please contact support. Error: ' +
e.toString());
}
}
//# sourceMappingURL=utils.js.map