UNPKG

@targetprocess/tokiny

Version:

Client for easy retrieving access_tokens from Targetprocess Auth service.

215 lines 8.49 kB
"use strict"; var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) { function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); } return new (P || (P = Promise))(function (resolve, reject) { function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } } function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } } function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); } step((generator = generator.apply(thisArg, _arguments || [])).next()); }); }; Object.defineProperty(exports, "__esModule", { value: true }); exports.doFetch = exports.userHttpClientFactory = exports.httpClientFactory = exports.createTokenFactory = exports.TokenFactory = void 0; const retry = require("async-retry"); const openId = require("openid-client"); const utils_1 = require("./utils"); class TokenFactory { constructor(authOptions) { this.authOptions = authOptions; this.cache = {}; this.tokenRetrievingStatus = {}; const initialOptions = { authUrl: '', clientId: '', clientSecret: '', expirationLimitSeconds: 120, httpRequestTimeout: 5000, useCache: true, retryOptions: { retry: false, retries: 3 }, loggerMessagePrefix: '[tokiny] ' }; this.authOptions = Object.assign(Object.assign({}, initialOptions), authOptions); } createOpenIdClient(authOptions) { return __awaiter(this, void 0, void 0, function* () { try { openId.custom.setHttpOptionsDefaults({ timeout: authOptions.httpRequestTimeout }); this.logDebug('Discovering OpenId issuer ...'); const issuer = yield (0, utils_1.createIssuer)(authOptions); this.logDebug('Creating OpenId client ...'); const client = new issuer.Client({ client_id: authOptions.clientId, client_secret: authOptions.clientSecret, token_endpoint_auth_method: authOptions.tokenEndpointAuthMethod }); client[openId.custom.http_options] = (url, options) => { const result = { cert: authOptions.cert, ca: authOptions.ca, key: authOptions.key }; return result; }; return client; } catch (error) { this.logWarn('Failed to create OpenId client.', error); throw error; } }); } tryGetFromCache(scope) { if (!this.authOptions.useCache) { return null; } const token = this.cache[this.getTokenKey(scope)]; if (token && token.expires_at - Date.now() / 1000 > this.authOptions.expirationLimitSeconds * 10) { return token; } return null; } getTokenKey(scopes) { return `${this.authOptions.clientId}_${scopes}`; } addToCache(scope, token) { if (this.authOptions.useCache) { this.cache[this.getTokenKey(scope)] = token; } } getTokenFromAuthWithRetries(scopes, refreshToken) { return __awaiter(this, void 0, void 0, function* () { try { const getTokenPromise = this.authOptions.retryOptions.retry ? retry((_, attempt) => this.getTokenFromAuth(scopes, attempt, refreshToken), this.authOptions.retryOptions) : yield this.getTokenFromAuth(scopes, 1, refreshToken); return yield getTokenPromise; } catch (error) { this.logError('Failed to retrieve auth token.', error); throw error; } }); } getTokenFromAuth(scopes, attempt, refreshToken) { return __awaiter(this, void 0, void 0, function* () { const tokenKey = this.getTokenKey(scopes); const gettingTokenPromise = this.tokenRetrievingStatus[tokenKey]; if (gettingTokenPromise) { return gettingTokenPromise; } return (this.tokenRetrievingStatus[tokenKey] = (() => __awaiter(this, void 0, void 0, function* () { try { return yield this.retrieveToken(scopes, attempt, refreshToken); } finally { this.tokenRetrievingStatus[tokenKey] = null; } }))()); }); } retrieveToken(scopes, attempt, refreshToken) { return __awaiter(this, void 0, void 0, function* () { try { const client = yield this.createOpenIdClient(this.authOptions); this.logDebug('Retrieving auth token ...'); const { access_token, expires_at, token_type, refresh_token } = refreshToken ? yield client.refresh(refreshToken) : yield client.grant({ grant_type: this.authOptions.grantType || 'client_credentials', scope: scopes }); return { access_token, expires_at, type: token_type, refresh_token }; } catch (error) { this.logWarn(`Failed attempt#${attempt} to retrieve auth token.`, error); throw error; } }); } getToken(scopes, refreshToken) { return __awaiter(this, void 0, void 0, function* () { const token = this.tryGetFromCache(scopes); if (!!token) { return token; } const newToken = yield this.getTokenFromAuthWithRetries(scopes, refreshToken); this.addToCache(scopes, newToken); return newToken; }); } logDebug(msg) { this.log('debug', msg); } logWarn(msg, err) { this.log('warn', msg, err); } logError(msg, err) { this.log('error', msg, err); } log(level, msg, err) { if (this.authOptions.logger) { this.authOptions.logger[level](`${this.authOptions.loggerMessagePrefix}${msg}`, err); } } } exports.TokenFactory = TokenFactory; function createTokenFactory(authOptions) { return new TokenFactory(authOptions); } exports.createTokenFactory = createTokenFactory; function httpClientFactory(scopes, tokenFactory, fetch) { return { fetch: (input, init) => __awaiter(this, void 0, void 0, function* () { const token = yield tokenFactory.getToken(scopes); const authorization = 'Bearer ' + token.access_token; return doFetch(authorization, fetch, input, init); }) }; } exports.httpClientFactory = httpClientFactory; function userHttpClientFactory(authorizationProvider, fetch) { return { fetch: (input, init) => __awaiter(this, void 0, void 0, function* () { let authorization; if (typeof authorizationProvider === 'string') { authorization = authorizationProvider; } else { authorization = yield authorizationProvider(); } return doFetch(authorization, fetch, input, init); }) }; } exports.userHttpClientFactory = userHttpClientFactory; function isHeadersLike(x) { return typeof x.append === 'function' && x.append.length === 2; } function doFetch(authorization, fetch, input, init = {}) { const requestInit = Object.assign({}, init); if (!requestInit.headers) { requestInit.headers = { Authorization: authorization }; } else { const headers = requestInit.headers; if (isHeadersLike(headers)) { headers.append('Authorization', authorization); } else if (Array.isArray(headers)) { headers.push(['Authorization', authorization]); } else { requestInit.headers = Object.assign(Object.assign({}, headers), { ['Authorization']: authorization }); } } return fetch(input, requestInit); } exports.doFetch = doFetch; //# sourceMappingURL=index.js.map