ngx-oauth 2.0.0 → 2.1.0

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.
@@ -1,10 +1,10 @@
1
1
  import * as i0 from '@angular/core';
2
- import { InjectionToken, Injectable, Inject, EventEmitter, Component, Input, Output, ContentChild, HostListener, PLATFORM_ID, Optional, NgModule } from '@angular/core';
2
+ import { InjectionToken, Injectable, Inject, EventEmitter, Component, ViewEncapsulation, Input, Output, ContentChild, HostListener, PLATFORM_ID, Optional, NgModule } from '@angular/core';
3
3
  import { __awaiter } from 'tslib';
4
4
  import * as i1 from '@angular/common/http';
5
5
  import { HttpHeaders, HttpParams, HTTP_INTERCEPTORS, HttpClientModule } from '@angular/common/http';
6
- import { catchError, concatMap, delay, switchMap, tap } from 'rxjs/operators';
7
- import { ReplaySubject, EMPTY, from, of, noop, throwError, Subscription } from 'rxjs';
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
8
  import * as i2 from '@angular/common';
9
9
  import { isPlatformBrowser, CommonModule } from '@angular/common';
10
10
  import * as i3 from '@angular/forms';
@@ -16,6 +16,7 @@ const SERVER_PATH = new InjectionToken('SERVER_PATH');
16
16
  const LOCATION = new InjectionToken('Location');
17
17
  const STORAGE = new InjectionToken('Storage');
18
18
  const OAUTH_CONFIG = new InjectionToken('OAuthConfig');
19
+ const OAUTH_TOKEN = new InjectionToken('OAuthToken');
19
20
  var OAuthType;
20
21
  (function (OAuthType) {
21
22
  OAuthType["RESOURCE"] = "password";
@@ -57,100 +58,116 @@ const parseOauthUri = (hash) => {
57
58
  }
58
59
  return null;
59
60
  };
61
+ const jwt = (token) => JSON.parse(atob(token.split('.')[1]));
60
62
  class OAuthService {
61
- constructor(http, zone, authConfig, locationService) {
63
+ constructor(http, zone, authConfig, location, locationService) {
62
64
  this.http = http;
63
65
  this.zone = zone;
64
66
  this.authConfig = authConfig;
67
+ this.location = location;
65
68
  this.locationService = locationService;
66
69
  this._token = null;
67
70
  this._status = OAuthStatus.NOT_AUTHORIZED;
68
71
  this.state$ = new ReplaySubject(1);
69
72
  this.status$ = new ReplaySubject(1);
70
- this.init();
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;
71
94
  }
95
+ /**
96
+ * Init. Will check the url implicit or authorization flow or existing saved token.
97
+ * @protected
98
+ */
72
99
  init() {
73
- const { hash, search, origin, pathname } = this.locationService;
74
- const isImplicitRedirect = hash && /(#access_token=)|(#error=)/.test(hash);
100
+ const { hash, search, origin, pathname } = this.location;
101
+ const isImplicitRedirect = hash && /(access_token=)|(error=)/.test(hash);
75
102
  const isAuthCodeRedirect = search && /(code=)|(error=)/.test(search);
76
103
  const { storageKey } = this.authConfig;
77
104
  const savedToken = storageKey && this.authConfig.storage && this.authConfig.storage[storageKey] &&
78
105
  JSON.parse(this.authConfig.storage[storageKey]);
79
- if (isImplicitRedirect) {
80
- const parameters = parseOauthUri(hash.substr(1));
81
- this.emitState(parameters);
82
- this.cleanLocationHash();
83
- if (!parameters || parameters.error) {
84
- this.token = null;
85
- this.status = OAuthStatus.DENIED;
86
- }
87
- else {
106
+ this.config$.subscribe(config => {
107
+ if (isImplicitRedirect) {
108
+ const parameters = parseOauthUri(hash.substr(1));
88
109
  this.token = parameters;
89
- this.status = OAuthStatus.AUTHORIZED;
110
+ this.status = this.checkResponse(savedToken, parameters) && OAuthStatus.AUTHORIZED || OAuthStatus.DENIED;
90
111
  }
91
- }
92
- else if (isAuthCodeRedirect) {
93
- const parameters = parseOauthUri(search.substr(1));
94
- this.emitState(parameters);
95
- const newParametersString = this.getCleanedUnSearchParameters();
96
- if (parameters && parameters.code) {
97
- const { clientId, clientSecret, tokenPath, scope } = this.authConfig.config;
98
- const codeVerifier = savedToken && savedToken.codeVerifier;
99
- setTimeout(() => {
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;
100
122
  this.http.post(tokenPath, new HttpParams({
101
- fromObject: Object.assign(Object.assign(Object.assign(Object.assign({ code: parameters.code, client_id: clientId }, clientSecret && { client_secret: clientSecret } || {}), { redirect_uri: `${origin}${pathname}${newParametersString}`, grant_type: 'authorization_code' }), scope && { scope } || {}), codeVerifier && { code_verifier: codeVerifier } || {})
102
- }), { headers: REQUEST_HEADER }).pipe(catchError(() => {
103
- this.token = { error: 'error' };
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;
104
126
  this.status = OAuthStatus.DENIED;
105
- this.locationService.href = `${origin}${pathname}${newParametersString}`;
127
+ this.locationService.replaceState(`${pathname}${newParametersString}`);
106
128
  return EMPTY;
107
129
  })).subscribe(token => {
108
130
  this.token = token;
109
- // authorized event will be triggered after redirect
110
- this.locationService.href = `${origin}${pathname}${newParametersString}`;
131
+ this.status = OAuthStatus.AUTHORIZED;
132
+ this.locationService.replaceState(`${pathname}${newParametersString}`);
111
133
  });
112
- });
113
- }
114
- else {
115
- this.token = null;
116
- this.status = OAuthStatus.DENIED;
117
- }
118
- }
119
- else if (savedToken) {
120
- const { access_token, refresh_token, error } = savedToken;
121
- if (error) {
122
- this.token = null;
123
- this.status = OAuthStatus.DENIED;
134
+ }
124
135
  }
125
- else if (access_token) {
126
- this.token = savedToken;
127
- if (refresh_token) {
128
- setTimeout(() => {
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
129
141
  this.refreshToken();
130
- });
142
+ }
143
+ else {
144
+ this.status = OAuthStatus.AUTHORIZED;
145
+ }
131
146
  }
132
147
  else {
133
- this.status = OAuthStatus.AUTHORIZED;
148
+ this.status = error && OAuthStatus.DENIED || OAuthStatus.NOT_AUTHORIZED;
134
149
  }
135
150
  }
136
- }
137
- else {
138
- this.status = OAuthStatus.NOT_AUTHORIZED;
139
- }
151
+ else {
152
+ this.status = OAuthStatus.NOT_AUTHORIZED;
153
+ }
154
+ });
140
155
  }
141
156
  login(parameters) {
142
- if (this.isResourceType(parameters)) {
143
- this.resourceLogin(parameters);
144
- }
145
- else if (this.isAuthorizationCodeType(parameters)) {
146
- this.authorizationCodeLogin(parameters).then();
147
- }
148
- else if (this.isImplicitType(parameters)) {
149
- this.implicitLogin(parameters).then();
150
- }
151
- else if (this.isClientCredentialType()) {
152
- this.clientCredentialLogin();
153
- }
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
+ });
154
171
  }
155
172
  logout() {
156
173
  this.revoke();
@@ -195,10 +212,10 @@ class OAuthService {
195
212
  const { clientId, clientSecret, tokenPath, scope } = this.authConfig.config;
196
213
  const { username, password } = parameters;
197
214
  this.http.post(tokenPath, new HttpParams({
198
- fromObject: Object.assign(Object.assign({ client_id: clientId, client_secret: clientSecret, grant_type: OAuthType.RESOURCE }, scope ? { scope } : {}), { username,
215
+ fromObject: Object.assign(Object.assign(Object.assign(Object.assign({ client_id: clientId }, clientSecret && { client_secret: clientSecret } || {}), { grant_type: OAuthType.RESOURCE }), scope && { scope } || {}), { username,
199
216
  password })
200
- }), { headers: REQUEST_HEADER }).pipe(catchError(() => {
201
- this.token = null;
217
+ }), { headers: REQUEST_HEADER }).pipe(catchError(err => {
218
+ this.token = err;
202
219
  this.status = OAuthStatus.DENIED;
203
220
  return EMPTY;
204
221
  })).subscribe(params => {
@@ -209,13 +226,13 @@ class OAuthService {
209
226
  authorizationCodeLogin(parameters) {
210
227
  return __awaiter(this, void 0, void 0, function* () {
211
228
  const authUrl = yield this.toAuthorizationUrl(parameters, OAuthType.AUTHORIZATION_CODE);
212
- this.locationService.replace(authUrl);
229
+ this.location.replace(authUrl);
213
230
  });
214
231
  }
215
232
  implicitLogin(parameters) {
216
233
  return __awaiter(this, void 0, void 0, function* () {
217
234
  const authUrl = yield this.toAuthorizationUrl(parameters, OAuthType.IMPLICIT);
218
- this.locationService.replace(authUrl);
235
+ this.location.replace(authUrl);
219
236
  });
220
237
  }
221
238
  clientCredentialLogin() {
@@ -246,21 +263,46 @@ class OAuthService {
246
263
  toAuthorizationUrl(parameters, responseType) {
247
264
  return __awaiter(this, void 0, void 0, function* () {
248
265
  const { config } = this.authConfig;
249
- const appendChar = config.authorizePath.includes('?') ? '&' : '?';
250
- const clientId = `${appendChar}client_id=${config.clientId}`;
251
- const redirectUri = `&redirect_uri=${encodeURIComponent(parameters.redirectUri)}`;
252
- const responseTypeString = `&response_type=${responseType}`;
253
- const scope = `&scope=${encodeURIComponent(config.scope || '')}`;
254
- const state = `&state=${encodeURIComponent(parameters.state || '')}`;
255
- const codeVerifier = config.pkce && randomString() || null;
256
- if (codeVerifier) {
257
- this.token = { codeVerifier }; //save the code verifier before we redirect
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`;
258
282
  }
259
- const codeChallenge = codeVerifier ? `&code_challenge=${yield pkce(codeVerifier)}&code_challenge_method=S256` : '';
260
- const parametersString = `${clientId}${redirectUri}${responseTypeString}${scope}${state}${codeChallenge}`;
261
- return `${config.authorizePath}${parametersString}`;
283
+ return '';
262
284
  });
263
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
+ }
264
306
  set token(token) {
265
307
  this._token = token;
266
308
  const { storageKey } = this.authConfig;
@@ -292,7 +334,7 @@ class OAuthService {
292
334
  const { refresh_token } = this.token;
293
335
  if (tokenPath && refresh_token) {
294
336
  this.http.post(tokenPath, new HttpParams({
295
- fromObject: Object.assign(Object.assign(Object.assign({ client_id: clientId }, clientSecret ? { client_secret: clientSecret } : {}), { grant_type: 'refresh_token', refresh_token }), scope ? { scope } : {})
337
+ fromObject: Object.assign(Object.assign(Object.assign({ client_id: clientId }, clientSecret && { client_secret: clientSecret } || {}), { grant_type: 'refresh_token', refresh_token }), scope && { scope } || {})
296
338
  }), { headers: REQUEST_HEADER }).pipe(catchError(() => {
297
339
  this.logout();
298
340
  return EMPTY;
@@ -303,24 +345,24 @@ class OAuthService {
303
345
  }
304
346
  }
305
347
  getCleanedUnSearchParameters() {
306
- const { search } = this.locationService;
348
+ const { search } = this.location;
307
349
  let searchString = search.substr(1);
308
350
  const hashKeys = ['code', 'state', 'error', 'error_description', 'session_state'];
309
351
  hashKeys.forEach((hashKey) => {
310
352
  const re = new RegExp('&' + hashKey + '(=[^&]*)?|^' + hashKey + '(=[^&]*)?&?');
311
353
  searchString = searchString.replace(re, '');
312
354
  });
313
- return searchString.length ? `?${searchString}` : '';
355
+ return searchString.length && `?${searchString}` || '';
314
356
  }
315
357
  cleanLocationHash() {
316
- const { hash } = this.locationService;
358
+ const { hash } = this.location;
317
359
  let curHash = hash.substr(1);
318
- const hashKeys = ['access_token', 'token_type', 'expires_in', 'scope', 'state', 'error', 'error_description', 'session_state'];
360
+ const hashKeys = ['access_token', 'token_type', 'expires_in', 'scope', 'state', 'error', 'error_description', 'session_state', 'nonce'];
319
361
  hashKeys.forEach((hashKey) => {
320
362
  const re = new RegExp('&' + hashKey + '(=[^&]*)?|^' + hashKey + '(=[^&]*)?&?');
321
363
  curHash = curHash.replace(re, '');
322
364
  });
323
- this.locationService.hash = curHash;
365
+ this.location.hash = curHash;
324
366
  }
325
367
  emitState(parameters) {
326
368
  const { state } = parameters;
@@ -329,7 +371,7 @@ class OAuthService {
329
371
  }
330
372
  }
331
373
  }
332
- 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 }], target: i0.ɵɵFactoryTarget.Injectable });
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 });
333
375
  OAuthService.ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "13.0.2", ngImport: i0, type: OAuthService });
334
376
  i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "13.0.2", ngImport: i0, type: OAuthService, decorators: [{
335
377
  type: Injectable
@@ -340,7 +382,7 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "13.0.2", ngImpor
340
382
  }] }, { type: Location, decorators: [{
341
383
  type: Inject,
342
384
  args: [LOCATION]
343
- }] }];
385
+ }] }, { type: i2.Location }];
344
386
  } });
345
387
 
346
388
  class OAuthInterceptor {
@@ -350,7 +392,7 @@ class OAuthInterceptor {
350
392
  intercept(req, next) {
351
393
  if (this.oauthService) {
352
394
  if (!this.isPathExcepted(req)) {
353
- const token = this.oauthService.token;
395
+ const { token } = this.oauthService;
354
396
  if (token && token.access_token) {
355
397
  req = req.clone({
356
398
  setHeaders: {
@@ -373,13 +415,16 @@ class OAuthInterceptor {
373
415
  }
374
416
  }
375
417
  isPathExcepted(req) {
376
- for (const ignorePath of this.oauthService.ignorePaths) {
377
- try {
378
- if (req.url.match(ignorePath)) {
379
- return true;
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) {
380
427
  }
381
- }
382
- catch (err) {
383
428
  }
384
429
  }
385
430
  return false;
@@ -392,8 +437,9 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "13.0.2", ngImpor
392
437
  }], ctorParameters: function () { return [{ type: OAuthService }]; } });
393
438
 
394
439
  class OAuthLoginComponent {
395
- constructor(oauthService, location) {
440
+ constructor(oauthService, locationService, location) {
396
441
  this.oauthService = oauthService;
442
+ this.locationService = locationService;
397
443
  this.location = location;
398
444
  this.subscription = new Subscription();
399
445
  this._i18n = {
@@ -412,14 +458,15 @@ class OAuthLoginComponent {
412
458
  this.OAuthType = OAuthType;
413
459
  this.collapse = false;
414
460
  this.type = this.oauthService.type;
415
- this.redirectUri = this.location.href;
416
461
  this.state$ = this.oauthService.state$.pipe(tap(s => this.stateChange.emit(s)));
417
462
  this.status$ = this.oauthService.status$.pipe(tap(s => {
418
463
  if (s === OAuthStatus.AUTHORIZED && this.profileName$) {
419
- this.subscription.add(this.profileName$.subscribe(n => this.profileName = n));
464
+ this.subscription.add(this.profileName$.pipe(take(1)).subscribe(n => this.profileName = n));
420
465
  }
421
466
  else {
422
- this.profileName = '';
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 || '';
423
470
  }
424
471
  }));
425
472
  this.loginFunction = (p) => this.login(p);
@@ -431,6 +478,14 @@ class OAuthLoginComponent {
431
478
  set i18n(i18n) {
432
479
  this._i18n = Object.assign(Object.assign({}, this._i18n), i18n);
433
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
+ }
434
489
  ngOnDestroy() {
435
490
  this.subscription.unsubscribe();
436
491
  }
@@ -438,8 +493,10 @@ class OAuthLoginComponent {
438
493
  this.oauthService.logout();
439
494
  }
440
495
  login(parameters) {
441
- this.oauthService.login(parameters);
442
- this.collapse = false;
496
+ return __awaiter(this, void 0, void 0, function* () {
497
+ yield this.oauthService.login(parameters);
498
+ this.collapse = false;
499
+ });
443
500
  }
444
501
  toggleCollapse() {
445
502
  this.collapse = !this.collapse;
@@ -448,18 +505,20 @@ class OAuthLoginComponent {
448
505
  this.collapse = false;
449
506
  }
450
507
  }
451
- OAuthLoginComponent.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "13.0.2", ngImport: i0, type: OAuthLoginComponent, deps: [{ token: OAuthService }, { token: LOCATION }], target: i0.ɵɵFactoryTarget.Component });
452
- OAuthLoginComponent.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "12.0.0", version: "13.0.2", type: OAuthLoginComponent, selector: "oauth-login", inputs: { i18n: "i18n", 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-right 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=\"form-group\">\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=\"form-group\">\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-right\">\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 } });
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 });
453
510
  i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "13.0.2", ngImport: i0, type: OAuthLoginComponent, decorators: [{
454
511
  type: Component,
455
- args: [{ selector: 'oauth-login', 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-right 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=\"form-group\">\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=\"form-group\">\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-right\">\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"] }]
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"] }]
456
513
  }], ctorParameters: function () {
457
- return [{ type: OAuthService }, { type: Location, decorators: [{
514
+ return [{ type: OAuthService }, { type: i2.Location }, { type: Location, decorators: [{
458
515
  type: Inject,
459
516
  args: [LOCATION]
460
517
  }] }];
461
518
  }, propDecorators: { i18n: [{
462
519
  type: Input
520
+ }], redirectUri: [{
521
+ type: Input
463
522
  }], state: [{
464
523
  type: Input
465
524
  }], stateChange: [{
@@ -530,8 +589,7 @@ const defaultConfig = (storage) => {
530
589
  return {
531
590
  storage,
532
591
  storageKey: 'token',
533
- ignorePaths: [],
534
- pkce: false
592
+ ignorePaths: []
535
593
  };
536
594
  };
537
595
  class OAuthModule {
@@ -600,4 +658,4 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "13.0.2", ngImpor
600
658
  * Generated bundle index. Do not edit.
601
659
  */
602
660
 
603
- export { LOCATION, OAUTH_CONFIG, OAuthInterceptor, OAuthLoginComponent, OAuthModule, OAuthService, OAuthStatus, OAuthType, SERVER_HOST, SERVER_PATH, STORAGE };
661
+ export { LOCATION, OAUTH_CONFIG, OAUTH_TOKEN, OAuthInterceptor, OAuthLoginComponent, OAuthModule, OAuthService, OAuthStatus, OAuthType, SERVER_HOST, SERVER_PATH, STORAGE };