/* eslint-disable import/prefer-default-export */
import fetch from 'node-fetch';
import stringify from 'qs/lib/stringify';
import debugLib from 'debug';
import jwtDecode from 'jwt-decode';
import { OIDC_REQUEST_TIMEOUT } from './AuthenticationHelper/constants';
import serviceConfig from '../../app/config/service.configdefinitions';
import OidcDiscovery from './AuthenticationHelper/OidcDiscovery';
const debug = debugLib('SlimmingWorld:clientCredentialsManagement');
const EXPIRATION_TIME_LEEWAY = 10000;
function getOidcDiscovery(config) {
return new OidcDiscovery(
config,
serviceConfig.useServerAuthentication || serviceConfig.useOidcDiscovery,
);
}
/**
* To memoize the token for caching purposes
*/
class TokenCache {
constructor() {
this.token = null;
this.expiration = null;
this.credentials = null;
this.tokenRequest = null;
}
get currentToken() {
if (this.token === null || this.isExpired) {
return null;
}
return this.token;
}
get isExpired() {
return this.expiration * 1000 - Date.now() < EXPIRATION_TIME_LEEWAY;
}
get currentCredentials() {
return this.credentials;
}
}
const tokenCacheMap = {};
function getTokenCacheFor(subscriptionType) {
if (!tokenCacheMap[subscriptionType]) {
tokenCacheMap[subscriptionType] = new TokenCache();
}
return tokenCacheMap[subscriptionType];
}
async function requestClientCredentialsToken({
/* eslint-disable */
grant_type,
client_id,
client_secret,
configuration_endpoint,
/* eslint-enable */
subscriptionType,
}) {
const tokenCache = getTokenCacheFor(subscriptionType);
const oidcDiscovery = getOidcDiscovery({
client_id,
client_secret,
configuration_endpoint,
});
await oidcDiscovery.discoverComplete;
const res = await fetch(oidcDiscovery.providerConfig.token_endpoint, {
method: 'POST',
body: stringify({
grant_type: 'client_credentials',
client_id,
client_secret,
subscriptionType,
}),
headers: {
'Content-Type': 'application/x-www-form-urlencoded',
},
timeout: OIDC_REQUEST_TIMEOUT,
});
const { access_token: accessToken } = await res.json();
try {
const expireAt = jwtDecode(accessToken).exp;
tokenCache.token = accessToken;
tokenCache.expiration = expireAt;
/* eslint-disable-next-line camelcase */
tokenCache.credentials = `${client_id}_${client_secret}`;
return accessToken;
} catch (e) {
tokenCache.tokenRequest = null;
throw new Error('requestClientCredentialsToken accessToken undefined');
}
}
// eslint-disable-next-line import/prefer-default-export
export async function getClientCredentialsToken({
/* eslint-disable */
grant_type,
client_id,
client_secret,
configuration_endpoint,
/* eslint-enable */
subscriptionType,
}) {
const tokenCache = getTokenCacheFor(subscriptionType);
if (tokenCache.tokenRequest) {
debug('Client credential tokenRequest exist');
return tokenCache.tokenRequest;
}
if (
tokenCache.currentToken &&
/* eslint-disable-next-line camelcase */
tokenCache?.currentCredentials === `${client_id}_${client_secret}`
) {
debug('Client credential token exist');
return tokenCache.currentToken;
}
debug('Request client credential token');
tokenCache.tokenRequest = requestClientCredentialsToken({
/* eslint-disable */
grant_type,
client_id,
client_secret,
configuration_endpoint,
/* eslint-enable */
subscriptionType,
});
await tokenCache.tokenRequest;
tokenCache.tokenRequest = null;
return tokenCache.currentToken;
}