ngx-oauth 1.1.0 → 2.1.2

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -0,0 +1,661 @@
1
+ import * as i0 from '@angular/core';
2
+ import { InjectionToken, Injectable, Inject, EventEmitter, Component, ViewEncapsulation, Input, Output, ContentChild, HostListener, PLATFORM_ID, Optional, NgModule } from '@angular/core';
3
+ import { __awaiter } from 'tslib';
4
+ import * as i1 from '@angular/common/http';
5
+ import { HttpHeaders, HttpParams, HTTP_INTERCEPTORS, HttpClientModule } from '@angular/common/http';
6
+ import { filter, map, switchMap, shareReplay, tap, catchError, concatMap, delay } from 'rxjs/operators';
7
+ import { ReplaySubject, of, EMPTY, from, noop, throwError, Subscription, take } from 'rxjs';
8
+ import * as i2 from '@angular/common';
9
+ import { isPlatformBrowser, CommonModule } from '@angular/common';
10
+ import * as i3 from '@angular/forms';
11
+ import { FormsModule } from '@angular/forms';
12
+ import { RouterModule } from '@angular/router';
13
+
14
+ const SERVER_HOST = new InjectionToken('SERVER_HOST');
15
+ const SERVER_PATH = new InjectionToken('SERVER_PATH');
16
+ const LOCATION = new InjectionToken('Location');
17
+ const STORAGE = new InjectionToken('Storage');
18
+ const OAUTH_CONFIG = new InjectionToken('OAuthConfig');
19
+ const OAUTH_TOKEN = new InjectionToken('OAuthToken');
20
+ var OAuthType;
21
+ (function (OAuthType) {
22
+ OAuthType["RESOURCE"] = "password";
23
+ OAuthType["AUTHORIZATION_CODE"] = "code";
24
+ OAuthType["IMPLICIT"] = "token";
25
+ OAuthType["CLIENT_CREDENTIAL"] = "client_credentials";
26
+ })(OAuthType || (OAuthType = {}));
27
+ var OAuthStatus;
28
+ (function (OAuthStatus) {
29
+ OAuthStatus["NOT_AUTHORIZED"] = "NOT_AUTHORIZED";
30
+ OAuthStatus["AUTHORIZED"] = "AUTHORIZED";
31
+ OAuthStatus["DENIED"] = "DENIED";
32
+ })(OAuthStatus || (OAuthStatus = {}));
33
+
34
+ const arrToString = (buf) => buf.reduce((s, b) => s + String.fromCharCode(b), '');
35
+ const base64url = (str) => btoa(str)
36
+ .replace(/\+/g, '-')
37
+ .replace(/\//g, '_')
38
+ .replace(/=/g, '');
39
+ const randomString = (length = 48) => {
40
+ const buff = arrToString(crypto.getRandomValues(new Uint8Array(length * 2)));
41
+ return base64url(buff).substring(0, length);
42
+ };
43
+ const pkce = (value) => __awaiter(void 0, void 0, void 0, function* () {
44
+ const buff = yield crypto.subtle.digest('SHA-256', new TextEncoder().encode(value));
45
+ return base64url(arrToString(new Uint8Array(buff)));
46
+ });
47
+ const REQUEST_HEADER = new HttpHeaders({ 'Content-Type': 'application/x-www-form-urlencoded' });
48
+ const parseOauthUri = (hash) => {
49
+ const regex = /([^&=]+)=([^&]*)/g;
50
+ const params = {};
51
+ let m;
52
+ // tslint:disable-next-line:no-conditional-assignment
53
+ while ((m = regex.exec(hash)) !== null) {
54
+ params[decodeURIComponent(m[1])] = decodeURIComponent(m[2]);
55
+ }
56
+ if (Object.keys(params).length) {
57
+ return params;
58
+ }
59
+ return null;
60
+ };
61
+ const jwt = (token) => JSON.parse(atob(token.split('.')[1]));
62
+ class OAuthService {
63
+ constructor(http, zone, authConfig, location, locationService) {
64
+ this.http = http;
65
+ this.zone = zone;
66
+ this.authConfig = authConfig;
67
+ this.location = location;
68
+ this.locationService = locationService;
69
+ this._token = null;
70
+ this._status = OAuthStatus.NOT_AUTHORIZED;
71
+ this.state$ = new ReplaySubject(1);
72
+ this.status$ = new ReplaySubject(1);
73
+ this.userInfo$ = this.status$.pipe(filter(s => s === OAuthStatus.AUTHORIZED), map(() => {
74
+ const { config } = this.authConfig;
75
+ return config.userPath;
76
+ }), filter(p => !!p), switchMap(path => this.http.get(path)), shareReplay());
77
+ setTimeout(() => this.init()); // decouple for http interceptor
78
+ }
79
+ /**
80
+ * Get the oauth config for initialize. If OpenId with issuerPath is configured then configure from server openid configuration.
81
+ * @protected
82
+ */
83
+ get config$() {
84
+ let { config } = this.authConfig;
85
+ if (config && config.clientId) {
86
+ const { issuerPath, scope } = config;
87
+ if (issuerPath) {
88
+ return this.http.get(`${issuerPath}/.well-known/openid-configuration`).pipe(tap(v => this.set(this.type, Object.assign(Object.assign(Object.assign(Object.assign(Object.assign(Object.assign(Object.assign({}, v.authorization_endpoint && { authorizePath: v.authorization_endpoint } || {}), v.token_endpoint && { tokenPath: v.token_endpoint } || {}), v.revocation_endpoint && { revokePath: v.revocation_endpoint } || {}), v.code_challenge_methods_supported && { pkce: v.code_challenge_methods_supported.indexOf('S256') > -1 } || {}), v.userinfo_endpoint && { userPath: v.userinfo_endpoint } || {}), v.introspection_endpoint && { introspectionPath: v.introspection_endpoint } || {}), scope && {} || { scope: 'openid' }))), map(() => this.authConfig.config));
89
+ }
90
+ return of(config);
91
+ }
92
+ console.warn('clientId is missing in oauth config');
93
+ return EMPTY;
94
+ }
95
+ /**
96
+ * Init. Will check the url implicit or authorization flow or existing saved token.
97
+ * @protected
98
+ */
99
+ init() {
100
+ const { hash, search, origin, pathname } = this.location;
101
+ const isImplicitRedirect = hash && /(access_token=)|(error=)/.test(hash);
102
+ const isAuthCodeRedirect = search && /(code=)|(error=)/.test(search);
103
+ const { storageKey } = this.authConfig;
104
+ const savedToken = storageKey && this.authConfig.storage && this.authConfig.storage[storageKey] &&
105
+ JSON.parse(this.authConfig.storage[storageKey]);
106
+ this.config$.subscribe(config => {
107
+ if (isImplicitRedirect) {
108
+ const parameters = parseOauthUri(hash.substr(1));
109
+ this.token = parameters;
110
+ this.status = this.checkResponse(savedToken, parameters) && OAuthStatus.AUTHORIZED || OAuthStatus.DENIED;
111
+ }
112
+ else if (isAuthCodeRedirect) {
113
+ const parameters = parseOauthUri(search.substr(1));
114
+ if (!this.checkResponse(savedToken, parameters)) {
115
+ this.token = parameters;
116
+ this.status = OAuthStatus.DENIED;
117
+ }
118
+ else {
119
+ const newParametersString = this.getCleanedUnSearchParameters();
120
+ const { clientId, clientSecret, tokenPath, scope } = config;
121
+ const codeVerifier = savedToken && savedToken.codeVerifier;
122
+ this.http.post(tokenPath, new HttpParams({
123
+ fromObject: Object.assign(Object.assign(Object.assign(Object.assign({ code: parameters === null || parameters === void 0 ? void 0 : parameters.code, client_id: clientId }, clientSecret && { client_secret: clientSecret } || {}), { redirect_uri: `${origin}${pathname}${newParametersString}`, grant_type: 'authorization_code' }), scope && { scope } || {}), codeVerifier && { code_verifier: codeVerifier } || {})
124
+ }), { headers: REQUEST_HEADER }).pipe(catchError((err) => {
125
+ this.token = err;
126
+ this.status = OAuthStatus.DENIED;
127
+ this.locationService.replaceState(`${pathname}${newParametersString}`);
128
+ return EMPTY;
129
+ })).subscribe(token => {
130
+ this.token = token;
131
+ this.status = OAuthStatus.AUTHORIZED;
132
+ this.locationService.replaceState(`${pathname}${newParametersString}`);
133
+ });
134
+ }
135
+ }
136
+ else if (savedToken) {
137
+ this._token = savedToken;
138
+ const { access_token, refresh_token, error } = savedToken;
139
+ if (access_token) {
140
+ if (refresh_token) { // force refresh since might be a manual page refresh
141
+ this.refreshToken();
142
+ }
143
+ else {
144
+ this.status = OAuthStatus.AUTHORIZED;
145
+ }
146
+ }
147
+ else {
148
+ this.status = error && OAuthStatus.DENIED || OAuthStatus.NOT_AUTHORIZED;
149
+ }
150
+ }
151
+ else {
152
+ this.status = OAuthStatus.NOT_AUTHORIZED;
153
+ }
154
+ });
155
+ }
156
+ login(parameters) {
157
+ return __awaiter(this, void 0, void 0, function* () {
158
+ if (this.isResourceType(parameters)) {
159
+ this.resourceLogin(parameters);
160
+ }
161
+ else if (this.isAuthorizationCodeType(parameters)) {
162
+ yield this.authorizationCodeLogin(parameters);
163
+ }
164
+ else if (this.isImplicitType(parameters)) {
165
+ yield this.implicitLogin(parameters);
166
+ }
167
+ else if (this.isClientCredentialType()) {
168
+ this.clientCredentialLogin();
169
+ }
170
+ });
171
+ }
172
+ logout() {
173
+ this.revoke();
174
+ this.token = null;
175
+ this.status = OAuthStatus.NOT_AUTHORIZED;
176
+ }
177
+ revoke() {
178
+ const { revokePath, clientId, clientSecret } = this.authConfig.config;
179
+ if (revokePath) {
180
+ const { access_token, refresh_token } = this.token;
181
+ const toRevoke = [];
182
+ if (access_token) {
183
+ toRevoke.push(Object.assign(Object.assign(Object.assign({}, clientId && { client_id: clientId } || {}), clientSecret && { client_secret: clientSecret } || {}), { token: access_token, token_type_hint: 'access_token' }));
184
+ }
185
+ if (refresh_token) {
186
+ toRevoke.push(Object.assign(Object.assign(Object.assign({}, clientId && { client_id: clientId } || {}), clientSecret && { client_secret: clientSecret } || {}), { token: refresh_token, token_type_hint: 'refresh_token' }));
187
+ }
188
+ from(toRevoke).pipe(concatMap(o => of(o).pipe(delay(300))), // space request to avoid cancellation
189
+ switchMap(o => this.http.post(revokePath, new HttpParams({ fromObject: o })))).subscribe(noop);
190
+ }
191
+ }
192
+ get status() {
193
+ return this._status;
194
+ }
195
+ set status(status) {
196
+ this._status = status;
197
+ this.status$.next(status);
198
+ }
199
+ set(type, config) {
200
+ this.authConfig.type = type;
201
+ if (config) {
202
+ this.authConfig.config = Object.assign(Object.assign({}, this.authConfig.config), config);
203
+ }
204
+ }
205
+ get type() {
206
+ return this.authConfig.type;
207
+ }
208
+ get ignorePaths() {
209
+ return this.authConfig.ignorePaths || [];
210
+ }
211
+ resourceLogin(parameters) {
212
+ const { clientId, clientSecret, tokenPath, scope } = this.authConfig.config;
213
+ const { username, password } = parameters;
214
+ this.http.post(tokenPath, new HttpParams({
215
+ fromObject: Object.assign(Object.assign(Object.assign(Object.assign({ client_id: clientId }, clientSecret && { client_secret: clientSecret } || {}), { grant_type: OAuthType.RESOURCE }), scope && { scope } || {}), { username,
216
+ password })
217
+ }), { headers: REQUEST_HEADER }).pipe(catchError(err => {
218
+ this.token = err;
219
+ this.status = OAuthStatus.DENIED;
220
+ return EMPTY;
221
+ })).subscribe(params => {
222
+ this.token = params;
223
+ this.status = OAuthStatus.AUTHORIZED;
224
+ });
225
+ }
226
+ authorizationCodeLogin(parameters) {
227
+ return __awaiter(this, void 0, void 0, function* () {
228
+ const authUrl = yield this.toAuthorizationUrl(parameters, OAuthType.AUTHORIZATION_CODE);
229
+ this.location.replace(authUrl);
230
+ });
231
+ }
232
+ implicitLogin(parameters) {
233
+ return __awaiter(this, void 0, void 0, function* () {
234
+ const authUrl = yield this.toAuthorizationUrl(parameters, OAuthType.IMPLICIT);
235
+ this.location.replace(authUrl);
236
+ });
237
+ }
238
+ clientCredentialLogin() {
239
+ const { clientId, clientSecret, tokenPath, scope } = this.authConfig.config;
240
+ this.http.post(tokenPath, new HttpParams({
241
+ fromObject: Object.assign({ client_id: clientId, client_secret: clientSecret, grant_type: OAuthType.CLIENT_CREDENTIAL }, scope ? { scope } : {})
242
+ }), { headers: REQUEST_HEADER }).pipe(catchError(() => {
243
+ this.token = null;
244
+ this.status = OAuthStatus.DENIED;
245
+ return EMPTY;
246
+ })).subscribe(params => {
247
+ this.token = params;
248
+ this.status = OAuthStatus.AUTHORIZED;
249
+ });
250
+ }
251
+ isClientCredentialType() {
252
+ return this.authConfig.type === OAuthType.CLIENT_CREDENTIAL;
253
+ }
254
+ isResourceType(parameters) {
255
+ return this.authConfig.type === OAuthType.RESOURCE && !!(parameters === null || parameters === void 0 ? void 0 : parameters.password);
256
+ }
257
+ isImplicitType(parameters) {
258
+ return this.authConfig.type === OAuthType.IMPLICIT && !!(parameters === null || parameters === void 0 ? void 0 : parameters.redirectUri);
259
+ }
260
+ isAuthorizationCodeType(parameters) {
261
+ return this.authConfig.type === OAuthType.AUTHORIZATION_CODE && !!(parameters === null || parameters === void 0 ? void 0 : parameters.redirectUri);
262
+ }
263
+ toAuthorizationUrl(parameters, responseType) {
264
+ return __awaiter(this, void 0, void 0, function* () {
265
+ const { config } = this.authConfig;
266
+ let authorizationUrl = `${config.authorizePath}`;
267
+ authorizationUrl += config.authorizePath.includes('?') && '&' || '?';
268
+ authorizationUrl += `client_id=${config.clientId}`;
269
+ authorizationUrl += `&redirect_uri=${encodeURIComponent(parameters.redirectUri)}`;
270
+ authorizationUrl += `&response_type=${responseType}`;
271
+ authorizationUrl += `&scope=${encodeURIComponent(config.scope || '')}`;
272
+ authorizationUrl += `&state=${encodeURIComponent(parameters.state || '')}`;
273
+ return `${authorizationUrl}${this.generateNonce(config)}${yield this.generateCodeChallenge(config)}`;
274
+ });
275
+ }
276
+ generateCodeChallenge(config) {
277
+ return __awaiter(this, void 0, void 0, function* () {
278
+ if (config.pkce) {
279
+ const codeVerifier = randomString();
280
+ this.token = Object.assign(Object.assign({}, this.token), { codeVerifier });
281
+ return `&code_challenge=${yield pkce(codeVerifier)}&code_challenge_method=S256`;
282
+ }
283
+ return '';
284
+ });
285
+ }
286
+ generateNonce(config) {
287
+ if (config && config.scope && config.scope.indexOf('openid') > -1) {
288
+ const nonce = randomString(10);
289
+ this.token = Object.assign(Object.assign({}, this.token), { nonce });
290
+ return `&nonce=${nonce}`;
291
+ }
292
+ return '';
293
+ }
294
+ checkResponse(token, parameters) {
295
+ this.emitState(parameters);
296
+ this.cleanLocationHash();
297
+ if (!parameters || parameters.error) {
298
+ return false;
299
+ }
300
+ if (token && token.nonce && parameters.access_token) {
301
+ const jwtToken = jwt(parameters.access_token);
302
+ return token.nonce === jwtToken.nonce;
303
+ }
304
+ return parameters.access_token || parameters.code;
305
+ }
306
+ set token(token) {
307
+ this._token = token;
308
+ const { storageKey } = this.authConfig;
309
+ if (token) {
310
+ // @ts-ignore
311
+ this.authConfig.storage[storageKey] = JSON.stringify(this.token);
312
+ clearTimeout(this.timer);
313
+ if (this.token && this.token.expires_in) {
314
+ this.zone.runOutsideAngular(() => {
315
+ var _a;
316
+ this.timer = setTimeout(() => {
317
+ this.zone.run(() => {
318
+ this.refreshToken();
319
+ });
320
+ }, Number((_a = this.token) === null || _a === void 0 ? void 0 : _a.expires_in) * 1000);
321
+ });
322
+ }
323
+ }
324
+ else {
325
+ // @ts-ignore
326
+ delete this.authConfig.storage[storageKey];
327
+ }
328
+ }
329
+ get token() {
330
+ return this._token;
331
+ }
332
+ refreshToken() {
333
+ const { tokenPath, clientId, clientSecret, scope } = this.authConfig.config;
334
+ const { refresh_token } = this.token;
335
+ if (tokenPath && refresh_token) {
336
+ this.http.post(tokenPath, new HttpParams({
337
+ fromObject: Object.assign(Object.assign(Object.assign({ client_id: clientId }, clientSecret && { client_secret: clientSecret } || {}), { grant_type: 'refresh_token', refresh_token }), scope && { scope } || {})
338
+ }), { headers: REQUEST_HEADER }).pipe(catchError(() => {
339
+ this.logout();
340
+ return EMPTY;
341
+ })).subscribe(params => {
342
+ this.token = Object.assign(Object.assign({}, this.token), params);
343
+ this.status = OAuthStatus.AUTHORIZED;
344
+ });
345
+ }
346
+ }
347
+ getCleanedUnSearchParameters() {
348
+ const { search } = this.location;
349
+ let searchString = search.substr(1);
350
+ const hashKeys = ['code', 'state', 'error', 'error_description', 'session_state'];
351
+ hashKeys.forEach((hashKey) => {
352
+ const re = new RegExp('&' + hashKey + '(=[^&]*)?|^' + hashKey + '(=[^&]*)?&?');
353
+ searchString = searchString.replace(re, '');
354
+ });
355
+ return searchString.length && `?${searchString}` || '';
356
+ }
357
+ cleanLocationHash() {
358
+ const { hash } = this.location;
359
+ let curHash = hash.substr(1);
360
+ const hashKeys = ['access_token', 'token_type', 'expires_in', 'scope', 'state', 'error', 'error_description', 'session_state', 'nonce'];
361
+ hashKeys.forEach((hashKey) => {
362
+ const re = new RegExp('&' + hashKey + '(=[^&]*)?|^' + hashKey + '(=[^&]*)?&?');
363
+ curHash = curHash.replace(re, '');
364
+ });
365
+ this.location.hash = curHash;
366
+ }
367
+ emitState(parameters) {
368
+ const { state } = parameters;
369
+ if (state) {
370
+ this.state$.next(state);
371
+ }
372
+ }
373
+ }
374
+ OAuthService.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "13.0.2", ngImport: i0, type: OAuthService, deps: [{ token: i1.HttpClient }, { token: i0.NgZone }, { token: OAUTH_CONFIG }, { token: LOCATION }, { token: i2.Location }], target: i0.ɵɵFactoryTarget.Injectable });
375
+ OAuthService.ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "13.0.2", ngImport: i0, type: OAuthService });
376
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "13.0.2", ngImport: i0, type: OAuthService, decorators: [{
377
+ type: Injectable
378
+ }], ctorParameters: function () {
379
+ return [{ type: i1.HttpClient }, { type: i0.NgZone }, { type: undefined, decorators: [{
380
+ type: Inject,
381
+ args: [OAUTH_CONFIG]
382
+ }] }, { type: Location, decorators: [{
383
+ type: Inject,
384
+ args: [LOCATION]
385
+ }] }, { type: i2.Location }];
386
+ } });
387
+
388
+ class OAuthInterceptor {
389
+ constructor(oauthService) {
390
+ this.oauthService = oauthService;
391
+ }
392
+ intercept(req, next) {
393
+ if (this.oauthService) {
394
+ if (!this.isPathExcepted(req)) {
395
+ const { token } = this.oauthService;
396
+ if (token && token.access_token) {
397
+ req = req.clone({
398
+ setHeaders: {
399
+ Authorization: `${token.token_type} ${token.access_token}`
400
+ }
401
+ });
402
+ }
403
+ }
404
+ return next.handle(req).pipe(catchError((err) => {
405
+ if (err.status === 401) {
406
+ this.oauthService.token = null;
407
+ this.oauthService.status = OAuthStatus.DENIED;
408
+ return EMPTY;
409
+ }
410
+ return throwError(() => new Error(err));
411
+ }));
412
+ }
413
+ else {
414
+ return next.handle(req);
415
+ }
416
+ }
417
+ isPathExcepted(req) {
418
+ const { ignorePaths } = this.oauthService;
419
+ if (ignorePaths) {
420
+ for (const ignorePath of this.oauthService.ignorePaths) {
421
+ try {
422
+ if (req.url.match(ignorePath)) {
423
+ return true;
424
+ }
425
+ }
426
+ catch (err) {
427
+ }
428
+ }
429
+ }
430
+ return false;
431
+ }
432
+ }
433
+ OAuthInterceptor.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "13.0.2", ngImport: i0, type: OAuthInterceptor, deps: [{ token: OAuthService }], target: i0.ɵɵFactoryTarget.Injectable });
434
+ OAuthInterceptor.ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "13.0.2", ngImport: i0, type: OAuthInterceptor });
435
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "13.0.2", ngImport: i0, type: OAuthInterceptor, decorators: [{
436
+ type: Injectable
437
+ }], ctorParameters: function () { return [{ type: OAuthService }]; } });
438
+
439
+ class OAuthLoginComponent {
440
+ constructor(oauthService, locationService, location) {
441
+ this.oauthService = oauthService;
442
+ this.locationService = locationService;
443
+ this.location = location;
444
+ this.subscription = new Subscription();
445
+ this._i18n = {
446
+ username: 'Username',
447
+ password: 'Password',
448
+ submit: 'Sign in',
449
+ notAuthorized: 'Sign in',
450
+ authorized: 'Welcome',
451
+ denied: 'Access Denied. Try again!'
452
+ };
453
+ this.state = '';
454
+ this.stateChange = new EventEmitter();
455
+ this.username = '';
456
+ this.password = '';
457
+ this.OAuthStatus = OAuthStatus;
458
+ this.OAuthType = OAuthType;
459
+ this.collapse = false;
460
+ this.type = this.oauthService.type;
461
+ this.state$ = this.oauthService.state$.pipe(tap(s => this.stateChange.emit(s)));
462
+ this.status$ = this.oauthService.status$.pipe(tap(s => {
463
+ if (s === OAuthStatus.AUTHORIZED && this.profileName$) {
464
+ this.subscription.add(this.profileName$.pipe(take(1)).subscribe(n => this.profileName = n));
465
+ }
466
+ else {
467
+ const { token } = this.oauthService;
468
+ const userInfo = token && token.id_token && JSON.parse(atob(token.id_token.split('.')[1])) || {};
469
+ this.profileName = userInfo.name || userInfo.username || userInfo.email || userInfo.sub || '';
470
+ }
471
+ }));
472
+ this.loginFunction = (p) => this.login(p);
473
+ this.logoutFunction = () => this.logout();
474
+ }
475
+ get i18n() {
476
+ return this._i18n;
477
+ }
478
+ set i18n(i18n) {
479
+ this._i18n = Object.assign(Object.assign({}, this._i18n), i18n);
480
+ }
481
+ set redirectUri(redirectUri) {
482
+ if (redirectUri) {
483
+ this._redirectUri = redirectUri;
484
+ }
485
+ }
486
+ get redirectUri() {
487
+ return this._redirectUri || `${this.location.origin}${this.locationService.path(true) || '/'}`;
488
+ }
489
+ ngOnDestroy() {
490
+ this.subscription.unsubscribe();
491
+ }
492
+ logout() {
493
+ this.oauthService.logout();
494
+ }
495
+ login(parameters) {
496
+ return __awaiter(this, void 0, void 0, function* () {
497
+ yield this.oauthService.login(parameters);
498
+ this.collapse = false;
499
+ });
500
+ }
501
+ toggleCollapse() {
502
+ this.collapse = !this.collapse;
503
+ }
504
+ keyboardEvent() {
505
+ this.collapse = false;
506
+ }
507
+ }
508
+ OAuthLoginComponent.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "13.0.2", ngImport: i0, type: OAuthLoginComponent, deps: [{ token: OAuthService }, { token: i2.Location }, { token: LOCATION }], target: i0.ɵɵFactoryTarget.Component });
509
+ OAuthLoginComponent.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "12.0.0", version: "13.0.2", type: OAuthLoginComponent, selector: "oauth-login", inputs: { i18n: "i18n", redirectUri: "redirectUri", state: "state", profileName$: "profileName$" }, outputs: { stateChange: "stateChange" }, host: { listeners: { "window:keydown.escape": "keyboardEvent()" } }, queries: [{ propertyName: "loginTemplate", first: true, predicate: ["login"], descendants: true }], ngImport: i0, template: "<ng-container *ngIf=\"state$ | async\"></ng-container>\r\n<ng-container *ngIf=\"loginTemplate; else defaultLogin\"\r\n [ngTemplateOutlet]=\"loginTemplate\"\r\n [ngTemplateOutletContext]=\"{login: loginFunction, logout: logoutFunction, status: status$ | async}\">\r\n</ng-container>\r\n<ng-template #defaultLogin>\r\n <ng-container *ngIf=\"status$ | async as status\">\r\n <ng-container *ngIf=\"type === OAuthType.RESOURCE; else noResource\">\r\n <div class=\"oauth dropdown text-end p-3 {{collapse ? 'show': ''}}\">\r\n <button class=\"btn btn-link p-0 dropdown-toggle\"\r\n (click)=\"status === OAuthStatus.AUTHORIZED ? logout() : toggleCollapse()\">\r\n <ng-container *ngTemplateOutlet=\"message\"></ng-container>\r\n </button>\r\n <div class=\"dropdown-menu mr-3 {{collapse ? 'show': ''}}\">\r\n <form class=\"p-3\" #form=\"ngForm\"\r\n *ngIf=\"status === OAuthStatus.NOT_AUTHORIZED || status === OAuthStatus.DENIED\"\r\n (submit)=\"login({username: username, password: password})\">\r\n <div class=\"mb-3\">\r\n <input type=\"text\"\r\n class=\"form-control\"\r\n name=\"username\"\r\n required\r\n [(ngModel)]=\"username\"\r\n [placeholder]=\"i18n.username\">\r\n </div>\r\n <div class=\"mb-3\">\r\n <input type=\"password\"\r\n class=\"form-control\"\r\n name=\"password\"\r\n required\r\n [(ngModel)]=\"password\"\r\n [placeholder]=\"i18n.password\">\r\n </div>\r\n <div class=\"text-end\">\r\n <button type=\"submit\"\r\n class=\"btn btn-primary\"\r\n [disabled]=\"form.invalid\">{{i18n.submit}}</button>\r\n </div>\r\n </form>\r\n </div>\r\n </div>\r\n </ng-container>\r\n\r\n <ng-template #noResource>\r\n <a role=\"button\" class=\"oauth\"\r\n (click)=\"status === OAuthStatus.AUTHORIZED ? logout() : login({redirectUri: redirectUri, state:state})\">\r\n <ng-container *ngTemplateOutlet=\"message\"></ng-container>\r\n </a>\r\n </ng-template>\r\n\r\n <ng-template #message>\r\n <span *ngIf=\"status === OAuthStatus.NOT_AUTHORIZED\">{{i18n.notAuthorized}}</span>\r\n <span *ngIf=\"status === OAuthStatus.AUTHORIZED\">\r\n {{i18n.authorized}}<strong>&nbsp;{{profileName}}</strong>\r\n </span>\r\n <span *ngIf=\"status === OAuthStatus.DENIED\">{{i18n.denied}}</span>\r\n </ng-template>\r\n </ng-container>\r\n</ng-template>\r\n\r\n", styles: [".oauth .dropdown-menu{left:auto;right:0;box-shadow:0 5px 10px #0003;min-width:250px}.oauth .dropdown-menu:before{content:\"\";display:inline-block;border-left:7px solid transparent;border-right:7px solid transparent;border-bottom:7px solid #ccc;border-bottom-color:#0003;position:absolute;top:-7px;left:auto;right:15px}.oauth .dropdown-menu:after{content:\"\";display:inline-block;border-left:6px solid transparent;border-right:6px solid transparent;border-bottom:6px solid #ffffff;position:absolute;top:-6px;left:auto;right:16px}\n"], directives: [{ type: i2.NgIf, selector: "[ngIf]", inputs: ["ngIf", "ngIfThen", "ngIfElse"] }, { type: i2.NgTemplateOutlet, selector: "[ngTemplateOutlet]", inputs: ["ngTemplateOutletContext", "ngTemplateOutlet"] }, { type: i3.ɵNgNoValidate, selector: "form:not([ngNoForm]):not([ngNativeValidate])" }, { type: i3.NgControlStatusGroup, selector: "[formGroupName],[formArrayName],[ngModelGroup],[formGroup],form:not([ngNoForm]),[ngForm]" }, { type: i3.NgForm, selector: "form:not([ngNoForm]):not([formGroup]),ng-form,[ngForm]", inputs: ["ngFormOptions"], outputs: ["ngSubmit"], exportAs: ["ngForm"] }, { type: i3.DefaultValueAccessor, selector: "input:not([type=checkbox])[formControlName],textarea[formControlName],input:not([type=checkbox])[formControl],textarea[formControl],input:not([type=checkbox])[ngModel],textarea[ngModel],[ngDefaultControl]" }, { type: i3.RequiredValidator, selector: ":not([type=checkbox])[required][formControlName],:not([type=checkbox])[required][formControl],:not([type=checkbox])[required][ngModel]", inputs: ["required"] }, { type: i3.NgControlStatus, selector: "[formControlName],[ngModel],[formControl]" }, { type: i3.NgModel, selector: "[ngModel]:not([formControlName]):not([formControl])", inputs: ["name", "disabled", "ngModel", "ngModelOptions"], outputs: ["ngModelChange"], exportAs: ["ngModel"] }], pipes: { "async": i2.AsyncPipe }, encapsulation: i0.ViewEncapsulation.None });
510
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "13.0.2", ngImport: i0, type: OAuthLoginComponent, decorators: [{
511
+ type: Component,
512
+ args: [{ selector: 'oauth-login', encapsulation: ViewEncapsulation.None, template: "<ng-container *ngIf=\"state$ | async\"></ng-container>\r\n<ng-container *ngIf=\"loginTemplate; else defaultLogin\"\r\n [ngTemplateOutlet]=\"loginTemplate\"\r\n [ngTemplateOutletContext]=\"{login: loginFunction, logout: logoutFunction, status: status$ | async}\">\r\n</ng-container>\r\n<ng-template #defaultLogin>\r\n <ng-container *ngIf=\"status$ | async as status\">\r\n <ng-container *ngIf=\"type === OAuthType.RESOURCE; else noResource\">\r\n <div class=\"oauth dropdown text-end p-3 {{collapse ? 'show': ''}}\">\r\n <button class=\"btn btn-link p-0 dropdown-toggle\"\r\n (click)=\"status === OAuthStatus.AUTHORIZED ? logout() : toggleCollapse()\">\r\n <ng-container *ngTemplateOutlet=\"message\"></ng-container>\r\n </button>\r\n <div class=\"dropdown-menu mr-3 {{collapse ? 'show': ''}}\">\r\n <form class=\"p-3\" #form=\"ngForm\"\r\n *ngIf=\"status === OAuthStatus.NOT_AUTHORIZED || status === OAuthStatus.DENIED\"\r\n (submit)=\"login({username: username, password: password})\">\r\n <div class=\"mb-3\">\r\n <input type=\"text\"\r\n class=\"form-control\"\r\n name=\"username\"\r\n required\r\n [(ngModel)]=\"username\"\r\n [placeholder]=\"i18n.username\">\r\n </div>\r\n <div class=\"mb-3\">\r\n <input type=\"password\"\r\n class=\"form-control\"\r\n name=\"password\"\r\n required\r\n [(ngModel)]=\"password\"\r\n [placeholder]=\"i18n.password\">\r\n </div>\r\n <div class=\"text-end\">\r\n <button type=\"submit\"\r\n class=\"btn btn-primary\"\r\n [disabled]=\"form.invalid\">{{i18n.submit}}</button>\r\n </div>\r\n </form>\r\n </div>\r\n </div>\r\n </ng-container>\r\n\r\n <ng-template #noResource>\r\n <a role=\"button\" class=\"oauth\"\r\n (click)=\"status === OAuthStatus.AUTHORIZED ? logout() : login({redirectUri: redirectUri, state:state})\">\r\n <ng-container *ngTemplateOutlet=\"message\"></ng-container>\r\n </a>\r\n </ng-template>\r\n\r\n <ng-template #message>\r\n <span *ngIf=\"status === OAuthStatus.NOT_AUTHORIZED\">{{i18n.notAuthorized}}</span>\r\n <span *ngIf=\"status === OAuthStatus.AUTHORIZED\">\r\n {{i18n.authorized}}<strong>&nbsp;{{profileName}}</strong>\r\n </span>\r\n <span *ngIf=\"status === OAuthStatus.DENIED\">{{i18n.denied}}</span>\r\n </ng-template>\r\n </ng-container>\r\n</ng-template>\r\n\r\n", styles: [".oauth .dropdown-menu{left:auto;right:0;box-shadow:0 5px 10px #0003;min-width:250px}.oauth .dropdown-menu:before{content:\"\";display:inline-block;border-left:7px solid transparent;border-right:7px solid transparent;border-bottom:7px solid #ccc;border-bottom-color:#0003;position:absolute;top:-7px;left:auto;right:15px}.oauth .dropdown-menu:after{content:\"\";display:inline-block;border-left:6px solid transparent;border-right:6px solid transparent;border-bottom:6px solid #ffffff;position:absolute;top:-6px;left:auto;right:16px}\n"] }]
513
+ }], ctorParameters: function () {
514
+ return [{ type: OAuthService }, { type: i2.Location }, { type: Location, decorators: [{
515
+ type: Inject,
516
+ args: [LOCATION]
517
+ }] }];
518
+ }, propDecorators: { i18n: [{
519
+ type: Input
520
+ }], redirectUri: [{
521
+ type: Input
522
+ }], state: [{
523
+ type: Input
524
+ }], stateChange: [{
525
+ type: Output
526
+ }], profileName$: [{
527
+ type: Input
528
+ }], loginTemplate: [{
529
+ type: ContentChild,
530
+ args: ['login', { static: false }]
531
+ }], keyboardEvent: [{
532
+ type: HostListener,
533
+ args: ['window:keydown.escape']
534
+ }] } });
535
+
536
+ const mockLocation = (serverHost, serverPath) => {
537
+ const url = new URL(serverHost && serverPath ? `${serverHost}${serverPath}` : 'http://localhost');
538
+ const { href, origin, protocol, host, hostname, port, pathname, search, hash } = url;
539
+ return {
540
+ href, origin, protocol, host, hostname, port, pathname, search, hash,
541
+ reload() {
542
+ },
543
+ assign(u) {
544
+ },
545
+ ancestorOrigins: {},
546
+ replace(u) {
547
+ }
548
+ };
549
+ };
550
+ const LocationService = {
551
+ provide: LOCATION,
552
+ useFactory(platformId, serverHost, serverPath) {
553
+ return isPlatformBrowser(platformId) ? location : mockLocation(serverHost, serverPath);
554
+ },
555
+ deps: [
556
+ PLATFORM_ID,
557
+ [new Optional(), SERVER_HOST],
558
+ [new Optional(), SERVER_PATH]
559
+ ]
560
+ };
561
+ const mockStorage = {
562
+ clear() {
563
+ },
564
+ getItem(key) {
565
+ return null;
566
+ },
567
+ key(index) {
568
+ return null;
569
+ },
570
+ removeItem(key) {
571
+ },
572
+ setItem(key, value) {
573
+ },
574
+ length: 0
575
+ };
576
+ const StorageService = {
577
+ provide: STORAGE,
578
+ useFactory(platformId) {
579
+ return isPlatformBrowser(platformId) ? localStorage : mockStorage;
580
+ },
581
+ deps: [PLATFORM_ID]
582
+ };
583
+ const OAuthInterceptorService = {
584
+ provide: HTTP_INTERCEPTORS,
585
+ useClass: OAuthInterceptor,
586
+ multi: true,
587
+ };
588
+ const defaultConfig = (storage) => {
589
+ return {
590
+ storage,
591
+ storageKey: 'token',
592
+ ignorePaths: []
593
+ };
594
+ };
595
+ class OAuthModule {
596
+ static forRoot(config) {
597
+ return {
598
+ ngModule: OAuthModule,
599
+ providers: [
600
+ LocationService,
601
+ StorageService,
602
+ OAuthService,
603
+ OAuthInterceptorService,
604
+ {
605
+ provide: OAUTH_CONFIG,
606
+ useFactory(storage) {
607
+ return Object.assign(Object.assign({}, defaultConfig(storage)), config);
608
+ },
609
+ deps: [
610
+ STORAGE
611
+ ]
612
+ }
613
+ ]
614
+ };
615
+ }
616
+ }
617
+ OAuthModule.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "13.0.2", ngImport: i0, type: OAuthModule, deps: [], target: i0.ɵɵFactoryTarget.NgModule });
618
+ OAuthModule.ɵmod = i0.ɵɵngDeclareNgModule({ minVersion: "12.0.0", version: "13.0.2", ngImport: i0, type: OAuthModule, declarations: [OAuthLoginComponent], imports: [CommonModule,
619
+ FormsModule,
620
+ HttpClientModule,
621
+ RouterModule], exports: [OAuthLoginComponent] });
622
+ OAuthModule.ɵinj = i0.ɵɵngDeclareInjector({ minVersion: "12.0.0", version: "13.0.2", ngImport: i0, type: OAuthModule, providers: [
623
+ LocationService,
624
+ StorageService,
625
+ OAuthService,
626
+ OAuthInterceptorService,
627
+ ], imports: [[
628
+ CommonModule,
629
+ FormsModule,
630
+ HttpClientModule,
631
+ RouterModule,
632
+ ]] });
633
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "13.0.2", ngImport: i0, type: OAuthModule, decorators: [{
634
+ type: NgModule,
635
+ args: [{
636
+ imports: [
637
+ CommonModule,
638
+ FormsModule,
639
+ HttpClientModule,
640
+ RouterModule,
641
+ ],
642
+ declarations: [OAuthLoginComponent],
643
+ exports: [OAuthLoginComponent],
644
+ providers: [
645
+ LocationService,
646
+ StorageService,
647
+ OAuthService,
648
+ OAuthInterceptorService,
649
+ ]
650
+ }]
651
+ }] });
652
+
653
+ /*
654
+ * Public API Surface of ngx-oauth
655
+ */
656
+
657
+ /**
658
+ * Generated bundle index. Do not edit.
659
+ */
660
+
661
+ export { LOCATION, OAUTH_CONFIG, OAUTH_TOKEN, OAuthInterceptor, OAuthLoginComponent, OAuthModule, OAuthService, OAuthStatus, OAuthType, SERVER_HOST, SERVER_PATH, STORAGE };