@coko/server
Version:
Reusable server for use by Coko's projects
192 lines • 8.44 kB
JavaScript
;
var __importDefault = (this && this.__importDefault) || function (mod) {
return (mod && mod.__esModule) ? mod : { "default": mod };
};
Object.defineProperty(exports, "__esModule", { value: true });
exports.renewAuthTokens = exports.getAccessToken = exports.getAuthTokens = void 0;
const get_1 = __importDefault(require("lodash/get"));
const axios_1 = __importDefault(require("axios"));
const dayjs_1 = __importDefault(require("dayjs"));
const utc_1 = __importDefault(require("dayjs/plugin/utc"));
const config_1 = __importDefault(require("../configManager/config"));
const pubsub_1 = __importDefault(require("../graphql/pubsub"));
const time_1 = require("./time");
const constants_1 = require("../models/user/constants");
const identity_model_1 = __importDefault(require("../models/identity/identity.model"));
const serviceCredential_model_1 = __importDefault(require("../models/serviceCredential/serviceCredential.model"));
const user_controller_1 = require("../models/user/user.controller");
const identity_controller_1 = require("../models/identity/identity.controller");
const number_1 = require("./number");
dayjs_1.default.extend(utc_1.default);
const { USER_UPDATED } = constants_1.subscriptions;
const getAuthTokens = async (userId, providerLabel) => {
return requestTokensFromProvider(userId, providerLabel, {
checkAccessToken: true,
returnAccessToken: true,
});
};
exports.getAuthTokens = getAuthTokens;
const requestTokensFromProvider = async (userId, providerLabel, options = {}) => {
const { checkAccessToken, returnAccessToken } = options;
const providerUserIdentity = await identity_model_1.default.findOne({
userId,
provider: providerLabel,
});
if (!providerUserIdentity) {
throw new Error(`identity for provider ${providerLabel} does not exist`);
}
const { oauthAccessToken, oauthAccessTokenExpiration, oauthRefreshToken, oauthRefreshTokenExpiration, } = providerUserIdentity;
const UTCNowTimestamp = (0, dayjs_1.default)().utc().valueOf();
if (checkAccessToken) {
const accessTokenExpired = oauthAccessTokenExpiration.getTime() < UTCNowTimestamp;
if (!accessTokenExpired) {
return oauthAccessToken;
}
}
const refreshTokenExpired = oauthRefreshTokenExpiration.getTime() < UTCNowTimestamp;
if (refreshTokenExpired) {
const updatedUser = await (0, user_controller_1.getUser)(userId);
pubsub_1.default.publish(USER_UPDATED, {
userUpdated: updatedUser,
});
// logger.error(
// `refresh token for provider ${providerLabel} expired, authorization flow should (provider login) be followed by the user`,
// )
// return false
throw new Error(`refresh token for provider ${providerLabel} expired, authorization flow should (provider login) be followed by the user`);
}
const integrations = config_1.default.has('integrations') && config_1.default.get('integrations');
if (!integrations) {
throw new Error('Integrations are undefined in config');
}
const externalProvider = integrations[providerLabel];
if (!externalProvider) {
throw new Error(`Integration ${providerLabel} configuration is undefined `);
}
const { tokenUrl, clientId } = integrations[providerLabel];
if (!tokenUrl) {
throw new Error(`Integration ${providerLabel} tokenUrl is undefined `);
}
if (!clientId) {
throw new Error(`Integration ${providerLabel} clientId is undefined `);
}
const tokenData = new URLSearchParams({
grant_type: 'refresh_token',
refresh_token: oauthRefreshToken,
client_id: clientId,
});
const { data, status } = await (0, axios_1.default)({
method: 'post',
url: tokenUrl,
headers: { 'Content-Type': 'application/x-www-form-urlencoded' },
data: tokenData.toString(),
});
if (status === 401) {
// for the case that something happened and refreshToken become invalid -> set that expired
await (0, identity_controller_1.invalidateProviderTokens)(userId, providerLabel);
}
/* eslint-disable camelcase */
const { access_token, expires_in, refresh_token, refresh_expires_in } = data;
if (!access_token) {
throw new Error('Missing access_token from response!');
}
if ((0, number_1.isValidPositiveIntegerOrZero)(expires_in)) {
throw new Error('Missing expires_in from response!');
}
if (!refresh_token) {
throw new Error('Missing refresh_token from response!');
}
if ((0, number_1.isValidPositiveIntegerOrZero)(refresh_expires_in)) {
throw new Error('Missing refresh_expires_in from response!');
}
await identity_model_1.default.patchAndFetchById(providerUserIdentity.id, {
oauthAccessToken: access_token,
oauthRefreshToken: refresh_token,
oauthAccessTokenExpiration: expires_in === 0 ? time_1.foreverDate : (0, time_1.getExpirationTime)(expires_in),
oauthRefreshTokenExpiration: refresh_expires_in === 0
? time_1.foreverDate
: (0, time_1.getExpirationTime)(refresh_expires_in),
});
if (returnAccessToken) {
return access_token;
}
return true;
};
const renewAuthTokens = async (userId, providerLabel) => requestTokensFromProvider(userId, providerLabel, { checkAccessToken: false });
exports.renewAuthTokens = renewAuthTokens;
const getAccessToken = async (serviceName, renew = false) => {
try {
const services = config_1.default.has('services') && config_1.default.get('services');
if (!services) {
throw new Error('services are undefined');
}
const service = (0, get_1.default)(services, `${serviceName}`);
if (!service) {
throw new Error(`service ${serviceName} configuration is undefined `);
}
const { clientId, clientSecret, url } = service;
if (!clientId) {
throw new Error(`service ${serviceName} clientId is undefined `);
}
if (!clientSecret) {
throw new Error(`service ${serviceName} clientSecret is undefined `);
}
if (!url) {
throw new Error(`service ${serviceName} url is undefined `);
}
const buff = Buffer.from(`${clientId}:${clientSecret}`, 'utf8');
const base64data = buff.toString('base64');
const serviceHealthCheck = await (0, axios_1.default)({
method: 'get',
url: `${url}/healthcheck`,
});
const { data: healthCheckData } = serviceHealthCheck;
const { message } = healthCheckData;
if (message !== 'Coolio') {
throw new Error(`service ${serviceName} is down`);
}
const foundServiceCredential = await serviceCredential_model_1.default.findOne({
name: serviceName,
});
if (!foundServiceCredential) {
const { data } = await (0, axios_1.default)({
method: 'post',
url: `${url}/api/auth`,
headers: { authorization: `Basic ${base64data}` },
});
const { accessToken } = data;
await serviceCredential_model_1.default.insert({
name: serviceName,
accessToken,
});
return accessToken;
}
const { accessToken, id } = foundServiceCredential;
if (!accessToken || renew) {
const { data } = await (0, axios_1.default)({
method: 'post',
url: `${url}/api/auth`,
headers: { authorization: `Basic ${base64data}` },
});
const { accessToken: freshAccessToken } = data;
await serviceCredential_model_1.default.patchAndFetchById(id, {
accessToken: freshAccessToken,
});
return freshAccessToken;
}
return accessToken;
}
catch (e) {
const foundServiceCredential = await serviceCredential_model_1.default.findOne({
name: serviceName,
});
if (foundServiceCredential) {
await serviceCredential_model_1.default.patchAndFetchById(foundServiceCredential.id, {
accessToken: null,
});
}
throw e;
}
};
exports.getAccessToken = getAccessToken;
//# sourceMappingURL=tokens.js.map