UNPKG

11.3 kBJavaScriptView Raw
1import * as i0 from '@angular/core';
2import { InjectionToken, Injectable, Inject, NgModule, Optional, SkipSelf } from '@angular/core';
3import { DOCUMENT } from '@angular/common';
4import { map, mergeMap } from 'rxjs/operators';
5import { defer, of } from 'rxjs';
6import { HTTP_INTERCEPTORS } from '@angular/common/http';
7
8const JWT_OPTIONS = new InjectionToken('JWT_OPTIONS');
9
10// tslint:disable:no-bitwise
11class JwtHelperService {
12 constructor(config = null) {
13 this.tokenGetter = (config && config.tokenGetter) || function () { };
14 }
15 urlBase64Decode(str) {
16 let output = str.replace(/-/g, '+').replace(/_/g, '/');
17 switch (output.length % 4) {
18 case 0: {
19 break;
20 }
21 case 2: {
22 output += '==';
23 break;
24 }
25 case 3: {
26 output += '=';
27 break;
28 }
29 default: {
30 throw new Error('Illegal base64url string!');
31 }
32 }
33 return this.b64DecodeUnicode(output);
34 }
35 // credits for decoder goes to https://github.com/atk
36 b64decode(str) {
37 const chars = 'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/=';
38 let output = '';
39 str = String(str).replace(/=+$/, '');
40 if (str.length % 4 === 1) {
41 throw new Error(`'atob' failed: The string to be decoded is not correctly encoded.`);
42 }
43 for (
44 // initialize result and counters
45 let bc = 0, bs, buffer, idx = 0;
46 // get next character
47 (buffer = str.charAt(idx++));
48 // character found in table? initialize bit storage and add its ascii value;
49 ~buffer &&
50 ((bs = bc % 4 ? bs * 64 + buffer : buffer),
51 // and if not first of each 4 characters,
52 // convert the first 8 bits to one ascii character
53 bc++ % 4)
54 ? (output += String.fromCharCode(255 & (bs >> ((-2 * bc) & 6))))
55 : 0) {
56 // try to find character in table (0-63, not found => -1)
57 buffer = chars.indexOf(buffer);
58 }
59 return output;
60 }
61 b64DecodeUnicode(str) {
62 return decodeURIComponent(Array.prototype.map
63 .call(this.b64decode(str), (c) => {
64 return '%' + ('00' + c.charCodeAt(0).toString(16)).slice(-2);
65 })
66 .join(''));
67 }
68 decodeToken(token = this.tokenGetter()) {
69 if (token instanceof Promise) {
70 return token.then(t => this._decodeToken(t));
71 }
72 return this._decodeToken(token);
73 }
74 _decodeToken(token) {
75 if (!token || token === '') {
76 return null;
77 }
78 const parts = token.split('.');
79 if (parts.length !== 3) {
80 throw new Error(`The inspected token doesn't appear to be a JWT. Check to make sure it has three parts and see https://jwt.io for more.`);
81 }
82 const decoded = this.urlBase64Decode(parts[1]);
83 if (!decoded) {
84 throw new Error('Cannot decode the token.');
85 }
86 return JSON.parse(decoded);
87 }
88 getTokenExpirationDate(token = this.tokenGetter()) {
89 if (token instanceof Promise) {
90 return token.then(t => this._getTokenExpirationDate(t));
91 }
92 return this._getTokenExpirationDate(token);
93 }
94 _getTokenExpirationDate(token) {
95 let decoded;
96 decoded = this.decodeToken(token);
97 if (!decoded || !decoded.hasOwnProperty('exp')) {
98 return null;
99 }
100 const date = new Date(0);
101 date.setUTCSeconds(decoded.exp);
102 return date;
103 }
104 isTokenExpired(token = this.tokenGetter(), offsetSeconds) {
105 if (token instanceof Promise) {
106 return token.then(t => this._isTokenExpired(t, offsetSeconds));
107 }
108 return this._isTokenExpired(token, offsetSeconds);
109 }
110 _isTokenExpired(token, offsetSeconds) {
111 if (!token || token === '') {
112 return true;
113 }
114 const date = this.getTokenExpirationDate(token);
115 offsetSeconds = offsetSeconds || 0;
116 if (date === null) {
117 return false;
118 }
119 return !(date.valueOf() > new Date().valueOf() + offsetSeconds * 1000);
120 }
121 getAuthScheme(authScheme, request) {
122 if (typeof authScheme === 'function') {
123 return authScheme(request);
124 }
125 return authScheme;
126 }
127}
128JwtHelperService.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "12.2.16", ngImport: i0, type: JwtHelperService, deps: [{ token: JWT_OPTIONS }], target: i0.ɵɵFactoryTarget.Injectable });
129JwtHelperService.ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "12.2.16", ngImport: i0, type: JwtHelperService });
130i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "12.2.16", ngImport: i0, type: JwtHelperService, decorators: [{
131 type: Injectable
132 }], ctorParameters: function () { return [{ type: undefined, decorators: [{
133 type: Inject,
134 args: [JWT_OPTIONS]
135 }] }]; } });
136
137const fromPromiseOrValue = (input) => {
138 if (input instanceof Promise) {
139 return defer(() => input);
140 }
141 return of(input);
142};
143class JwtInterceptor {
144 constructor(config, jwtHelper, document) {
145 this.jwtHelper = jwtHelper;
146 this.document = document;
147 this.standardPorts = ['80', '443'];
148 this.tokenGetter = config.tokenGetter;
149 this.headerName = config.headerName || 'Authorization';
150 this.authScheme =
151 config.authScheme || config.authScheme === ''
152 ? config.authScheme
153 : 'Bearer ';
154 this.allowedDomains = config.allowedDomains || [];
155 this.disallowedRoutes = config.disallowedRoutes || [];
156 this.throwNoTokenError = config.throwNoTokenError || false;
157 this.skipWhenExpired = config.skipWhenExpired;
158 }
159 isAllowedDomain(request) {
160 const requestUrl = new URL(request.url, this.document.location.origin);
161 // If the host equals the current window origin,
162 // the domain is allowed by default
163 if (requestUrl.host === this.document.location.host) {
164 return true;
165 }
166 // If not the current domain, check the allowed list
167 const hostName = `${requestUrl.hostname}${requestUrl.port && !this.standardPorts.includes(requestUrl.port)
168 ? ':' + requestUrl.port
169 : ''}`;
170 return (this.allowedDomains.findIndex((domain) => typeof domain === 'string'
171 ? domain === hostName
172 : domain instanceof RegExp
173 ? domain.test(hostName)
174 : false) > -1);
175 }
176 isDisallowedRoute(request) {
177 const requestedUrl = new URL(request.url, this.document.location.origin);
178 return (this.disallowedRoutes.findIndex((route) => {
179 if (typeof route === 'string') {
180 const parsedRoute = new URL(route, this.document.location.origin);
181 return (parsedRoute.hostname === requestedUrl.hostname &&
182 parsedRoute.pathname === requestedUrl.pathname);
183 }
184 if (route instanceof RegExp) {
185 return route.test(request.url);
186 }
187 return false;
188 }) > -1);
189 }
190 handleInterception(token, request, next) {
191 const authScheme = this.jwtHelper.getAuthScheme(this.authScheme, request);
192 if (!token && this.throwNoTokenError) {
193 throw new Error('Could not get token from tokenGetter function.');
194 }
195 let tokenIsExpired = of(false);
196 if (this.skipWhenExpired) {
197 tokenIsExpired = token ? fromPromiseOrValue(this.jwtHelper.isTokenExpired(token)) : of(true);
198 }
199 if (token) {
200 return tokenIsExpired.pipe(map((isExpired) => isExpired && this.skipWhenExpired
201 ? request.clone()
202 : request.clone({
203 setHeaders: {
204 [this.headerName]: `${authScheme}${token}`,
205 },
206 })), mergeMap((innerRequest) => next.handle(innerRequest)));
207 }
208 return next.handle(request);
209 }
210 intercept(request, next) {
211 if (!this.isAllowedDomain(request) || this.isDisallowedRoute(request)) {
212 return next.handle(request);
213 }
214 const token = this.tokenGetter(request);
215 return fromPromiseOrValue(token).pipe(mergeMap((asyncToken) => {
216 return this.handleInterception(asyncToken, request, next);
217 }));
218 }
219}
220JwtInterceptor.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "12.2.16", ngImport: i0, type: JwtInterceptor, deps: [{ token: JWT_OPTIONS }, { token: JwtHelperService }, { token: DOCUMENT }], target: i0.ɵɵFactoryTarget.Injectable });
221JwtInterceptor.ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "12.2.16", ngImport: i0, type: JwtInterceptor });
222i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "12.2.16", ngImport: i0, type: JwtInterceptor, decorators: [{
223 type: Injectable
224 }], ctorParameters: function () { return [{ type: undefined, decorators: [{
225 type: Inject,
226 args: [JWT_OPTIONS]
227 }] }, { type: JwtHelperService }, { type: Document, decorators: [{
228 type: Inject,
229 args: [DOCUMENT]
230 }] }]; } });
231
232class JwtModule {
233 constructor(parentModule) {
234 if (parentModule) {
235 throw new Error(`JwtModule is already loaded. It should only be imported in your application's main module.`);
236 }
237 }
238 static forRoot(options) {
239 return {
240 ngModule: JwtModule,
241 providers: [
242 {
243 provide: HTTP_INTERCEPTORS,
244 useClass: JwtInterceptor,
245 multi: true,
246 },
247 options.jwtOptionsProvider || {
248 provide: JWT_OPTIONS,
249 useValue: options.config,
250 },
251 JwtHelperService,
252 ],
253 };
254 }
255}
256JwtModule.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "12.2.16", ngImport: i0, type: JwtModule, deps: [{ token: JwtModule, optional: true, skipSelf: true }], target: i0.ɵɵFactoryTarget.NgModule });
257JwtModule.ɵmod = i0.ɵɵngDeclareNgModule({ minVersion: "12.0.0", version: "12.2.16", ngImport: i0, type: JwtModule });
258JwtModule.ɵinj = i0.ɵɵngDeclareInjector({ minVersion: "12.0.0", version: "12.2.16", ngImport: i0, type: JwtModule });
259i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "12.2.16", ngImport: i0, type: JwtModule, decorators: [{
260 type: NgModule
261 }], ctorParameters: function () { return [{ type: JwtModule, decorators: [{
262 type: Optional
263 }, {
264 type: SkipSelf
265 }] }]; } });
266
267/*
268 * Public API Surface of angular-jwt
269 */
270
271/**
272 * Generated bundle index. Do not edit.
273 */
274
275export { JWT_OPTIONS, JwtHelperService, JwtInterceptor, JwtModule };
276//# sourceMappingURL=auth0-angular-jwt.js.map