search-client
Version:
Javascript library for executing searches in the Haive search-index via the SearchManager REST interface.
177 lines • 8.29 kB
JavaScript
import { __awaiter, __extends, __generator } from "tslib";
import * as jwt from 'jwt-simple';
import { BaseCall, Query } from '../Common';
import { AuthenticationSettings, } from './AuthenticationSettings';
import { AuthToken } from './AuthToken';
/**
* The JwtAuthentication service is a supporting feature for the other services.
* Typically used via the [[SearchClient.constructor]] and by providing [[AuthenticationSettings]] settings in
* the [[Settings.authentication]] property.
*
* The authentication system is based on JWT and needs an end-point to be configured from where it will get its
* authentication-token. This service will be monitoring the token-value to see if it is either missing or
* expired. When that happens a new token will be fetched from the end-point. The [[AuthenticationSettings.expiryOverlap]]
* object controls how long before expiration the new token is to be fetched.
*/
var JwtAuthentication = /** @class */ (function (_super) {
__extends(JwtAuthentication, _super);
/**
* Creates an JwtAuthentication object that knows where to get the auth-token and when to refresh it.
* @param settings - The settings for the authentication object.
* @param auth - An object that controls the authentication for the lookups.
*/
function JwtAuthentication(settings, auth, fetchMethod) {
var _this = _super.call(this) // dummy
|| this;
// prepare for super.init
if (typeof settings === 'string') {
settings = {
baseUrl: settings,
type: 'jwt',
};
}
else {
settings.type = 'jwt';
}
settings = new AuthenticationSettings(settings);
auth = auth || new AuthToken();
_super.prototype.init.call(_this, settings, auth, fetchMethod);
// Set own this props
if (settings.token) {
// this.auth.authenticationToken = settings.token;
var token_1 = settings.token;
_this.auth.tokenResolver = function () { return token_1; };
settings.token = undefined;
_this.setupRefresh();
}
else if (settings.enabled) {
// We authenticate immediately in order to have the token in place when the first calls come in.
_this.update(null);
}
return _this;
}
/**
* Call the service, but take into account deferredUpdates.
*
* @param query The query object to create the fetch for.
* @param delay A delay for when to execute the update, in milliseconds. Defaults to undefined.
*/
JwtAuthentication.prototype.update = function (query, delay) {
var _this = this;
if (this.deferUpdate) {
// Save the query, so that when the deferUpdate is again false we can then execute it.
this.deferredQuery = query;
}
else {
// In case this action is triggered when a delayed execution is already pending, clear that pending timeout.
clearTimeout(this.delay);
if (delay > 0) {
// Set up the delay
this.delay = setTimeout(function () {
var fetchPromise = _this.fetch(query);
if (fetchPromise) {
fetchPromise.catch(function (error) { return Promise.resolve(null); });
}
}, delay);
}
else {
var fetchPromise = this.fetch(query);
if (fetchPromise) {
fetchPromise.catch(function (error) { return Promise.resolve(null); });
}
}
}
};
/**
* Fetches the authentication-token from the server.
* @param query - For the Authentication service this parameter is ignored.
* @param suppressCallbacks - Set to true if you have defined callbacks, but somehow don't want them to be called.
* @returns a promise that when resolved returns a jwt token.
*/
JwtAuthentication.prototype.fetchInternal = function (query, suppressCallbacks) {
if (query === void 0) { query = new Query(); }
if (suppressCallbacks === void 0) { suppressCallbacks = false; }
return __awaiter(this, void 0, void 0, function () {
var reqInit, err, response, data_1, _i, _a, i, error_1;
return __generator(this, function (_b) {
switch (_b.label) {
case 0:
reqInit = this.requestObject(false);
_b.label = 1;
case 1:
_b.trys.push([1, 4, , 5]);
if (!this.cbRequest(suppressCallbacks, this.settings.url, reqInit)) {
err = new Error();
err.name = 'cbRequestCancelled';
throw err;
}
return [4 /*yield*/, this.fetchMethod(this.settings.url, reqInit)];
case 2:
response = _b.sent();
if (!response.ok) {
throw Error(response.status + " " + response.statusText + " for request url '" + this.settings.url + "'");
}
return [4 /*yield*/, response.json()
// Find the auth token by using the settings for where it is in the structure.
];
case 3:
data_1 = _b.sent();
// Find the auth token by using the settings for where it is in the structure.
for (_i = 0, _a = this.settings.tokenPath; _i < _a.length; _i++) {
i = _a[_i];
data_1 = data_1[i];
}
// Update the token
this.auth.tokenResolver = function () { return data_1; };
// Set up a timer for refreshing the token before/if it expires.
this.setupRefresh();
this.cbSuccess(suppressCallbacks, this.auth.authenticationToken, this.settings.url, reqInit);
return [2 /*return*/, this.auth.authenticationToken];
case 4:
error_1 = _b.sent();
if (error_1.name !== 'AbortError') {
this.cbError(suppressCallbacks, error_1, this.settings.url, reqInit);
}
throw error_1;
case 5: return [2 /*return*/];
}
});
});
};
JwtAuthentication.prototype.setupRefresh = function () {
var _this = this;
try {
if (this.auth && this.auth.authenticationToken) {
var token = jwt.decode(this.auth.authenticationToken, null, true);
var expiration = token.exp
? new Date(token.exp * 1000)
: undefined;
if (expiration) {
var remainingSeconds = (expiration.valueOf() - new Date().valueOf()) / 1000;
remainingSeconds = Math.max(remainingSeconds - this.settings.triggers.expiryOverlap, 0);
// console.log(
// `Setting up JWT-token to refresh in ${remainingSeconds} seconds, at ${expiration}.`,
// "Token:",
// token
// );
setTimeout(function () {
_this.update(null);
}, remainingSeconds * 1000);
}
else {
// console.log(
// "The received JWT token does not expire.",
// "Token:",
// token
// );
}
}
}
catch (e) {
console.error("Unable to parse the provided token '" + this.auth.authenticationToken + "': " + e);
}
};
return JwtAuthentication;
}(BaseCall));
export { JwtAuthentication };
//# sourceMappingURL=JwtAuthentication.js.map