ngx-oauth 2.0.0 → 2.2.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,9 +1,9 @@
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 * as i1 from '@angular/common/http';
4
4
  import { HttpHeaders, HttpParams, HTTP_INTERCEPTORS, HttpClientModule } from '@angular/common/http';
5
- import { catchError, concatMap, delay, switchMap, tap } from 'rxjs/operators';
6
- import { ReplaySubject, EMPTY, from, of, noop, throwError, Subscription } from 'rxjs';
5
+ import { filter, map, switchMap, shareReplay, tap, catchError, concatMap, delay } from 'rxjs/operators';
6
+ import { ReplaySubject, of, EMPTY, from, noop, throwError, Subscription, take } from 'rxjs';
7
7
  import * as i2 from '@angular/common';
8
8
  import { isPlatformBrowser, CommonModule } from '@angular/common';
9
9
  import * as i3 from '@angular/forms';
@@ -15,6 +15,7 @@ const SERVER_PATH = new InjectionToken('SERVER_PATH');
15
15
  const LOCATION = new InjectionToken('Location');
16
16
  const STORAGE = new InjectionToken('Storage');
17
17
  const OAUTH_CONFIG = new InjectionToken('OAuthConfig');
18
+ const OAUTH_TOKEN = new InjectionToken('OAuthToken');
18
19
  var OAuthType;
19
20
  (function (OAuthType) {
20
21
  OAuthType["RESOURCE"] = "password";
@@ -56,113 +57,142 @@ const parseOauthUri = (hash) => {
56
57
  }
57
58
  return null;
58
59
  };
60
+ const jwt = (token) => JSON.parse(atob(token.split('.')[1]));
59
61
  class OAuthService {
60
- constructor(http, zone, authConfig, locationService) {
62
+ constructor(http, zone, authConfig, location, locationService) {
61
63
  this.http = http;
62
64
  this.zone = zone;
63
65
  this.authConfig = authConfig;
66
+ this.location = location;
64
67
  this.locationService = locationService;
65
68
  this._token = null;
66
69
  this._status = OAuthStatus.NOT_AUTHORIZED;
67
70
  this.state$ = new ReplaySubject(1);
68
71
  this.status$ = new ReplaySubject(1);
69
- this.init();
72
+ this.userInfo$ = this.status$.pipe(filter(s => s === OAuthStatus.AUTHORIZED), map(() => {
73
+ const { config } = this.authConfig;
74
+ return config.userPath;
75
+ }), filter(p => !!p), switchMap(path => this.http.get(path)), shareReplay());
76
+ setTimeout(() => this.init()); // decouple for http interceptor
77
+ }
78
+ /**
79
+ * Get the oauth config for initialize. If OpenId with issuerPath is configured then configure from server openid configuration.
80
+ * @protected
81
+ */
82
+ get config$() {
83
+ let { config } = this.authConfig;
84
+ if (config && config.clientId) {
85
+ const { issuerPath, scope } = config;
86
+ if (issuerPath) {
87
+ return this.http.get(`${issuerPath}/.well-known/openid-configuration`).pipe(tap(v => this.set(this.type, {
88
+ ...v.authorization_endpoint && { authorizePath: v.authorization_endpoint } || {},
89
+ ...v.token_endpoint && { tokenPath: v.token_endpoint } || {},
90
+ ...v.revocation_endpoint && { revokePath: v.revocation_endpoint } || {},
91
+ ...v.code_challenge_methods_supported && { pkce: v.code_challenge_methods_supported.indexOf('S256') > -1 } || {},
92
+ ...v.userinfo_endpoint && { userPath: v.userinfo_endpoint } || {},
93
+ ...v.introspection_endpoint && { introspectionPath: v.introspection_endpoint } || {},
94
+ ...v.end_session_endpoint && { logoutPath: v.end_session_endpoint } || {},
95
+ ...scope && {} || { scope: 'openid' }
96
+ })), map(() => this.authConfig.config));
97
+ }
98
+ return of(config);
99
+ }
100
+ console.warn('clientId is missing in oauth config');
101
+ return EMPTY;
70
102
  }
103
+ /**
104
+ * Init. Will check the url implicit or authorization flow or existing saved token.
105
+ * @protected
106
+ */
71
107
  init() {
72
- const { hash, search, origin, pathname } = this.locationService;
73
- const isImplicitRedirect = hash && /(#access_token=)|(#error=)/.test(hash);
108
+ const { hash, search, origin, pathname } = this.location;
109
+ const isImplicitRedirect = hash && /(access_token=)|(error=)/.test(hash);
74
110
  const isAuthCodeRedirect = search && /(code=)|(error=)/.test(search);
75
111
  const { storageKey } = this.authConfig;
76
112
  const savedToken = storageKey && this.authConfig.storage && this.authConfig.storage[storageKey] &&
77
113
  JSON.parse(this.authConfig.storage[storageKey]);
78
- if (isImplicitRedirect) {
79
- const parameters = parseOauthUri(hash.substr(1));
80
- this.emitState(parameters);
81
- this.cleanLocationHash();
82
- if (!parameters || parameters.error) {
83
- this.token = null;
84
- this.status = OAuthStatus.DENIED;
85
- }
86
- else {
114
+ this.config$.subscribe(config => {
115
+ if (isImplicitRedirect) {
116
+ const parameters = parseOauthUri(hash.substr(1));
87
117
  this.token = parameters;
88
- this.status = OAuthStatus.AUTHORIZED;
118
+ this.status = this.checkResponse(savedToken, parameters) && OAuthStatus.AUTHORIZED || OAuthStatus.DENIED;
89
119
  }
90
- }
91
- else if (isAuthCodeRedirect) {
92
- const parameters = parseOauthUri(search.substr(1));
93
- this.emitState(parameters);
94
- const newParametersString = this.getCleanedUnSearchParameters();
95
- if (parameters && parameters.code) {
96
- const { clientId, clientSecret, tokenPath, scope } = this.authConfig.config;
97
- const codeVerifier = savedToken && savedToken.codeVerifier;
98
- setTimeout(() => {
120
+ else if (isAuthCodeRedirect) {
121
+ const parameters = parseOauthUri(search.substr(1));
122
+ if (!this.checkResponse(savedToken, parameters)) {
123
+ this.token = parameters;
124
+ this.status = OAuthStatus.DENIED;
125
+ }
126
+ else {
127
+ const newParametersString = this.getCleanedUnSearchParameters();
128
+ const { clientId, clientSecret, tokenPath, scope } = config;
129
+ const codeVerifier = savedToken && savedToken.codeVerifier;
99
130
  this.http.post(tokenPath, new HttpParams({
100
131
  fromObject: {
101
- code: parameters.code,
132
+ code: parameters?.code,
102
133
  client_id: clientId,
103
134
  ...clientSecret && { client_secret: clientSecret } || {},
104
- redirect_uri: `${origin}${pathname}${newParametersString}`,
135
+ redirect_uri: `${origin}${pathname}`,
105
136
  grant_type: 'authorization_code',
106
137
  ...scope && { scope } || {},
107
138
  ...codeVerifier && { code_verifier: codeVerifier } || {}
108
139
  }
109
- }), { headers: REQUEST_HEADER }).pipe(catchError(() => {
110
- this.token = { error: 'error' };
140
+ }), { headers: REQUEST_HEADER }).pipe(catchError((err) => {
141
+ this.token = err;
111
142
  this.status = OAuthStatus.DENIED;
112
- this.locationService.href = `${origin}${pathname}${newParametersString}`;
143
+ this.locationService.replaceState(`${pathname}${newParametersString}`);
113
144
  return EMPTY;
114
145
  })).subscribe(token => {
115
146
  this.token = token;
116
- // authorized event will be triggered after redirect
117
- this.locationService.href = `${origin}${pathname}${newParametersString}`;
147
+ this.status = OAuthStatus.AUTHORIZED;
148
+ this.locationService.replaceState(`${pathname}${newParametersString}`);
118
149
  });
119
- });
120
- }
121
- else {
122
- this.token = null;
123
- this.status = OAuthStatus.DENIED;
124
- }
125
- }
126
- else if (savedToken) {
127
- const { access_token, refresh_token, error } = savedToken;
128
- if (error) {
129
- this.token = null;
130
- this.status = OAuthStatus.DENIED;
150
+ }
131
151
  }
132
- else if (access_token) {
133
- this.token = savedToken;
134
- if (refresh_token) {
135
- setTimeout(() => {
152
+ else if (savedToken) {
153
+ this._token = savedToken;
154
+ const { access_token, refresh_token, error } = savedToken;
155
+ if (access_token) {
156
+ if (refresh_token) { // force refresh since might be a manual page refresh
136
157
  this.refreshToken();
137
- });
158
+ }
159
+ else {
160
+ this.status = OAuthStatus.AUTHORIZED;
161
+ }
138
162
  }
139
163
  else {
140
- this.status = OAuthStatus.AUTHORIZED;
164
+ this.status = error && OAuthStatus.DENIED || OAuthStatus.NOT_AUTHORIZED;
141
165
  }
142
166
  }
143
- }
144
- else {
145
- this.status = OAuthStatus.NOT_AUTHORIZED;
146
- }
167
+ else {
168
+ this.status = OAuthStatus.NOT_AUTHORIZED;
169
+ }
170
+ });
147
171
  }
148
- login(parameters) {
172
+ async login(parameters) {
149
173
  if (this.isResourceType(parameters)) {
150
174
  this.resourceLogin(parameters);
151
175
  }
152
176
  else if (this.isAuthorizationCodeType(parameters)) {
153
- this.authorizationCodeLogin(parameters).then();
177
+ await this.authorizationCodeLogin(parameters);
154
178
  }
155
179
  else if (this.isImplicitType(parameters)) {
156
- this.implicitLogin(parameters).then();
180
+ await this.implicitLogin(parameters);
157
181
  }
158
182
  else if (this.isClientCredentialType()) {
159
183
  this.clientCredentialLogin();
160
184
  }
161
185
  }
162
- logout() {
186
+ logout(useLogoutUrl) {
163
187
  this.revoke();
164
188
  this.token = null;
165
189
  this.status = OAuthStatus.NOT_AUTHORIZED;
190
+ const { logoutPath, logoutRedirectUri } = this.authConfig.config;
191
+ if (useLogoutUrl && logoutPath) {
192
+ const { origin, pathname } = this.location;
193
+ const currentPath = `${origin}${pathname}`;
194
+ this.location.replace(`${logoutPath}?post_logout_redirect_uri=${logoutRedirectUri || currentPath}`);
195
+ }
166
196
  }
167
197
  revoke() {
168
198
  const { revokePath, clientId, clientSecret } = this.authConfig.config;
@@ -217,14 +247,14 @@ class OAuthService {
217
247
  this.http.post(tokenPath, new HttpParams({
218
248
  fromObject: {
219
249
  client_id: clientId,
220
- client_secret: clientSecret,
250
+ ...clientSecret && { client_secret: clientSecret } || {},
221
251
  grant_type: OAuthType.RESOURCE,
222
- ...scope ? { scope } : {},
252
+ ...scope && { scope } || {},
223
253
  username,
224
254
  password
225
255
  }
226
- }), { headers: REQUEST_HEADER }).pipe(catchError(() => {
227
- this.token = null;
256
+ }), { headers: REQUEST_HEADER }).pipe(catchError(err => {
257
+ this.token = err;
228
258
  this.status = OAuthStatus.DENIED;
229
259
  return EMPTY;
230
260
  })).subscribe(params => {
@@ -234,11 +264,11 @@ class OAuthService {
234
264
  }
235
265
  async authorizationCodeLogin(parameters) {
236
266
  const authUrl = await this.toAuthorizationUrl(parameters, OAuthType.AUTHORIZATION_CODE);
237
- this.locationService.replace(authUrl);
267
+ this.location.replace(authUrl);
238
268
  }
239
269
  async implicitLogin(parameters) {
240
270
  const authUrl = await this.toAuthorizationUrl(parameters, OAuthType.IMPLICIT);
241
- this.locationService.replace(authUrl);
271
+ this.location.replace(authUrl);
242
272
  }
243
273
  clientCredentialLogin() {
244
274
  const { clientId, clientSecret, tokenPath, scope } = this.authConfig.config;
@@ -272,19 +302,42 @@ class OAuthService {
272
302
  }
273
303
  async toAuthorizationUrl(parameters, responseType) {
274
304
  const { config } = this.authConfig;
275
- const appendChar = config.authorizePath.includes('?') ? '&' : '?';
276
- const clientId = `${appendChar}client_id=${config.clientId}`;
277
- const redirectUri = `&redirect_uri=${encodeURIComponent(parameters.redirectUri)}`;
278
- const responseTypeString = `&response_type=${responseType}`;
279
- const scope = `&scope=${encodeURIComponent(config.scope || '')}`;
280
- const state = `&state=${encodeURIComponent(parameters.state || '')}`;
281
- const codeVerifier = config.pkce && randomString() || null;
282
- if (codeVerifier) {
283
- this.token = { codeVerifier }; //save the code verifier before we redirect
305
+ let authorizationUrl = `${config.authorizePath}`;
306
+ authorizationUrl += config.authorizePath.includes('?') && '&' || '?';
307
+ authorizationUrl += `client_id=${config.clientId}`;
308
+ authorizationUrl += `&redirect_uri=${encodeURIComponent(parameters.redirectUri)}`;
309
+ authorizationUrl += `&response_type=${responseType}`;
310
+ authorizationUrl += `&scope=${encodeURIComponent(config.scope || '')}`;
311
+ authorizationUrl += `&state=${encodeURIComponent(parameters.state || '')}`;
312
+ return `${authorizationUrl}${this.generateNonce(config)}${await this.generateCodeChallenge(config)}`;
313
+ }
314
+ async generateCodeChallenge(config) {
315
+ if (config.pkce) {
316
+ const codeVerifier = randomString();
317
+ this.token = { ...this.token, codeVerifier };
318
+ return `&code_challenge=${await pkce(codeVerifier)}&code_challenge_method=S256`;
319
+ }
320
+ return '';
321
+ }
322
+ generateNonce(config) {
323
+ if (config && config.scope && config.scope.indexOf('openid') > -1) {
324
+ const nonce = randomString(10);
325
+ this.token = { ...this.token, nonce };
326
+ return `&nonce=${nonce}`;
284
327
  }
285
- const codeChallenge = codeVerifier ? `&code_challenge=${await pkce(codeVerifier)}&code_challenge_method=S256` : '';
286
- const parametersString = `${clientId}${redirectUri}${responseTypeString}${scope}${state}${codeChallenge}`;
287
- return `${config.authorizePath}${parametersString}`;
328
+ return '';
329
+ }
330
+ checkResponse(token, parameters) {
331
+ this.emitState(parameters);
332
+ this.cleanLocationHash();
333
+ if (!parameters || parameters.error) {
334
+ return false;
335
+ }
336
+ if (token && token.nonce && parameters.access_token) {
337
+ const jwtToken = jwt(parameters.access_token);
338
+ return token.nonce === jwtToken.nonce;
339
+ }
340
+ return parameters.access_token || parameters.code;
288
341
  }
289
342
  set token(token) {
290
343
  this._token = token;
@@ -318,10 +371,10 @@ class OAuthService {
318
371
  this.http.post(tokenPath, new HttpParams({
319
372
  fromObject: {
320
373
  client_id: clientId,
321
- ...clientSecret ? { client_secret: clientSecret } : {},
374
+ ...clientSecret && { client_secret: clientSecret } || {},
322
375
  grant_type: 'refresh_token',
323
376
  refresh_token,
324
- ...scope ? { scope } : {},
377
+ ...scope && { scope } || {},
325
378
  }
326
379
  }), { headers: REQUEST_HEADER }).pipe(catchError(() => {
327
380
  this.logout();
@@ -336,24 +389,24 @@ class OAuthService {
336
389
  }
337
390
  }
338
391
  getCleanedUnSearchParameters() {
339
- const { search } = this.locationService;
392
+ const { search } = this.location;
340
393
  let searchString = search.substr(1);
341
- const hashKeys = ['code', 'state', 'error', 'error_description', 'session_state'];
394
+ const hashKeys = ['code', 'state', 'error', 'error_description', 'session_state', 'scope', 'authuser', 'prompt'];
342
395
  hashKeys.forEach((hashKey) => {
343
396
  const re = new RegExp('&' + hashKey + '(=[^&]*)?|^' + hashKey + '(=[^&]*)?&?');
344
397
  searchString = searchString.replace(re, '');
345
398
  });
346
- return searchString.length ? `?${searchString}` : '';
399
+ return searchString.length && `?${searchString}` || '';
347
400
  }
348
401
  cleanLocationHash() {
349
- const { hash } = this.locationService;
402
+ const { hash } = this.location;
350
403
  let curHash = hash.substr(1);
351
- const hashKeys = ['access_token', 'token_type', 'expires_in', 'scope', 'state', 'error', 'error_description', 'session_state'];
404
+ const hashKeys = ['access_token', 'token_type', 'expires_in', 'scope', 'state', 'error', 'error_description', 'session_state', 'nonce'];
352
405
  hashKeys.forEach((hashKey) => {
353
406
  const re = new RegExp('&' + hashKey + '(=[^&]*)?|^' + hashKey + '(=[^&]*)?&?');
354
407
  curHash = curHash.replace(re, '');
355
408
  });
356
- this.locationService.hash = curHash;
409
+ this.location.hash = curHash;
357
410
  }
358
411
  emitState(parameters) {
359
412
  const { state } = parameters;
@@ -362,9 +415,9 @@ class OAuthService {
362
415
  }
363
416
  }
364
417
  }
365
- 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 });
366
- OAuthService.ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "13.0.2", ngImport: i0, type: OAuthService });
367
- i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "13.0.2", ngImport: i0, type: OAuthService, decorators: [{
418
+ OAuthService.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "13.0.3", ngImport: i0, type: OAuthService, deps: [{ token: i1.HttpClient }, { token: i0.NgZone }, { token: OAUTH_CONFIG }, { token: LOCATION }, { token: i2.Location }], target: i0.ɵɵFactoryTarget.Injectable });
419
+ OAuthService.ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "13.0.3", ngImport: i0, type: OAuthService });
420
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "13.0.3", ngImport: i0, type: OAuthService, decorators: [{
368
421
  type: Injectable
369
422
  }], ctorParameters: function () { return [{ type: i1.HttpClient }, { type: i0.NgZone }, { type: undefined, decorators: [{
370
423
  type: Inject,
@@ -372,7 +425,7 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "13.0.2", ngImpor
372
425
  }] }, { type: Location, decorators: [{
373
426
  type: Inject,
374
427
  args: [LOCATION]
375
- }] }]; } });
428
+ }] }, { type: i2.Location }]; } });
376
429
 
377
430
  class OAuthInterceptor {
378
431
  constructor(oauthService) {
@@ -381,7 +434,7 @@ class OAuthInterceptor {
381
434
  intercept(req, next) {
382
435
  if (this.oauthService) {
383
436
  if (!this.isPathExcepted(req)) {
384
- const token = this.oauthService.token;
437
+ const { token } = this.oauthService;
385
438
  if (token && token.access_token) {
386
439
  req = req.clone({
387
440
  setHeaders: {
@@ -404,27 +457,31 @@ class OAuthInterceptor {
404
457
  }
405
458
  }
406
459
  isPathExcepted(req) {
407
- for (const ignorePath of this.oauthService.ignorePaths) {
408
- try {
409
- if (req.url.match(ignorePath)) {
410
- return true;
460
+ const { ignorePaths } = this.oauthService;
461
+ if (ignorePaths) {
462
+ for (const ignorePath of this.oauthService.ignorePaths) {
463
+ try {
464
+ if (req.url.match(ignorePath)) {
465
+ return true;
466
+ }
467
+ }
468
+ catch (err) {
411
469
  }
412
- }
413
- catch (err) {
414
470
  }
415
471
  }
416
472
  return false;
417
473
  }
418
474
  }
419
- OAuthInterceptor.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "13.0.2", ngImport: i0, type: OAuthInterceptor, deps: [{ token: OAuthService }], target: i0.ɵɵFactoryTarget.Injectable });
420
- OAuthInterceptor.ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "13.0.2", ngImport: i0, type: OAuthInterceptor });
421
- i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "13.0.2", ngImport: i0, type: OAuthInterceptor, decorators: [{
475
+ OAuthInterceptor.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "13.0.3", ngImport: i0, type: OAuthInterceptor, deps: [{ token: OAuthService }], target: i0.ɵɵFactoryTarget.Injectable });
476
+ OAuthInterceptor.ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "13.0.3", ngImport: i0, type: OAuthInterceptor });
477
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "13.0.3", ngImport: i0, type: OAuthInterceptor, decorators: [{
422
478
  type: Injectable
423
479
  }], ctorParameters: function () { return [{ type: OAuthService }]; } });
424
480
 
425
481
  class OAuthLoginComponent {
426
- constructor(oauthService, location) {
482
+ constructor(oauthService, locationService, location) {
427
483
  this.oauthService = oauthService;
484
+ this.locationService = locationService;
428
485
  this.location = location;
429
486
  this.subscription = new Subscription();
430
487
  this._i18n = {
@@ -435,6 +492,7 @@ class OAuthLoginComponent {
435
492
  authorized: 'Welcome',
436
493
  denied: 'Access Denied. Try again!'
437
494
  };
495
+ this.useLogoutUrl = false;
438
496
  this.state = '';
439
497
  this.stateChange = new EventEmitter();
440
498
  this.username = '';
@@ -443,14 +501,15 @@ class OAuthLoginComponent {
443
501
  this.OAuthType = OAuthType;
444
502
  this.collapse = false;
445
503
  this.type = this.oauthService.type;
446
- this.redirectUri = this.location.href;
447
504
  this.state$ = this.oauthService.state$.pipe(tap(s => this.stateChange.emit(s)));
448
505
  this.status$ = this.oauthService.status$.pipe(tap(s => {
449
506
  if (s === OAuthStatus.AUTHORIZED && this.profileName$) {
450
- this.subscription.add(this.profileName$.subscribe(n => this.profileName = n));
507
+ this.subscription.add(this.profileName$.pipe(take(1)).subscribe(n => this.profileName = n));
451
508
  }
452
509
  else {
453
- this.profileName = '';
510
+ const { token } = this.oauthService;
511
+ const userInfo = token && token.id_token && JSON.parse(atob(token.id_token.split('.')[1])) || {};
512
+ this.profileName = userInfo.name || userInfo.username || userInfo.email || userInfo.sub || '';
454
513
  }
455
514
  }));
456
515
  this.loginFunction = (p) => this.login(p);
@@ -465,14 +524,22 @@ class OAuthLoginComponent {
465
524
  ...i18n
466
525
  };
467
526
  }
527
+ set redirectUri(redirectUri) {
528
+ if (redirectUri) {
529
+ this._redirectUri = redirectUri;
530
+ }
531
+ }
532
+ get redirectUri() {
533
+ return this._redirectUri || `${this.location.origin}${this.locationService.path(true) || '/'}`;
534
+ }
468
535
  ngOnDestroy() {
469
536
  this.subscription.unsubscribe();
470
537
  }
471
538
  logout() {
472
- this.oauthService.logout();
539
+ this.oauthService.logout(this.useLogoutUrl);
473
540
  }
474
- login(parameters) {
475
- this.oauthService.login(parameters);
541
+ async login(parameters) {
542
+ await this.oauthService.login(parameters);
476
543
  this.collapse = false;
477
544
  }
478
545
  toggleCollapse() {
@@ -482,16 +549,20 @@ class OAuthLoginComponent {
482
549
  this.collapse = false;
483
550
  }
484
551
  }
485
- 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 });
486
- 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 } });
487
- i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "13.0.2", ngImport: i0, type: OAuthLoginComponent, decorators: [{
552
+ OAuthLoginComponent.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "13.0.3", ngImport: i0, type: OAuthLoginComponent, deps: [{ token: OAuthService }, { token: i2.Location }, { token: LOCATION }], target: i0.ɵɵFactoryTarget.Component });
553
+ OAuthLoginComponent.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "12.0.0", version: "13.0.3", type: OAuthLoginComponent, selector: "oauth-login", inputs: { i18n: "i18n", redirectUri: "redirectUri", useLogoutUrl: "useLogoutUrl", 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\" class=\"not-authorized\">{{i18n.notAuthorized}}</span>\r\n <span *ngIf=\"status === OAuthStatus.AUTHORIZED\" class=\"authorized\">\r\n <span class=\"welcome\">{{i18n.authorized}}&nbsp;</span>\r\n <strong class=\"profile-name\" [innerHTML]=\"profileName\"></strong>\r\n </span>\r\n <span *ngIf=\"status === OAuthStatus.DENIED\" class=\"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 });
554
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "13.0.3", ngImport: i0, type: OAuthLoginComponent, decorators: [{
488
555
  type: Component,
489
- 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"] }]
490
- }], ctorParameters: function () { return [{ type: OAuthService }, { type: Location, decorators: [{
556
+ 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\" class=\"not-authorized\">{{i18n.notAuthorized}}</span>\r\n <span *ngIf=\"status === OAuthStatus.AUTHORIZED\" class=\"authorized\">\r\n <span class=\"welcome\">{{i18n.authorized}}&nbsp;</span>\r\n <strong class=\"profile-name\" [innerHTML]=\"profileName\"></strong>\r\n </span>\r\n <span *ngIf=\"status === OAuthStatus.DENIED\" class=\"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"] }]
557
+ }], ctorParameters: function () { return [{ type: OAuthService }, { type: i2.Location }, { type: Location, decorators: [{
491
558
  type: Inject,
492
559
  args: [LOCATION]
493
560
  }] }]; }, propDecorators: { i18n: [{
494
561
  type: Input
562
+ }], redirectUri: [{
563
+ type: Input
564
+ }], useLogoutUrl: [{
565
+ type: Input
495
566
  }], state: [{
496
567
  type: Input
497
568
  }], stateChange: [{
@@ -562,8 +633,7 @@ const defaultConfig = (storage) => {
562
633
  return {
563
634
  storage,
564
635
  storageKey: 'token',
565
- ignorePaths: [],
566
- pkce: false
636
+ ignorePaths: []
567
637
  };
568
638
  };
569
639
  class OAuthModule {
@@ -591,12 +661,12 @@ class OAuthModule {
591
661
  };
592
662
  }
593
663
  }
594
- OAuthModule.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "13.0.2", ngImport: i0, type: OAuthModule, deps: [], target: i0.ɵɵFactoryTarget.NgModule });
595
- OAuthModule.ɵmod = i0.ɵɵngDeclareNgModule({ minVersion: "12.0.0", version: "13.0.2", ngImport: i0, type: OAuthModule, declarations: [OAuthLoginComponent], imports: [CommonModule,
664
+ OAuthModule.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "13.0.3", ngImport: i0, type: OAuthModule, deps: [], target: i0.ɵɵFactoryTarget.NgModule });
665
+ OAuthModule.ɵmod = i0.ɵɵngDeclareNgModule({ minVersion: "12.0.0", version: "13.0.3", ngImport: i0, type: OAuthModule, declarations: [OAuthLoginComponent], imports: [CommonModule,
596
666
  FormsModule,
597
667
  HttpClientModule,
598
668
  RouterModule], exports: [OAuthLoginComponent] });
599
- OAuthModule.ɵinj = i0.ɵɵngDeclareInjector({ minVersion: "12.0.0", version: "13.0.2", ngImport: i0, type: OAuthModule, providers: [
669
+ OAuthModule.ɵinj = i0.ɵɵngDeclareInjector({ minVersion: "12.0.0", version: "13.0.3", ngImport: i0, type: OAuthModule, providers: [
600
670
  LocationService,
601
671
  StorageService,
602
672
  OAuthService,
@@ -607,7 +677,7 @@ OAuthModule.ɵinj = i0.ɵɵngDeclareInjector({ minVersion: "12.0.0", version: "1
607
677
  HttpClientModule,
608
678
  RouterModule,
609
679
  ]] });
610
- i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "13.0.2", ngImport: i0, type: OAuthModule, decorators: [{
680
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "13.0.3", ngImport: i0, type: OAuthModule, decorators: [{
611
681
  type: NgModule,
612
682
  args: [{
613
683
  imports: [
@@ -635,4 +705,5 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "13.0.2", ngImpor
635
705
  * Generated bundle index. Do not edit.
636
706
  */
637
707
 
638
- export { LOCATION, OAUTH_CONFIG, OAuthInterceptor, OAuthLoginComponent, OAuthModule, OAuthService, OAuthStatus, OAuthType, SERVER_HOST, SERVER_PATH, STORAGE };
708
+ export { LOCATION, OAUTH_CONFIG, OAUTH_TOKEN, OAuthInterceptor, OAuthLoginComponent, OAuthModule, OAuthService, OAuthStatus, OAuthType, SERVER_HOST, SERVER_PATH, STORAGE };
709
+ //# sourceMappingURL=ngx-oauth.mjs.map