@targetprocess/tokiny
Version:
Client for easy retrieving access_tokens from Targetprocess Auth service.
218 lines • 8.87 kB
JavaScript
"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(body) {
if (!this.authOptions.useCache) {
return null;
}
const token = this.cache[this.getTokenKey(body)];
if (token && token.expires_at - Date.now() / 1000 > this.authOptions.expirationLimitSeconds * 10) {
return token;
}
return null;
}
getTokenKey(body) {
return `${this.authOptions.clientId}_${body.scope}_${body.grant_type}_${body.sub}_${body.email}_${body.workspace}`;
}
addToCache(body, token) {
if (this.authOptions.useCache) {
this.cache[this.getTokenKey(body)] = token;
}
}
getTokenFromAuthWithRetries(body, refreshToken) {
return __awaiter(this, void 0, void 0, function* () {
try {
const getTokenPromise = this.authOptions.retryOptions.retry
? retry((_, attempt) => this.getTokenFromAuth(body, attempt, refreshToken), this.authOptions.retryOptions)
: yield this.getTokenFromAuth(body, 1, refreshToken);
return yield getTokenPromise;
}
catch (error) {
this.logError('Failed to retrieve auth token.', error);
throw error;
}
});
}
getTokenFromAuth(body, attempt, refreshToken) {
return __awaiter(this, void 0, void 0, function* () {
const tokenKey = this.getTokenKey(body);
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(body, attempt, refreshToken);
}
finally {
this.tokenRetrievingStatus[tokenKey] = null;
}
}))());
});
}
retrieveToken(body, attempt, refreshToken) {
return __awaiter(this, void 0, void 0, function* () {
try {
const client = yield this.createOpenIdClient(this.authOptions);
this.logDebug('Retrieving auth token ...');
const requestBody = Object.assign({ grant_type: body.grant_type || this.authOptions.grantType || 'client_credentials' }, body);
const { access_token, expires_at, token_type, refresh_token } = refreshToken
? yield client.refresh(refreshToken)
: yield client.grant(requestBody);
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(body, refreshToken) {
return __awaiter(this, void 0, void 0, function* () {
const requestBody = typeof body === 'string' || body instanceof String ? { scope: body } : body;
const token = this.tryGetFromCache(requestBody);
if (!!token) {
return token;
}
const newToken = yield this.getTokenFromAuthWithRetries(requestBody, refreshToken);
this.addToCache(requestBody, newToken);
return newToken;
});
}
removeTokenFromCache(body) {
const requestBody = typeof body === 'string' || body instanceof String ? { scope: body } : body;
delete this.cache[this.getTokenKey(requestBody)];
}
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