@targetprocess/tokiny
Version:
Client for easy retrieving access_tokens from Targetprocess Auth service.
215 lines • 8.49 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(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