@fullerstack/ngx-jwt
Version:
A simple JWT library for Angular applications
131 lines • 15.4 kB
JavaScript
/**
* @license
* Copyright Neekware Inc. All Rights Reserved.
*
* Use of this source code is governed by a proprietary notice
* that can be found at http://neekware.com/license/PRI.html
*/
/* eslint-disable @typescript-eslint/no-explicit-any */
import { Injectable } from '@angular/core';
import { ConfigService, DefaultApplicationConfig, } from '@fullerstack/ngx-config';
import { LoggerService } from '@fullerstack/ngx-logger';
import { Base64 } from 'js-base64';
import { cloneDeep as ldDeepClone, mergeWith as ldMergeWith } from 'lodash-es';
import { DefaultJwtConfig } from './jwt.default';
import * as i0 from "@angular/core";
import * as i1 from "@fullerstack/ngx-config";
import * as i2 from "@fullerstack/ngx-logger";
/**
* An injectable class that handles JWT service
*/
export class JwtService {
/**
* Class constructor
* @param options an optional configuration object
*/
constructor(config, logger) {
this.config = config;
this.logger = logger;
this.nameSpace = 'JWT';
this.options = DefaultApplicationConfig;
this.options = ldMergeWith(ldDeepClone({ jwt: DefaultJwtConfig }), this.config.options, (dest, src) => (Array.isArray(dest) ? src : undefined));
this.logger.info(`[${this.nameSpace}] JwtService ready ...`);
}
/**
* Gets the payload portion of a JWT token
* @param token JWT token (base64 encrypted)
* @returns a payload object or null if decode fails
*/
getPayload(token) {
let parts = [];
try {
parts = token.split('.');
if (parts.length !== 3) {
throw Error('JWT must have 3 parts');
}
}
catch (e) {
this.logger.error(e.message);
return undefined;
}
try {
const decoded = Base64.decode(parts[1]);
const payload = JSON.parse(decoded);
return payload;
}
catch (e) {
this.logger.error('Cannot decode the token');
}
return undefined;
}
/**
* Tells if a JWT is token is expired
* @param payload JWT payload object
* @return true if JWT is already expired, else false
*/
isExpired(payload) {
if (typeof payload === 'string') {
payload = this.getPayload(payload);
}
if (payload) {
const offset = (parseInt(payload.lee, 10) || this.options.jwt.expiryLeeway) * 1000;
const now = this.utcSeconds();
const expiry = this.utcSeconds(payload.exp);
const expired = now > expiry + offset;
return expired;
}
return true;
}
/**
* Calculates the next refresh time
* @param payload JWT payload object
* @param offset if true, a random time is added to the refresh time
* where networkDelay < random < leeway
* @returns total number of seconds till expiry or 0 if token is expired
*/
getRefreshTime(payload, offset = true) {
if (typeof payload === 'string') {
payload = this.getPayload(payload);
}
if (payload && !this.isExpired(payload)) {
const now = this.utcSeconds();
const expiry = this.utcSeconds(payload.exp);
const refresh = Math.floor((expiry - now) / 1000);
const random = this.getRandomOffset(payload);
const time = offset ? refresh + random : refresh;
return time;
}
return 0;
}
/**
* Calculates a random number where networkDelay < random < leeway
* @param payload JWT payload object
* @returns a random total number of seconds
*/
getRandomOffset(payload) {
if (typeof payload === 'string') {
payload = this.getPayload(payload);
}
const leeway = payload?.leeway || payload?.lee || this.options?.jwt?.expiryLeeway;
const range = {
lower: 1,
upper: leeway - this.options?.jwt?.networkDelay || 2,
};
return Math.floor(Math.random() * range.upper + range.lower);
}
/**
* Calculates the UTC value of date/time in seconds
* @param input date/time in seconds
* @returns UTC value of date/time in seconds
*/
utcSeconds(input) {
return input ? new Date(0).setUTCSeconds(input).valueOf() : new Date().valueOf();
}
}
JwtService.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "13.1.1", ngImport: i0, type: JwtService, deps: [{ token: i1.ConfigService }, { token: i2.LoggerService }], target: i0.ɵɵFactoryTarget.Injectable });
JwtService.ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "13.1.1", ngImport: i0, type: JwtService, providedIn: 'root' });
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "13.1.1", ngImport: i0, type: JwtService, decorators: [{
type: Injectable,
args: [{ providedIn: 'root' }]
}], ctorParameters: function () { return [{ type: i1.ConfigService }, { type: i2.LoggerService }]; } });
//# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoiand0LnNlcnZpY2UuanMiLCJzb3VyY2VSb290IjoiIiwic291cmNlcyI6WyIuLi8uLi8uLi8uLi8uLi9saWJzL25neC1qd3Qvc3JjL2xpYi9qd3Quc2VydmljZS50cyJdLCJuYW1lcyI6W10sIm1hcHBpbmdzIjoiQUFBQTs7Ozs7O0dBTUc7QUFFSCx1REFBdUQ7QUFDdkQsT0FBTyxFQUFFLFVBQVUsRUFBRSxNQUFNLGVBQWUsQ0FBQztBQUMzQyxPQUFPLEVBRUwsYUFBYSxFQUNiLHdCQUF3QixHQUN6QixNQUFNLHlCQUF5QixDQUFDO0FBQ2pDLE9BQU8sRUFBRSxhQUFhLEVBQUUsTUFBTSx5QkFBeUIsQ0FBQztBQUN4RCxPQUFPLEVBQUUsTUFBTSxFQUFFLE1BQU0sV0FBVyxDQUFDO0FBQ25DLE9BQU8sRUFBRSxTQUFTLElBQUksV0FBVyxFQUFFLFNBQVMsSUFBSSxXQUFXLEVBQUUsTUFBTSxXQUFXLENBQUM7QUFHL0UsT0FBTyxFQUFFLGdCQUFnQixFQUFFLE1BQU0sZUFBZSxDQUFDOzs7O0FBRWpEOztHQUVHO0FBRUgsTUFBTSxPQUFPLFVBQVU7SUFJckI7OztPQUdHO0lBQ0gsWUFBcUIsTUFBcUIsRUFBVyxNQUFxQjtRQUFyRCxXQUFNLEdBQU4sTUFBTSxDQUFlO1FBQVcsV0FBTSxHQUFOLE1BQU0sQ0FBZTtRQVBsRSxjQUFTLEdBQUcsS0FBSyxDQUFDO1FBQzFCLFlBQU8sR0FBb0Msd0JBQXdCLENBQUM7UUFPbEUsSUFBSSxDQUFDLE9BQU8sR0FBRyxXQUFXLENBQ3hCLFdBQVcsQ0FBQyxFQUFFLEdBQUcsRUFBRSxnQkFBZ0IsRUFBRSxDQUFDLEVBQ3RDLElBQUksQ0FBQyxNQUFNLENBQUMsT0FBTyxFQUNuQixDQUFDLElBQUksRUFBRSxHQUFHLEVBQUUsRUFBRSxDQUFDLENBQUMsS0FBSyxDQUFDLE9BQU8sQ0FBQyxJQUFJLENBQUMsQ0FBQyxDQUFDLENBQUMsR0FBRyxDQUFDLENBQUMsQ0FBQyxTQUFTLENBQUMsQ0FDdkQsQ0FBQztRQUVGLElBQUksQ0FB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QU8sT0FBTyxLQUFLLFFBQVEsRUFBRTtZQUMvQixPQUFPLEdBQUcsSUFBSSxDQUFDLFVBQVUsQ0FBQyxPQUFPLENBQUMsQ0FBQztTQUNwQztRQUNELE1BQU0sTUFBTSxHQUFHLE9BQU8sRUFBRSxNQUFNLElBQUksT0FBTyxFQUFFLEdBQUcsSUFBSSxJQUFJLENBQUMsT0FBTyxFQUFFLEdBQUcsRUFBRSxZQUFZLENBQUM7UUFDbEYsTUFBTSxLQUFLLEdBQUc7WUFDWixLQUFLLEVBQUUsQ0FBQztZQUNSLEtBQUssRUFBRSxNQUFNLEdBQUcsSUFBSSxDQUFDLE9BQU8sRUFBRSxHQUFHLEVBQUUsWUFBWSxJQUFJLENBQUM7U0FDckQsQ0FBQztRQUNGLE9BQU8sSUFBSSxDQUFDLEtBQUssQ0FBQyxJQUFJLENBQUMsTUFBTSxFQUFFLEdBQUcsS0FBSyxDQUFDLEtBQUssR0FBRyxLQUFLLENBQUMsS0FBSyxDQUFDLENBQUM7SUFDL0QsQ0FBQztJQUVEOzs7O09BSUc7SUFDSyxVQUFVLENBQUMsS0FBYztRQUMvQixPQUFPLEtBQUssQ0FBQyxDQUFDLENBQUMsSUFBSSxJQUFJLENBQUMsQ0FBQyxDQUFDLENBQUMsYUFBYSxDQUFDLEtBQUssQ0FBQyxDQUFDLE9BQU8sRUFBRSxDQUFDLENBQUMsQ0FBQyxJQUFJLElBQUksRUFBRSxDQUFDLE9BQU8sRUFBRSxDQUFDO0lBQ25GLENBQUM7O3VHQWhIVSxVQUFVOzJHQUFWLFVBQVUsY0FERyxNQUFNOzJGQUNuQixVQUFVO2tCQUR0QixVQUFVO21CQUFDLEVBQUUsVUFBVSxFQUFFLE1BQU0sRUFBRSIsInNvdXJjZXNDb250ZW50IjpbIi8qKlxuICogQGxpY2Vuc2VcbiAqIENvcHlyaWdodCBOZWVrd2FyZSBJbmMuIEFsbCBSaWdodHMgUmVzZXJ2ZWQuXG4gKlxuICogVXNlIG9mIHRoaXMgc291cmNlIGNvZGUgaXMgZ292ZXJuZWQgYnkgYSBwcm9wcmlldGFyeSBub3RpY2VcbiAqIHRoYXQgY2FuIGJlIGZvdW5kIGF0IGh0dHA6Ly9uZWVrd2FyZS5jb20vbGljZW5zZS9QUkkuaHRtbFxuICovXG5cbi8qIGVzbGludC1kaXNhYmxlIEB0eXBlc2NyaXB0LWVzbGludC9uby1leHBsaWNpdC1hbnkgKi9cbmltcG9ydCB7IEluamVjdGFibGUgfSBmcm9tICdAYW5ndWxhci9jb3JlJztcbmltcG9ydCB7XG4gIEFwcGxpY2F0aW9uQ29uZmlnLFxuICBDb25maWdTZXJ2aWNlLFxuICBEZWZhdWx0QXBwbGljYXRpb25Db25maWcsXG59IGZyb20gJ0BmdWxsZXJzdGFjay9uZ3gtY29uZmlnJztcbmltcG9ydCB7IExvZ2dlclNlcnZpY2UgfSBmcm9tICdAZnVsbGVyc3RhY2svbmd4LWxvZ2dlcic7XG5pbXBvcnQgeyBCYXNlNjQgfSBmcm9tICdqcy1iYXNlNjQnO1xuaW1wb3J0IHsgY2xvbmVEZWVwIGFzIGxkRGVlcENsb25lLCBtZXJnZVdpdGggYXMgbGRNZXJnZVdpdGggfSBmcm9tICdsb2Rhc2gtZXMnO1xuaW1wb3J0IHsgRGVlcFJlYWRvbmx5IH0gZnJvbSAndHMtZXNzZW50aWFscyc7XG5cbmltcG9ydCB7IERlZmF1bHRKd3RDb25maWcgfSBmcm9tICcuL2p3dC5kZWZhdWx0JztcblxuLyoqXG4gKiBBbiBpbmplY3RhYmxlIGNsYXNzIHRoYXQgaGFuZGxlcyBKV1Qgc2VydmljZVxuICovXG5ASW5qZWN0YWJsZSh7IHByb3ZpZGVkSW46ICdyb290JyB9KVxuZXhwb3J0IGNsYXNzIEp3dFNlcnZpY2Uge1xuICBwcml2YXRlIG5hbWVTcGFjZSA9ICdKV1QnO1xuICBvcHRpb25zOiBEZWVwUmVhZG9ubHk8QXBwbGljYXRpb25Db25maWc+ID0gRGVmYXVsdEFwcGxpY2F0aW9uQ29uZmlnO1xuXG4gIC8qKlxuICAgKiBDbGFzcyBjb25zdHJ1Y3RvclxuICAgKiBAcGFyYW0gb3B0aW9ucyBhbiBvcHRpb25hbCBjb25maWd1cmF0aW9uIG9iamVjdFxuICAgKi9cbiAgY29uc3RydWN0b3IocmVhZG9ubHkgY29uZmlnOiBDb25maWdTZXJ2aWNlLCByZWFkb25seSBsb2dnZXI6IExvZ2dlclNlcnZpY2UpIHtcbiAgICB0aGlzLm9wdGlvbnMgPSBsZE1lcmdlV2l0aChcbiAgICAgIGxkRGVlcENsb25lKHsgand0OiBEZWZhdWx0Snd0Q29uZmlnIH0pLFxuICAgICAgdGhpcy5jb25maWcub3B0aW9ucyxcbiAgICAgIChkZXN0LCBzcmMpID0+IChBcnJheS5pc0FycmF5KGRlc3QpID8gc3JjIDogdW5kZWZpbmVkKVxuICAgICk7XG5cbiAgICB0aGlzLmxvZ2dlci5pbmZvKGBbJHt0aGlzLm5hbWVTcGFjZX1dIEp3dFNlcnZpY2UgcmVhZHkgLi4uYCk7XG4gIH1cblxuICAvKipcbiAgICogR2V0cyB0aGUgcGF5bG9hZCBwb3J0aW9uIG9mIGEgSldUIHRva2VuXG4gICAqIEBwYXJhbSB0b2tlbiBKV1QgdG9rZW4gKGJhc2U2NCBlbmNyeXB0ZWQpXG4gICAqIEByZXR1cm5zIGEgcGF5bG9hZCBvYmplY3Qgb3IgbnVsbCBpZiBkZWNvZGUgZmFpbHNcbiAgICovXG4gIGdldFBheWxvYWQ8VD4odG9rZW46IHN0cmluZyk6IGFueSB7XG4gICAgbGV0IHBhcnRzID0gW107XG5cbiAgICB0cnkge1xuICAgICAgcGFydHMgPSB0b2tlbi5zcGxpdCgnLicpO1xuICAgICAgaWYgKHBhcnRzLmxlbmd0aCAhPT0gMykge1xuICAgICAgICB0aHJvdyBFcnJvcignSldUIG11c3QgaGF2ZSAzIHBhcnRzJyk7XG4gICAgICB9XG4gICAgfSBjYXRjaCAoZSkge1xuICAgICAgdGhpcy5sb2dnZXIuZXJyb3IoZS5tZXNzYWdlKTtcbiAgICAgIHJldHVybiB1bmRlZmluZWQ7XG4gICAgfVxuXG4gICAgdHJ5IHtcbiAgICAgIGNvbnN0IGRlY29kZWQgPSBCYXNlNjQuZGVjb2RlKHBhcnRzWzFdKTtcbiAgICAgIGNvbnN0IHBheWxvYWQgPSBKU09OLnBhcnNlKGRlY29kZWQpO1xuICAgICAgcmV0dXJuIHBheWxvYWQgYXMgVDtcbiAgICB9IGNhdGNoIChlKSB7XG4gICAgICB0aGlzLmxvZ2dlci5lcnJvcignQ2Fubm90IGRlY29kZSB0aGUgdG9rZW4nKTtcbiAgICB9XG5cbiAgICByZXR1cm4gdW5kZWZpbmVkO1xuICB9XG5cbiAgLyoqXG4gICAqIFRlbGxzIGlmIGEgSldUIGlzIHRva2VuIGlzIGV4cGlyZWRcbiAgICogQHBhcmFtIHBheWxvYWQgSldUIHBheWxvYWQgb2JqZWN0XG4gICAqIEByZXR1cm4gdHJ1ZSBpZiBKV1QgaXMgYWxyZWFkeSBleHBpcmVkLCBlbHNlIGZhbHNlXG4gICAqL1xuICBpc0V4cGlyZWQocGF5bG9hZDogYW55KTogYm9vbGVhbiB7XG4gICAgaWYgKHR5cGVvZiBwYXlsb2FkID09PSAnc3RyaW5nJykge1xuICAgICAgcGF5bG9hZCA9IHRoaXMuZ2V0UGF5bG9hZChwYXlsb2FkKTtcbiAgICB9XG4gICAgaWYgKHBheWxvYWQpIHtcbiAgICAgIGNvbnN0IG9mZnNldCA9IChwYXJzZUludChwYXlsb2FkLmxlZSwgMTApIHx8IHRoaXMub3B0aW9ucy5qd3QuZXhwaXJ5TGVld2F5KSAqIDEwMDA7XG4gICAgICBjb25zdCBub3cgPSB0aGlzLnV0Y1NlY29uZHMoKTtcbiAgICAgIGNvbnN0IGV4cGlyeSA9IHRoaXMudXRjU2Vjb25kcyhwYXlsb2FkLmV4cCk7XG4gICAgICBjb25zdCBleHBpcmVkID0gbm93ID4gZXhwaXJ5ICsgb2Zmc2V0O1xuICAgICAgcmV0dXJuIGV4cGlyZWQ7XG4gICAgfVxuICAgIHJldHVybiB0cnVlO1xuICB9XG5cbiAgLyoqXG4gICAqIENhbGN1bGF0ZXMgdGhlIG5leHQgcmVmcmVzaCB0aW1lXG4gICAqIEBwYXJhbSBwYXlsb2FkIEpXVCBwYXlsb2FkIG9iamVjdFxuICAgKiBAcGFyYW0gb2Zmc2V0IGlmIHRydWUsIGEgcmFuZG9tIHRpbWUgaXMgYWRkZWQgdG8gdGhlIHJlZnJlc2ggdGltZVxuICAgKiB3aGVyZSBuZXR3b3JrRGVsYXkgPCByYW5kb20gPCBsZWV3YXlcbiAgICogQHJldHVybnMgdG90YWwgbnVtYmVyIG9mIHNlY29uZHMgdGlsbCBleHBpcnkgb3IgMCBpZiB0b2tlbiBpcyBleHBpcmVkXG4gICAqL1xuICBnZXRSZWZyZXNoVGltZShwYXlsb2FkOiBhbnksIG9mZnNldCA9IHRydWUpOiBudW1iZXIge1xuICAgIGlmICh0eXBlb2YgcGF5bG9hZCA9PT0gJ3N0cmluZycpIHtcbiAgICAgIHBheWxvYWQgPSB0aGlzLmdldFBheWxvYWQocGF5bG9hZCk7XG4gICAgfVxuICAgIGlmIChwYXlsb2FkICYmICF0aGlzLmlzRXhwaXJlZChwYXlsb2FkKSkge1xuICAgICAgY29uc3Qgbm93ID0gdGhpcy51dGNTZWNvbmRzKCk7XG4gICAgICBjb25zdCBleHBpcnkgPSB0aGlzLnV0Y1NlY29uZHMocGF5bG9hZC5leHApO1xuICAgICAgY29uc3QgcmVmcmVzaCA9IE1hdGguZmxvb3IoKGV4cGlyeSAtIG5vdykgLyAxMDAwKTtcbiAgICAgIGNvbnN0IHJhbmRvbSA9IHRoaXMuZ2V0UmFuZG9tT2Zmc2V0KHBheWxvYWQpO1xuICAgICAgY29uc3QgdGltZSA9IG9mZnNldCA/IHJlZnJlc2ggKyByYW5kb20gOiByZWZyZXNoO1xuICAgICAgcmV0dXJuIHRpbWU7XG4gICAgfVxuICAgIHJldHVybiAwO1xuICB9XG5cbiAgLyoqXG4gICAqIENhbGN1bGF0ZXMgYSByYW5kb20gbnVtYmVyIHdoZXJlIG5ldHdvcmtEZWxheSA8IHJhbmRvbSA8IGxlZXdheVxuICAgKiBAcGFyYW0gcGF5bG9hZCBKV1QgcGF5bG9hZCBvYmplY3RcbiAgICogQHJldHVybnMgYSByYW5kb20gdG90YWwgbnVtYmVyIG9mIHNlY29uZHNcbiAgICovXG4gIHByaXZhdGUgZ2V0UmFuZG9tT2Zmc2V0KHBheWxvYWQ6IGFueSk6IG51bWJlciB7XG4gICAgaWYgKHR5cGVvZiBwYXlsb2FkID09PSAnc3RyaW5nJykge1xuICAgICAgcGF5bG9hZCA9IHRoaXMuZ2V0UGF5bG9hZChwYXlsb2FkKTtcbiAgICB9XG4gICAgY29uc3QgbGVld2F5ID0gcGF5bG9hZD8ubGVld2F5IHx8IHBheWxvYWQ/LmxlZSB8fCB0aGlzLm9wdGlvbnM/Lmp3dD8uZXhwaXJ5TGVld2F5O1xuICAgIGNvbnN0IHJhbmdlID0ge1xuICAgICAgbG93ZXI6IDEsXG4gICAgICB1cHBlcjogbGVld2F5IC0gdGhpcy5vcHRpb25zPy5qd3Q/Lm5ldHdvcmtEZWxheSB8fCAyLFxuICAgIH07XG4gICAgcmV0dXJuIE1hdGguZmxvb3IoTWF0aC5yYW5kb20oKSAqIHJhbmdlLnVwcGVyICsgcmFuZ2UubG93ZXIpO1xuICB9XG5cbiAgLyoqXG4gICAqIENhbGN1bGF0ZXMgdGhlIFVUQyB2YWx1ZSBvZiBkYXRlL3RpbWUgaW4gc2Vjb25kc1xuICAgKiBAcGFyYW0gaW5wdXQgZGF0ZS90aW1lIGluIHNlY29uZHNcbiAgICogQHJldHVybnMgVVRDIHZhbHVlIG9mIGRhdGUvdGltZSBpbiBzZWNvbmRzXG4gICAqL1xuICBwcml2YXRlIHV0Y1NlY29uZHMoaW5wdXQ/OiBudW1iZXIpOiBudW1iZXIge1xuICAgIHJldHVybiBpbnB1dCA/IG5ldyBEYXRlKDApLnNldFVUQ1NlY29uZHMoaW5wdXQpLnZhbHVlT2YoKSA6IG5ldyBEYXRlKCkudmFsdWVPZigpO1xuICB9XG59XG4iXX0=