1 | import * as i0 from '@angular/core';
|
2 | import { InjectionToken, Injectable, Inject, NgModule, Optional, SkipSelf } from '@angular/core';
|
3 | import { DOCUMENT } from '@angular/common';
|
4 | import { map, mergeMap } from 'rxjs/operators';
|
5 | import { defer, of } from 'rxjs';
|
6 | import { HTTP_INTERCEPTORS } from '@angular/common/http';
|
7 |
|
8 | const JWT_OPTIONS = new InjectionToken('JWT_OPTIONS');
|
9 |
|
10 |
|
11 | class 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 |
|
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 |
|
45 | let bc = 0, bs, buffer, idx = 0;
|
46 |
|
47 | (buffer = str.charAt(idx++));
|
48 |
|
49 | ~buffer &&
|
50 | ((bs = bc % 4 ? bs * 64 + buffer : buffer),
|
51 |
|
52 |
|
53 | bc++ % 4)
|
54 | ? (output += String.fromCharCode(255 & (bs >> ((-2 * bc) & 6))))
|
55 | : 0) {
|
56 |
|
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 | }
|
128 | JwtHelperService.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "12.2.16", ngImport: i0, type: JwtHelperService, deps: [{ token: JWT_OPTIONS }], target: i0.ɵɵFactoryTarget.Injectable });
|
129 | JwtHelperService.ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "12.2.16", ngImport: i0, type: JwtHelperService });
|
130 | i0.ɵɵ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 |
|
137 | const fromPromiseOrValue = (input) => {
|
138 | if (input instanceof Promise) {
|
139 | return defer(() => input);
|
140 | }
|
141 | return of(input);
|
142 | };
|
143 | class 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 |
|
162 |
|
163 | if (requestUrl.host === this.document.location.host) {
|
164 | return true;
|
165 | }
|
166 |
|
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 | }
|
220 | JwtInterceptor.ɵ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 });
|
221 | JwtInterceptor.ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "12.2.16", ngImport: i0, type: JwtInterceptor });
|
222 | i0.ɵɵ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 |
|
232 | class 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 | }
|
256 | JwtModule.ɵ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 });
|
257 | JwtModule.ɵmod = i0.ɵɵngDeclareNgModule({ minVersion: "12.0.0", version: "12.2.16", ngImport: i0, type: JwtModule });
|
258 | JwtModule.ɵinj = i0.ɵɵngDeclareInjector({ minVersion: "12.0.0", version: "12.2.16", ngImport: i0, type: JwtModule });
|
259 | i0.ɵɵ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 |
|
269 |
|
270 |
|
271 |
|
272 |
|
273 |
|
274 |
|
275 | export { JWT_OPTIONS, JwtHelperService, JwtInterceptor, JwtModule };
|
276 |
|