UNPKG

angular2-jwt

Version:

Helper library for handling JWTs in Angular 2+

362 lines 13.9 kB
"use strict"; var __extends = (this && this.__extends) || function (d, b) { for (var p in b) if (b.hasOwnProperty(p)) d[p] = b[p]; function __() { this.constructor = d; } d.prototype = b === null ? Object.create(b) : (__.prototype = b.prototype, new __()); }; var __decorate = (this && this.__decorate) || function (decorators, target, key, desc) { var c = arguments.length, r = c < 3 ? target : desc === null ? desc = Object.getOwnPropertyDescriptor(target, key) : desc, d; if (typeof Reflect === "object" && typeof Reflect.decorate === "function") r = Reflect.decorate(decorators, target, key, desc); else for (var i = decorators.length - 1; i >= 0; i--) if (d = decorators[i]) r = (c < 3 ? d(r) : c > 3 ? d(target, key, r) : d(target, key)) || r; return c > 3 && r && Object.defineProperty(target, key, r), r; }; var __metadata = (this && this.__metadata) || function (k, v) { if (typeof Reflect === "object" && typeof Reflect.metadata === "function") return Reflect.metadata(k, v); }; var __param = (this && this.__param) || function (paramIndex, decorator) { return function (target, key) { decorator(target, key, paramIndex); } }; var http_1 = require("@angular/http"); var core_1 = require("@angular/core"); var Observable_1 = require("rxjs/Observable"); require("rxjs/add/observable/fromPromise"); require("rxjs/add/observable/defer"); require("rxjs/add/operator/mergeMap"); var AuthConfigConsts = (function () { function AuthConfigConsts() { } return AuthConfigConsts; }()); AuthConfigConsts.DEFAULT_TOKEN_NAME = 'token'; AuthConfigConsts.DEFAULT_HEADER_NAME = 'Authorization'; AuthConfigConsts.HEADER_PREFIX_BEARER = 'Bearer '; exports.AuthConfigConsts = AuthConfigConsts; var AuthConfigDefaults = { headerName: AuthConfigConsts.DEFAULT_HEADER_NAME, headerPrefix: null, tokenName: AuthConfigConsts.DEFAULT_TOKEN_NAME, tokenGetter: function () { return localStorage.getItem(AuthConfigDefaults.tokenName); }, noJwtError: false, noClientCheck: false, globalHeaders: [], noTokenScheme: false }; /** * Sets up the authentication configuration. */ var AuthConfig = (function () { function AuthConfig(config) { config = config || {}; this._config = objectAssign({}, AuthConfigDefaults, config); if (this._config.headerPrefix) { this._config.headerPrefix += ' '; } else if (this._config.noTokenScheme) { this._config.headerPrefix = ''; } else { this._config.headerPrefix = AuthConfigConsts.HEADER_PREFIX_BEARER; } if (config.tokenName && !config.tokenGetter) { this._config.tokenGetter = function () { return localStorage.getItem(config.tokenName); }; } } AuthConfig.prototype.getConfig = function () { return this._config; }; return AuthConfig; }()); exports.AuthConfig = AuthConfig; var AuthHttpError = (function (_super) { __extends(AuthHttpError, _super); function AuthHttpError() { return _super !== null && _super.apply(this, arguments) || this; } return AuthHttpError; }(Error)); exports.AuthHttpError = AuthHttpError; /** * Allows for explicit authenticated HTTP requests. */ var AuthHttp = (function () { function AuthHttp(options, http, defOpts) { var _this = this; this.http = http; this.defOpts = defOpts; this.config = options.getConfig(); this.tokenStream = new Observable_1.Observable(function (obs) { obs.next(_this.config.tokenGetter()); }); } AuthHttp.prototype.mergeOptions = function (providedOpts, defaultOpts) { var newOptions = defaultOpts || new http_1.RequestOptions(); if (this.config.globalHeaders) { this.setGlobalHeaders(this.config.globalHeaders, providedOpts); } newOptions = newOptions.merge(new http_1.RequestOptions(providedOpts)); return newOptions; }; AuthHttp.prototype.requestHelper = function (requestArgs, additionalOptions) { var options = new http_1.RequestOptions(requestArgs); if (additionalOptions) { options = options.merge(additionalOptions); } return this.request(new http_1.Request(this.mergeOptions(options, this.defOpts))); }; AuthHttp.prototype.requestWithToken = function (req, token) { if (!this.config.noClientCheck && !tokenNotExpired(undefined, token)) { if (!this.config.noJwtError) { return new Observable_1.Observable(function (obs) { obs.error(new AuthHttpError('No JWT present or has expired')); }); } } else { req.headers.set(this.config.headerName, this.config.headerPrefix + token); } return this.http.request(req); }; AuthHttp.prototype.setGlobalHeaders = function (headers, request) { if (!request.headers) { request.headers = new http_1.Headers(); } headers.forEach(function (header) { var key = Object.keys(header)[0]; var headerValue = header[key]; request.headers.set(key, headerValue); }); }; AuthHttp.prototype.request = function (url, options) { var _this = this; if (typeof url === 'string') { return this.get(url, options); // Recursion: transform url from String to Request } // else if ( ! url instanceof Request ) { // throw new Error('First argument must be a url string or Request instance.'); // } // from this point url is always an instance of Request; var req = url; // Create a cold observable and load the token just in time return Observable_1.Observable.defer(function () { var token = _this.config.tokenGetter(); if (token instanceof Promise) { return Observable_1.Observable.fromPromise(token).mergeMap(function (jwtToken) { return _this.requestWithToken(req, jwtToken); }); } else { return _this.requestWithToken(req, token); } }); }; AuthHttp.prototype.get = function (url, options) { return this.requestHelper({ body: '', method: http_1.RequestMethod.Get, url: url }, options); }; AuthHttp.prototype.post = function (url, body, options) { return this.requestHelper({ body: body, method: http_1.RequestMethod.Post, url: url }, options); }; AuthHttp.prototype.put = function (url, body, options) { return this.requestHelper({ body: body, method: http_1.RequestMethod.Put, url: url }, options); }; AuthHttp.prototype.delete = function (url, options) { return this.requestHelper({ body: '', method: http_1.RequestMethod.Delete, url: url }, options); }; AuthHttp.prototype.patch = function (url, body, options) { return this.requestHelper({ body: body, method: http_1.RequestMethod.Patch, url: url }, options); }; AuthHttp.prototype.head = function (url, options) { return this.requestHelper({ body: '', method: http_1.RequestMethod.Head, url: url }, options); }; AuthHttp.prototype.options = function (url, options) { return this.requestHelper({ body: '', method: http_1.RequestMethod.Options, url: url }, options); }; return AuthHttp; }()); AuthHttp = __decorate([ core_1.Injectable(), __metadata("design:paramtypes", [AuthConfig, http_1.Http, http_1.RequestOptions]) ], AuthHttp); exports.AuthHttp = AuthHttp; /** * Helper class to decode and find JWT expiration. */ var JwtHelper = (function () { function JwtHelper() { } JwtHelper.prototype.urlBase64Decode = function (str) { var output = str.replace(/-/g, '+').replace(/_/g, '/'); switch (output.length % 4) { case 0: { break; } case 2: { output += '=='; break; } case 3: { output += '='; break; } default: { throw 'Illegal base64url string!'; } } return this.b64DecodeUnicode(output); }; // credits for decoder goes to https://github.com/atk JwtHelper.prototype.b64decode = function (str) { var chars = 'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/='; var output = ''; str = String(str).replace(/=+$/, ''); if (str.length % 4 == 1) { throw new Error("'atob' failed: The string to be decoded is not correctly encoded."); } for ( // initialize result and counters var bc = 0, bs = void 0, buffer = void 0, idx = 0; // get next character buffer = str.charAt(idx++); // character found in table? initialize bit storage and add its ascii value; ~buffer && (bs = bc % 4 ? bs * 64 + buffer : buffer, // and if not first of each 4 characters, // convert the first 8 bits to one ascii character bc++ % 4) ? output += String.fromCharCode(255 & bs >> (-2 * bc & 6)) : 0) { // try to find character in table (0-63, not found => -1) buffer = chars.indexOf(buffer); } return output; }; // https://developer.mozilla.org/en/docs/Web/API/WindowBase64/Base64_encoding_and_decoding#The_Unicode_Problem JwtHelper.prototype.b64DecodeUnicode = function (str) { return decodeURIComponent(Array.prototype.map.call(this.b64decode(str), function (c) { return '%' + ('00' + c.charCodeAt(0).toString(16)).slice(-2); }).join('')); }; JwtHelper.prototype.decodeToken = function (token) { var parts = token.split('.'); if (parts.length !== 3) { throw new Error('JWT must have 3 parts'); } var decoded = this.urlBase64Decode(parts[1]); if (!decoded) { throw new Error('Cannot decode the token'); } return JSON.parse(decoded); }; JwtHelper.prototype.getTokenExpirationDate = function (token) { var decoded; decoded = this.decodeToken(token); if (!decoded.hasOwnProperty('exp')) { return null; } var date = new Date(0); // The 0 here is the key, which sets the date to the epoch date.setUTCSeconds(decoded.exp); return date; }; JwtHelper.prototype.isTokenExpired = function (token, offsetSeconds) { var date = this.getTokenExpirationDate(token); offsetSeconds = offsetSeconds || 0; if (date == null) { return false; } // Token expired? return !(date.valueOf() > (new Date().valueOf() + (offsetSeconds * 1000))); }; return JwtHelper; }()); exports.JwtHelper = JwtHelper; /** * Checks for presence of token and that token hasn't expired. * For use with the @CanActivate router decorator and NgIf */ function tokenNotExpired(tokenName, jwt) { if (tokenName === void 0) { tokenName = AuthConfigConsts.DEFAULT_TOKEN_NAME; } var token = jwt || localStorage.getItem(tokenName); var jwtHelper = new JwtHelper(); return token != null && !jwtHelper.isTokenExpired(token); } exports.tokenNotExpired = tokenNotExpired; exports.AUTH_PROVIDERS = [ { provide: AuthHttp, deps: [http_1.Http, http_1.RequestOptions], useFactory: function (http, options) { return new AuthHttp(new AuthConfig(), http, options); } } ]; function provideAuth(config) { return [ { provide: AuthHttp, deps: [http_1.Http, http_1.RequestOptions], useFactory: function (http, options) { return new AuthHttp(new AuthConfig(config), http, options); } } ]; } exports.provideAuth = provideAuth; var hasOwnProperty = Object.prototype.hasOwnProperty; var propIsEnumerable = Object.prototype.propertyIsEnumerable; function toObject(val) { if (val === null || val === undefined) { throw new TypeError('Object.assign cannot be called with null or undefined'); } return Object(val); } function objectAssign(target) { var source = []; for (var _i = 1; _i < arguments.length; _i++) { source[_i - 1] = arguments[_i]; } var from; var to = toObject(target); var symbols; for (var s = 1; s < arguments.length; s++) { from = Object(arguments[s]); for (var key in from) { if (hasOwnProperty.call(from, key)) { to[key] = from[key]; } } if (Object.getOwnPropertySymbols) { symbols = Object.getOwnPropertySymbols(from); for (var i = 0; i < symbols.length; i++) { if (propIsEnumerable.call(from, symbols[i])) { to[symbols[i]] = from[symbols[i]]; } } } } return to; } /** * Module for angular2-jwt * @experimental */ var AuthModule = AuthModule_1 = (function () { function AuthModule(parentModule) { if (parentModule) { throw new Error('AuthModule is already loaded. Import it in the AppModule only'); } } AuthModule.forRoot = function (config) { return { ngModule: AuthModule_1, providers: [ { provide: AuthConfig, useValue: config } ] }; }; return AuthModule; }()); AuthModule = AuthModule_1 = __decorate([ core_1.NgModule({ imports: [http_1.HttpModule], providers: [AuthHttp, JwtHelper] }), __param(0, core_1.Optional()), __param(0, core_1.SkipSelf()), __metadata("design:paramtypes", [AuthModule]) ], AuthModule); exports.AuthModule = AuthModule; var AuthModule_1; //# sourceMappingURL=angular2-jwt.js.map