microservice-utilities
Version:
Utilities supporting authorization, request logging and other often reused parts of microservices.
73 lines (67 loc) • 2.78 kB
JavaScript
const jwtManager = require('jsonwebtoken');
class ServiceTokenProvider {
/**
* Constructor
* @param {Object} httpClient used to perform http calls
* @param {Object} kmsClient used to decrypt the encrypted secret
* @param {Object} configuration configuration object
* @param {String} configuration.clientId client ID
* @param {String} configuration.encryptedClientSecret base64 encoded encrypted secret
* @param {String} configuration.audience jwt audience
* @param {String} configuration.tokenEndpoint endpoint to resolve token from
*/
constructor(httpClient, kmsClient, configuration = {}) {
this.httpClient = httpClient;
this.kmsClient = kmsClient;
if (!configuration.clientId) {
throw new Error('Configuration error: missing required property "clientId"');
}
if (!configuration.encryptedClientSecret) {
throw new Error('Configuration error: missing required property "encryptedClientSecret"');
}
if (!configuration.audience) {
throw new Error('Configuration error: missing required property "audience"');
}
if (!configuration.tokenEndpoint) {
throw new Error('Configuration error: missing required property "tokenEndpoint"');
}
this.configuration = configuration;
this.currentTokenPromise = null;
}
/**
* Get access token. Subsequent calls are cached and the token is renewed only if it is expired.
* @returns {Promise<String>} access token
*/
async getToken() {
if (!this.currentTokenPromise) {
return (this.currentTokenPromise = this.getTokenWithoutCache());
}
try {
let jwtToken = await this.currentTokenPromise;
// lower the token expiry time by 10s so that the returned token will be not immediately expired
if (!jwtToken || jwtManager.decode(jwtToken).exp < (Date.now() / 1000) - 10) {
this.currentTokenPromise = this.getTokenWithoutCache();
}
return this.currentTokenPromise;
} catch (error) {
return (this.currentTokenPromise = this.getTokenWithoutCache());
}
}
/**
* Get access token.
* @returns {Promise<String>} access token
*/
async getTokenWithoutCache() {
let secret = await this.kmsClient.decrypt({ CiphertextBlob: Buffer.from(this.configuration.encryptedClientSecret, 'base64') }).promise().then(data => data.Plaintext.toString());
let headers = { 'Content-Type': 'application/json' };
let body = {
client_id: this.configuration.clientId,
client_secret: secret,
audience: this.configuration.audience,
grant_type: 'client_credentials'
};
let response = await this.httpClient.post(this.configuration.tokenEndpoint, body, headers);
return response.data.access_token;
}
}
module.exports = ServiceTokenProvider;