UNPKG

@coko/server

Version:

Reusable server for use by Coko's projects

192 lines 8.44 kB
"use strict"; 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