ngx-oauth 5.0.0 → 7.0.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.
Files changed (39) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +17 -16
  3. package/components/index.d.ts +1 -0
  4. package/components/{login/oauth-login.component.d.ts → o-auth-login.component.d.ts} +5 -6
  5. package/config/index.d.ts +17 -0
  6. package/esm2022/components/index.mjs +2 -0
  7. package/esm2022/components/o-auth-login.component.mjs +261 -0
  8. package/esm2022/config/index.mjs +32 -0
  9. package/esm2022/index.mjs +8 -0
  10. package/esm2022/models/index.mjs +16 -0
  11. package/esm2022/services/index.mjs +5 -0
  12. package/esm2022/services/o-auth-http-client.mjs +15 -0
  13. package/esm2022/services/o-auth-token.service.mjs +80 -0
  14. package/esm2022/services/o-auth.interceptor.mjs +43 -0
  15. package/esm2022/services/o-auth.service.mjs +302 -0
  16. package/fesm2022/ngx-oauth.mjs +731 -0
  17. package/fesm2022/ngx-oauth.mjs.map +1 -0
  18. package/index.d.ts +3 -5
  19. package/models/index.d.ts +3 -23
  20. package/package.json +10 -18
  21. package/services/index.d.ts +4 -0
  22. package/services/o-auth-http-client.d.ts +6 -0
  23. package/services/{token.service.d.ts → o-auth-token.service.d.ts} +5 -6
  24. package/services/o-auth.interceptor.d.ts +2 -0
  25. package/services/{oauth.service.d.ts → o-auth.service.d.ts} +5 -5
  26. package/esm2020/components/login/oauth-login.component.mjs +0 -119
  27. package/esm2020/index.mjs +0 -10
  28. package/esm2020/models/index.mjs +0 -47
  29. package/esm2020/oauth.module.mjs +0 -120
  30. package/esm2020/services/oauth.interceptor.mjs +0 -53
  31. package/esm2020/services/oauth.service.mjs +0 -300
  32. package/esm2020/services/token.service.mjs +0 -83
  33. package/fesm2015/ngx-oauth.mjs +0 -639
  34. package/fesm2015/ngx-oauth.mjs.map +0 -1
  35. package/fesm2020/ngx-oauth.mjs +0 -698
  36. package/fesm2020/ngx-oauth.mjs.map +0 -1
  37. package/oauth.module.d.ts +0 -14
  38. package/services/oauth.interceptor.d.ts +0 -13
  39. /package/{esm2020 → esm2022}/ngx-oauth.mjs +0 -0
@@ -0,0 +1,731 @@
1
+ import * as i3 from '@angular/common/http';
2
+ import { HttpHeaders, HttpClient, HttpBackend, HttpParams } from '@angular/common/http';
3
+ import * as i0 from '@angular/core';
4
+ import { InjectionToken, inject, Injectable, Component, ViewEncapsulation, Input, Output, ContentChild, HostListener } from '@angular/core';
5
+ import { BehaviorSubject, distinctUntilChanged, of, switchMap, ReplaySubject, noop, from, throwError, firstValueFrom, take } from 'rxjs';
6
+ import { shareReplay, map, catchError, filter, tap, switchMap as switchMap$1, delay, concatMap } from 'rxjs/operators';
7
+ import * as i2 from '@angular/common';
8
+ import { CommonModule } from '@angular/common';
9
+ import * as i3$1 from '@angular/forms';
10
+ import { FormsModule } from '@angular/forms';
11
+
12
+ const HEADER_APPLICATION = new HttpHeaders({ 'Content-Type': 'application/x-www-form-urlencoded' });
13
+ var OAuthType;
14
+ (function (OAuthType) {
15
+ OAuthType["RESOURCE"] = "password";
16
+ OAuthType["AUTHORIZATION_CODE"] = "code";
17
+ OAuthType["IMPLICIT"] = "token";
18
+ OAuthType["CLIENT_CREDENTIAL"] = "client_credentials";
19
+ })(OAuthType || (OAuthType = {}));
20
+ var OAuthStatus;
21
+ (function (OAuthStatus) {
22
+ OAuthStatus["NOT_AUTHORIZED"] = "NOT_AUTHORIZED";
23
+ OAuthStatus["AUTHORIZED"] = "AUTHORIZED";
24
+ OAuthStatus["DENIED"] = "DENIED";
25
+ })(OAuthStatus || (OAuthStatus = {}));
26
+
27
+ const OAUTH_CONFIG = new InjectionToken('OAuthConfig');
28
+ class OAuthConfig {
29
+ static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "17.0.4", ngImport: i0, type: OAuthConfig, deps: [], target: i0.ɵɵFactoryTarget.Injectable }); }
30
+ static { this.ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "17.0.4", ngImport: i0, type: OAuthConfig, providedIn: 'root', useFactory: () => inject(OAUTH_CONFIG).reduce((p, c) => ({ ...p, ...c }), defaultOAuthConfig) }); }
31
+ }
32
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "17.0.4", ngImport: i0, type: OAuthConfig, decorators: [{
33
+ type: Injectable,
34
+ args: [{
35
+ providedIn: 'root',
36
+ useFactory: () => inject(OAUTH_CONFIG).reduce((p, c) => ({ ...p, ...c }), defaultOAuthConfig)
37
+ }]
38
+ }] });
39
+ const provideOAuthConfig = (config = {}) => ({
40
+ provide: OAUTH_CONFIG,
41
+ useValue: config,
42
+ multi: true
43
+ });
44
+ const provideOAuthConfigFactory = (factory, deps) => ({
45
+ provide: OAUTH_CONFIG,
46
+ useFactory: factory,
47
+ deps: deps,
48
+ multi: true
49
+ });
50
+ const defaultOAuthConfig = {
51
+ storage: globalThis.localStorage,
52
+ location: globalThis.location,
53
+ storageKey: 'token',
54
+ ignorePaths: []
55
+ };
56
+
57
+ class OAuthHttpClient extends HttpClient {
58
+ static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "17.0.4", ngImport: i0, type: OAuthHttpClient, deps: null, target: i0.ɵɵFactoryTarget.Injectable }); }
59
+ static { this.ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "17.0.4", ngImport: i0, type: OAuthHttpClient, providedIn: 'root', useFactory: () => new HttpClient(inject(HttpBackend)) }); }
60
+ }
61
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "17.0.4", ngImport: i0, type: OAuthHttpClient, decorators: [{
62
+ type: Injectable,
63
+ args: [{
64
+ providedIn: 'root',
65
+ useFactory: () => new HttpClient(inject(HttpBackend)),
66
+ }]
67
+ }] });
68
+
69
+ const isExpiredToken = (token) => token && token.expires && Date.now() > token.expires || false;
70
+ class OAuthTokenService {
71
+ #token$;
72
+ constructor(authConfig, http) {
73
+ this.authConfig = authConfig;
74
+ this.http = http;
75
+ this.#token$ = new BehaviorSubject(this.saved);
76
+ this.token$ = this.#token$.pipe(distinctUntilChanged((p, c) => JSON.stringify(p || null) === JSON.stringify(c || null)), shareReplay(1), switchMap(token => !isExpiredToken(token) && of(token) || this.refreshToken(token)));
77
+ this.type$ = this.token$.pipe(map(token => token?.type), shareReplay(1));
78
+ this.accessToken$ = this.token$.pipe(map(token => token?.access_token), shareReplay(1));
79
+ }
80
+ get token() {
81
+ return this.#token$.value;
82
+ }
83
+ set token(token) {
84
+ const expiresIn = Number(token.expires_in) || 0;
85
+ const result = {
86
+ ...token,
87
+ ...expiresIn && { expires: Date.now() + expiresIn * 1000 } || {}
88
+ };
89
+ this.saved = result;
90
+ this.#token$.next(result);
91
+ }
92
+ get saved() {
93
+ const { storageKey, storage } = this.authConfig;
94
+ return storageKey && storage && storage[storageKey] && JSON.parse(storage[storageKey]) || {};
95
+ }
96
+ set saved(token) {
97
+ const { storageKey, storage } = this.authConfig;
98
+ if (storage && storageKey) {
99
+ if (token) {
100
+ storage[storageKey] = JSON.stringify(token);
101
+ }
102
+ else {
103
+ delete storage[storageKey];
104
+ }
105
+ }
106
+ }
107
+ refreshToken(token) {
108
+ const { tokenPath, clientId, clientSecret, scope } = this.authConfig.config;
109
+ const { refresh_token } = token || {};
110
+ return tokenPath && refresh_token && this.http.post(tokenPath, new HttpParams({
111
+ fromObject: {
112
+ client_id: clientId,
113
+ ...clientSecret && { client_secret: clientSecret } || {},
114
+ grant_type: 'refresh_token',
115
+ refresh_token,
116
+ ...scope && { scope } || {},
117
+ }
118
+ }), {
119
+ headers: HEADER_APPLICATION
120
+ }).pipe(catchError(() => {
121
+ this.token = {};
122
+ return of(this.token);
123
+ }), map(token => {
124
+ this.token = {
125
+ ...this.token,
126
+ ...token
127
+ };
128
+ return this.token;
129
+ })) || of(token);
130
+ }
131
+ static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "17.0.4", ngImport: i0, type: OAuthTokenService, deps: [{ token: OAuthConfig }, { token: OAuthHttpClient }], target: i0.ɵɵFactoryTarget.Injectable }); }
132
+ static { this.ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "17.0.4", ngImport: i0, type: OAuthTokenService, providedIn: 'root' }); }
133
+ }
134
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "17.0.4", ngImport: i0, type: OAuthTokenService, decorators: [{
135
+ type: Injectable,
136
+ args: [{
137
+ providedIn: 'root'
138
+ }]
139
+ }], ctorParameters: () => [{ type: OAuthConfig }, { type: OAuthHttpClient }] });
140
+
141
+ const arrToString = (buf) => buf.reduce((s, b) => s + String.fromCharCode(b), '');
142
+ const base64url = (str) => btoa(str)
143
+ .replace(/\+/g, '-')
144
+ .replace(/\//g, '_')
145
+ .replace(/=/g, '');
146
+ const randomString = (length = 48) => {
147
+ const buff = arrToString(crypto.getRandomValues(new Uint8Array(length * 2)));
148
+ return base64url(buff).substring(0, length);
149
+ };
150
+ const pkce = async (value) => {
151
+ const buff = await crypto.subtle.digest('SHA-256', new TextEncoder().encode(value));
152
+ return base64url(arrToString(new Uint8Array(buff)));
153
+ };
154
+ const parseOauthUri = (hash) => {
155
+ const regex = /([^&=]+)=([^&]*)/g;
156
+ const params = {};
157
+ let m;
158
+ // tslint:disable-next-line:no-conditional-assignment
159
+ while ((m = regex.exec(hash)) !== null) {
160
+ params[decodeURIComponent(m[1])] = decodeURIComponent(m[2]);
161
+ }
162
+ return Object.keys(params).length && params || {};
163
+ };
164
+ const jwt = (token) => JSON.parse(atob(token.split('.')[1]));
165
+ class OAuthService {
166
+ get storageKey() {
167
+ return this.authConfig.storageKey;
168
+ }
169
+ set storageKey(storageKey) {
170
+ if (storageKey) {
171
+ this.authConfig.storageKey = storageKey;
172
+ this.tokenService.token = this.tokenService.saved;
173
+ }
174
+ }
175
+ get token() {
176
+ return this.tokenService.token;
177
+ }
178
+ set token(token) {
179
+ this.tokenService.token = token;
180
+ }
181
+ get config() {
182
+ return this.authConfig.config;
183
+ }
184
+ set config(config) {
185
+ if (config) {
186
+ this.authConfig.config = {
187
+ ...this.authConfig.config,
188
+ ...config
189
+ };
190
+ }
191
+ }
192
+ constructor(authConfig, tokenService, http, locationService) {
193
+ this.authConfig = authConfig;
194
+ this.tokenService = tokenService;
195
+ this.http = http;
196
+ this.locationService = locationService;
197
+ this.state$ = new ReplaySubject(1);
198
+ this.config$ = of(this.config).pipe(filter(Boolean), filter(config => !!config?.clientId), map(config => config), switchMap$1(config => !config.issuerPath && of(config) || this.http.get(`${config.issuerPath}/.well-known/openid-configuration`).pipe(tap(v => this.config = {
199
+ ...v.authorization_endpoint && { authorizePath: v.authorization_endpoint } || {},
200
+ ...v.token_endpoint && { tokenPath: v.token_endpoint } || {},
201
+ ...v.revocation_endpoint && { revokePath: v.revocation_endpoint } || {},
202
+ ...v.code_challenge_methods_supported && { pkce: v.code_challenge_methods_supported.indexOf('S256') > -1 } || {},
203
+ ...v.userinfo_endpoint && { userPath: v.userinfo_endpoint } || {},
204
+ ...v.introspection_endpoint && { introspectionPath: v.introspection_endpoint } || {},
205
+ ...v.end_session_endpoint && { logoutPath: v.end_session_endpoint } || {},
206
+ ...{ scope: config.scope || 'openid' }
207
+ }), map(() => this.config))), shareReplay(1));
208
+ this.token$ = this.config$.pipe(tap(config => {
209
+ const { hash, search, origin, pathname } = this.authConfig.location || {};
210
+ const isImplicitRedirect = hash && /(access_token=)|(error=)/.test(hash);
211
+ const isAuthCodeRedirect = search && /(code=)|(error=)/.test(search) || hash && /(code=)|(error=)/.test(hash);
212
+ if (isImplicitRedirect) {
213
+ const parameters = parseOauthUri(hash.substring(1));
214
+ this.token = {
215
+ ...parameters,
216
+ type: OAuthType.IMPLICIT,
217
+ };
218
+ this.checkResponse(this.token, parameters);
219
+ }
220
+ else if (isAuthCodeRedirect) {
221
+ const parameters = parseOauthUri(search && search.substring(1) || hash && hash.substring(1) || '');
222
+ if (!this.checkResponse(this.token, parameters)) {
223
+ this.token = parameters;
224
+ }
225
+ else {
226
+ const newParametersString = this.getCleanedUnSearchParameters();
227
+ const { clientId, clientSecret, tokenPath, scope } = config;
228
+ const { codeVerifier } = this.token || {}; //should be set by authorizationUrl construction
229
+ this.http.post(tokenPath, new HttpParams({
230
+ fromObject: {
231
+ code: parameters?.['code'],
232
+ client_id: clientId,
233
+ ...clientSecret && { client_secret: clientSecret } || {},
234
+ redirect_uri: `${origin}${pathname}`,
235
+ grant_type: 'authorization_code',
236
+ ...scope && { scope } || {},
237
+ ...codeVerifier && { code_verifier: codeVerifier } || {}
238
+ }
239
+ }), { headers: HEADER_APPLICATION }).pipe().subscribe(token => {
240
+ this.token = {
241
+ ...token,
242
+ type: OAuthType.AUTHORIZATION_CODE
243
+ };
244
+ this.locationService.replaceState(`${pathname}${newParametersString}`);
245
+ });
246
+ }
247
+ }
248
+ }), switchMap$1(() => this.tokenService.token$), shareReplay(1));
249
+ this.status$ = this.token$.pipe(map(token => token.access_token && OAuthStatus.AUTHORIZED || token.error && OAuthStatus.DENIED || OAuthStatus.NOT_AUTHORIZED), shareReplay(1));
250
+ this.userInfo$ = this.status$.pipe(filter(s => s === OAuthStatus.AUTHORIZED), map(() => this.config.userPath), filter(Boolean), switchMap$1(path => this.getUserInfo(path)), shareReplay(1));
251
+ this.type$ = this.tokenService.type$;
252
+ this.ignorePaths = this.authConfig.ignorePaths || [];
253
+ }
254
+ async login(parameters) {
255
+ if (!!parameters && parameters.password) {
256
+ await this.resourceLogin(parameters);
257
+ }
258
+ else if (!!parameters && parameters.redirectUri && parameters.responseType) {
259
+ await this.toAuthorizationUrl(parameters);
260
+ }
261
+ else {
262
+ await this.clientCredentialLogin();
263
+ }
264
+ }
265
+ logout(useLogoutUrl) {
266
+ this.revoke();
267
+ this.token = {};
268
+ const { logoutPath, logoutRedirectUri } = this.authConfig.config;
269
+ if (useLogoutUrl && logoutPath) {
270
+ const { origin, pathname } = this.authConfig.location || {};
271
+ const currentPath = `${origin}${pathname}`;
272
+ this.authConfig.location?.replace(`${logoutPath}?post_logout_redirect_uri=${logoutRedirectUri || currentPath}`);
273
+ }
274
+ }
275
+ getUserInfo(path) {
276
+ const { userPath } = this.config;
277
+ return this.http.get(path || userPath);
278
+ }
279
+ revoke() {
280
+ const { revokePath, clientId, clientSecret } = this.authConfig.config;
281
+ if (revokePath) {
282
+ const { access_token, refresh_token } = this.token || {};
283
+ const toRevoke = [];
284
+ if (access_token) {
285
+ toRevoke.push({
286
+ ...clientId && { client_id: clientId } || {},
287
+ ...clientSecret && { client_secret: clientSecret } || {},
288
+ token: access_token,
289
+ token_type_hint: 'access_token'
290
+ });
291
+ }
292
+ if (refresh_token) {
293
+ toRevoke.push({
294
+ ...clientId && { client_id: clientId } || {},
295
+ ...clientSecret && { client_secret: clientSecret } || {},
296
+ token: refresh_token,
297
+ token_type_hint: 'refresh_token'
298
+ });
299
+ }
300
+ from(toRevoke).pipe(concatMap(o => of(o).pipe(delay(300))), // space request to avoid cancellation
301
+ switchMap$1(o => this.http.post(revokePath, new HttpParams({ fromObject: o })))).subscribe(noop);
302
+ }
303
+ }
304
+ clientCredentialLogin() {
305
+ const { clientId, clientSecret, tokenPath, scope } = this.authConfig.config;
306
+ return firstValueFrom(this.http.post(tokenPath, new HttpParams({
307
+ fromObject: {
308
+ client_id: clientId,
309
+ client_secret: clientSecret,
310
+ grant_type: OAuthType.CLIENT_CREDENTIAL,
311
+ ...scope ? { scope } : {},
312
+ }
313
+ }), { headers: HEADER_APPLICATION }).pipe(catchError((err) => {
314
+ this.token = err;
315
+ return throwError(() => err);
316
+ }), tap(params => {
317
+ this.token = {
318
+ ...params,
319
+ type: OAuthType.CLIENT_CREDENTIAL,
320
+ };
321
+ })));
322
+ }
323
+ resourceLogin(parameters) {
324
+ const { clientId, clientSecret, tokenPath, scope } = this.authConfig.config;
325
+ const { username, password } = parameters;
326
+ return firstValueFrom(this.http.post(tokenPath, new HttpParams({
327
+ fromObject: {
328
+ client_id: clientId,
329
+ ...clientSecret && { client_secret: clientSecret } || {},
330
+ grant_type: OAuthType.RESOURCE,
331
+ ...scope && { scope } || {},
332
+ username,
333
+ password
334
+ }
335
+ }), { headers: HEADER_APPLICATION }).pipe(catchError(err => {
336
+ this.token = err;
337
+ return throwError(() => err);
338
+ }), tap(params => {
339
+ this.token = {
340
+ ...params,
341
+ type: OAuthType.RESOURCE,
342
+ };
343
+ })));
344
+ }
345
+ async toAuthorizationUrl(parameters) {
346
+ const { config, location } = this.authConfig;
347
+ let authorizationUrl = `${config.authorizePath}`;
348
+ authorizationUrl += config.authorizePath.includes('?') && '&' || '?';
349
+ authorizationUrl += `client_id=${config.clientId}`;
350
+ authorizationUrl += `&redirect_uri=${encodeURIComponent(parameters.redirectUri)}`;
351
+ authorizationUrl += `&response_type=${parameters.responseType}`;
352
+ authorizationUrl += `&scope=${encodeURIComponent(config.scope || '')}`;
353
+ authorizationUrl += `&state=${encodeURIComponent(parameters.state || '')}`;
354
+ return location?.replace(`${authorizationUrl}${this.generateNonce(config)}${await this.generateCodeChallenge(config)}`);
355
+ }
356
+ async generateCodeChallenge(config) {
357
+ if (config.pkce) {
358
+ const codeVerifier = randomString();
359
+ this.token = { ...this.token, codeVerifier };
360
+ return `&code_challenge=${await pkce(codeVerifier)}&code_challenge_method=S256`;
361
+ }
362
+ return '';
363
+ }
364
+ generateNonce(config) {
365
+ if (config && config.scope && config.scope.indexOf('openid') > -1) {
366
+ const nonce = randomString(10);
367
+ this.token = { ...this.token, nonce };
368
+ return `&nonce=${nonce}`;
369
+ }
370
+ return '';
371
+ }
372
+ checkResponse(token, parameters) {
373
+ this.emitState(parameters);
374
+ this.cleanLocationHash();
375
+ if (!parameters || parameters['error']) {
376
+ return false;
377
+ }
378
+ if (token && token.nonce && parameters['access_token']) {
379
+ const jwtToken = jwt(parameters['access_token']);
380
+ return token.nonce === jwtToken.nonce;
381
+ }
382
+ return parameters['access_token'] || parameters['code'];
383
+ }
384
+ getCleanedUnSearchParameters() {
385
+ const { search } = this.authConfig.location || {};
386
+ let searchString = search && search.substring(1) || '';
387
+ const hashKeys = ['code', 'state', 'error', 'error_description', 'session_state', 'scope', 'authuser', 'prompt', 'iss'];
388
+ hashKeys.forEach(hashKey => {
389
+ const re = new RegExp('&' + hashKey + '(=[^&]*)?|^' + hashKey + '(=[^&]*)?&?');
390
+ searchString = searchString.replace(re, '');
391
+ });
392
+ return searchString.length && `?${searchString}` || '';
393
+ }
394
+ cleanLocationHash() {
395
+ if (this.authConfig.location) {
396
+ const { hash } = this.authConfig.location;
397
+ let curHash = hash && hash.substring(1) || '';
398
+ const hashKeys = [
399
+ 'access_token',
400
+ 'token_type',
401
+ 'expires_in',
402
+ 'scope',
403
+ 'state',
404
+ 'error',
405
+ 'error_description',
406
+ 'session_state',
407
+ 'nonce',
408
+ 'id_token',
409
+ 'code',
410
+ 'iss'
411
+ ];
412
+ hashKeys.forEach(hashKey => {
413
+ const re = new RegExp('&' + hashKey + '(=[^&]*)?|^' + hashKey + '(=[^&]*)?&?');
414
+ curHash = curHash.replace(re, '');
415
+ });
416
+ this.authConfig.location.hash = curHash;
417
+ }
418
+ }
419
+ emitState(parameters) {
420
+ const { state } = parameters || {};
421
+ state && this.state$.next(state);
422
+ }
423
+ static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "17.0.4", ngImport: i0, type: OAuthService, deps: [{ token: OAuthConfig }, { token: OAuthTokenService }, { token: i3.HttpClient }, { token: i2.Location }], target: i0.ɵɵFactoryTarget.Injectable }); }
424
+ static { this.ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "17.0.4", ngImport: i0, type: OAuthService, providedIn: 'root' }); }
425
+ }
426
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "17.0.4", ngImport: i0, type: OAuthService, decorators: [{
427
+ type: Injectable,
428
+ args: [{
429
+ providedIn: 'root',
430
+ }]
431
+ }], ctorParameters: () => [{ type: OAuthConfig }, { type: OAuthTokenService }, { type: i3.HttpClient }, { type: i2.Location }] });
432
+
433
+ const OAuthInterceptor = (req, next) => {
434
+ const authConfig = inject(OAuthConfig);
435
+ const tokenService = inject(OAuthTokenService);
436
+ const isPathExcepted = (req) => {
437
+ const { ignorePaths } = authConfig || {};
438
+ if (ignorePaths) {
439
+ for (const ignorePath of ignorePaths) {
440
+ try {
441
+ if (req.url.match(ignorePath)) {
442
+ return true;
443
+ }
444
+ }
445
+ catch (err) {
446
+ }
447
+ }
448
+ }
449
+ return false;
450
+ };
451
+ return isPathExcepted(req) && next(req) || tokenService.token$.pipe(take(1), map(token => {
452
+ if (token?.access_token) {
453
+ req = req.clone({
454
+ setHeaders: {
455
+ Authorization: `${token.token_type} ${token.access_token}`
456
+ }
457
+ });
458
+ }
459
+ return req;
460
+ }), switchMap(req => next(req)), catchError((err) => {
461
+ if (err.status === 401) {
462
+ tokenService.token = {
463
+ error: `${err.status}`,
464
+ error_description: err.message
465
+ };
466
+ }
467
+ return throwError(() => err);
468
+ }));
469
+ };
470
+
471
+ class OAuthLoginComponent {
472
+ #redirectUri;
473
+ #responseType;
474
+ #i18n;
475
+ get i18n() {
476
+ return this.#i18n;
477
+ }
478
+ set i18n(i18n) {
479
+ this.#i18n = {
480
+ ...this.#i18n,
481
+ ...i18n
482
+ };
483
+ }
484
+ get redirectUri() {
485
+ return this.#redirectUri || `${globalThis.location?.origin}${this.locationService.path(true) || '/'}`;
486
+ }
487
+ set redirectUri(redirectUri) {
488
+ if (redirectUri) {
489
+ this.#redirectUri = redirectUri;
490
+ }
491
+ }
492
+ set responseType(responseType) {
493
+ if (this.responseType) {
494
+ this.#responseType = responseType;
495
+ }
496
+ }
497
+ get responseType() {
498
+ return this.#responseType || this.type;
499
+ }
500
+ constructor(oauthService, locationService) {
501
+ this.oauthService = oauthService;
502
+ this.locationService = locationService;
503
+ this.#i18n = {
504
+ username: 'Username',
505
+ password: 'Password',
506
+ submit: 'Sign in',
507
+ notAuthorized: 'Sign in',
508
+ authorized: 'Welcome',
509
+ denied: 'Access Denied. Try again!'
510
+ };
511
+ this.type = OAuthType.RESOURCE;
512
+ this.useLogoutUrl = false;
513
+ this.state = '';
514
+ this.stateChange = this.oauthService.state$.asObservable();
515
+ this.loginTemplate = null;
516
+ this.username = '';
517
+ this.password = '';
518
+ this.OAuthStatus = OAuthStatus;
519
+ this.OAuthType = OAuthType;
520
+ this.collapse = false;
521
+ this.status$ = this.oauthService.status$.pipe(tap(s => {
522
+ if (s === OAuthStatus.AUTHORIZED && this.profileName$) {
523
+ this.profileName$.pipe(take(1)).subscribe(n => this.profileName = n);
524
+ }
525
+ else {
526
+ const { token } = this.oauthService;
527
+ const userInfo = token && token.id_token && JSON.parse(atob(token.id_token.split('.')[1])) || {};
528
+ this.profileName = userInfo.name || userInfo.username || userInfo.email || userInfo.sub || '';
529
+ }
530
+ }));
531
+ this.loginFunction = (p) => this.login(p);
532
+ this.logoutFunction = () => this.logout();
533
+ }
534
+ logout() {
535
+ this.oauthService.logout(this.useLogoutUrl);
536
+ }
537
+ login(parameters) {
538
+ this.collapse = false;
539
+ return this.oauthService.login(parameters);
540
+ }
541
+ toggleCollapse() {
542
+ this.collapse = !this.collapse;
543
+ }
544
+ keyboardEvent() {
545
+ this.collapse = false;
546
+ }
547
+ static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "17.0.4", ngImport: i0, type: OAuthLoginComponent, deps: [{ token: OAuthService }, { token: i2.Location }], target: i0.ɵɵFactoryTarget.Component }); }
548
+ static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "17.0.4", type: OAuthLoginComponent, isStandalone: true, selector: "oauth-login", inputs: { type: "type", i18n: "i18n", redirectUri: "redirectUri", responseType: "responseType", 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: `
549
+ @if (loginTemplate) {
550
+ <ng-container
551
+ [ngTemplateOutlet]="loginTemplate"
552
+ [ngTemplateOutletContext]="{login: loginFunction, logout: logoutFunction, status: status$ | async}">
553
+ </ng-container>
554
+ } @else {
555
+ @if (status$ | async; as status) {
556
+ @if (type === OAuthType.RESOURCE) {
557
+ <div class="oauth dropdown text-end {{collapse ? 'show': ''}}">
558
+ <button class="btn btn-link p-0 dropdown-toggle"
559
+ (click)="status === OAuthStatus.AUTHORIZED ? logout() : toggleCollapse()">
560
+ <ng-container *ngTemplateOutlet="message"></ng-container>
561
+ </button>
562
+ <div class="dropdown-menu mr-3 {{collapse ? 'show': ''}}">
563
+ @if (status === OAuthStatus.NOT_AUTHORIZED || status === OAuthStatus.DENIED) {
564
+ <form class="p-3"
565
+ #form="ngForm"
566
+ (submit)="login({username: username, password: password})">
567
+ <div class="mb-3">
568
+ <input type="text"
569
+ class="form-control"
570
+ name="username"
571
+ required
572
+ [(ngModel)]="username"
573
+ [placeholder]="i18n.username">
574
+ </div>
575
+ <div class="mb-3">
576
+ <input type="password"
577
+ class="form-control"
578
+ name="password"
579
+ required
580
+ [(ngModel)]="password"
581
+ [placeholder]="i18n.password">
582
+ </div>
583
+ <div class="text-end">
584
+ <button type="submit"
585
+ class="btn btn-primary"
586
+ [disabled]="form.invalid">{{i18n.submit}}</button>
587
+ </div>
588
+ </form>
589
+ }
590
+ </div>
591
+ </div>
592
+ } @else {
593
+ <a role="button"
594
+ class="oauth"
595
+ (click)="status === OAuthStatus.AUTHORIZED ? logout() : login({responseType: responseType, redirectUri: redirectUri, state:state})">
596
+ <ng-container *ngTemplateOutlet="message"></ng-container>
597
+ </a>
598
+ }
599
+ <ng-template #message>
600
+ @if (status === OAuthStatus.NOT_AUTHORIZED) {
601
+ <span class="not-authorized"
602
+ [innerHTML]="i18n.notAuthorized"></span>
603
+ }
604
+ @if (status === OAuthStatus.AUTHORIZED) {
605
+ <span class="authorized"
606
+ >
607
+ <span class="welcome" [innerHTML]="i18n.authorized + '&nbsp;'"></span>
608
+ <strong class="profile-name"
609
+ [innerHTML]="profileName"></strong>
610
+ </span>
611
+ }
612
+ @if (status === OAuthStatus.DENIED) {
613
+ <span class="denied"
614
+ [innerHTML]="i18n.denied"></span>
615
+ }
616
+ </ng-template>
617
+ }
618
+ }
619
+ `, isInline: true, 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"], dependencies: [{ kind: "ngmodule", type: CommonModule }, { kind: "directive", type: i2.NgTemplateOutlet, selector: "[ngTemplateOutlet]", inputs: ["ngTemplateOutletContext", "ngTemplateOutlet", "ngTemplateOutletInjector"] }, { kind: "pipe", type: i2.AsyncPipe, name: "async" }, { kind: "ngmodule", type: FormsModule }, { kind: "directive", type: i3$1.ɵNgNoValidate, selector: "form:not([ngNoForm]):not([ngNativeValidate])" }, { kind: "directive", type: i3$1.DefaultValueAccessor, selector: "input:not([type=checkbox])[formControlName],textarea[formControlName],input:not([type=checkbox])[formControl],textarea[formControl],input:not([type=checkbox])[ngModel],textarea[ngModel],[ngDefaultControl]" }, { kind: "directive", type: i3$1.NgControlStatus, selector: "[formControlName],[ngModel],[formControl]" }, { kind: "directive", type: i3$1.NgControlStatusGroup, selector: "[formGroupName],[formArrayName],[ngModelGroup],[formGroup],form:not([ngNoForm]),[ngForm]" }, { kind: "directive", type: i3$1.RequiredValidator, selector: ":not([type=checkbox])[required][formControlName],:not([type=checkbox])[required][formControl],:not([type=checkbox])[required][ngModel]", inputs: ["required"] }, { kind: "directive", type: i3$1.NgModel, selector: "[ngModel]:not([formControlName]):not([formControl])", inputs: ["name", "disabled", "ngModel", "ngModelOptions"], outputs: ["ngModelChange"], exportAs: ["ngModel"] }, { kind: "directive", type: i3$1.NgForm, selector: "form:not([ngNoForm]):not([formGroup]),ng-form,[ngForm]", inputs: ["ngFormOptions"], outputs: ["ngSubmit"], exportAs: ["ngForm"] }], encapsulation: i0.ViewEncapsulation.None }); }
620
+ }
621
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "17.0.4", ngImport: i0, type: OAuthLoginComponent, decorators: [{
622
+ type: Component,
623
+ args: [{ selector: 'oauth-login', standalone: true, imports: [
624
+ CommonModule,
625
+ FormsModule
626
+ ], template: `
627
+ @if (loginTemplate) {
628
+ <ng-container
629
+ [ngTemplateOutlet]="loginTemplate"
630
+ [ngTemplateOutletContext]="{login: loginFunction, logout: logoutFunction, status: status$ | async}">
631
+ </ng-container>
632
+ } @else {
633
+ @if (status$ | async; as status) {
634
+ @if (type === OAuthType.RESOURCE) {
635
+ <div class="oauth dropdown text-end {{collapse ? 'show': ''}}">
636
+ <button class="btn btn-link p-0 dropdown-toggle"
637
+ (click)="status === OAuthStatus.AUTHORIZED ? logout() : toggleCollapse()">
638
+ <ng-container *ngTemplateOutlet="message"></ng-container>
639
+ </button>
640
+ <div class="dropdown-menu mr-3 {{collapse ? 'show': ''}}">
641
+ @if (status === OAuthStatus.NOT_AUTHORIZED || status === OAuthStatus.DENIED) {
642
+ <form class="p-3"
643
+ #form="ngForm"
644
+ (submit)="login({username: username, password: password})">
645
+ <div class="mb-3">
646
+ <input type="text"
647
+ class="form-control"
648
+ name="username"
649
+ required
650
+ [(ngModel)]="username"
651
+ [placeholder]="i18n.username">
652
+ </div>
653
+ <div class="mb-3">
654
+ <input type="password"
655
+ class="form-control"
656
+ name="password"
657
+ required
658
+ [(ngModel)]="password"
659
+ [placeholder]="i18n.password">
660
+ </div>
661
+ <div class="text-end">
662
+ <button type="submit"
663
+ class="btn btn-primary"
664
+ [disabled]="form.invalid">{{i18n.submit}}</button>
665
+ </div>
666
+ </form>
667
+ }
668
+ </div>
669
+ </div>
670
+ } @else {
671
+ <a role="button"
672
+ class="oauth"
673
+ (click)="status === OAuthStatus.AUTHORIZED ? logout() : login({responseType: responseType, redirectUri: redirectUri, state:state})">
674
+ <ng-container *ngTemplateOutlet="message"></ng-container>
675
+ </a>
676
+ }
677
+ <ng-template #message>
678
+ @if (status === OAuthStatus.NOT_AUTHORIZED) {
679
+ <span class="not-authorized"
680
+ [innerHTML]="i18n.notAuthorized"></span>
681
+ }
682
+ @if (status === OAuthStatus.AUTHORIZED) {
683
+ <span class="authorized"
684
+ >
685
+ <span class="welcome" [innerHTML]="i18n.authorized + '&nbsp;'"></span>
686
+ <strong class="profile-name"
687
+ [innerHTML]="profileName"></strong>
688
+ </span>
689
+ }
690
+ @if (status === OAuthStatus.DENIED) {
691
+ <span class="denied"
692
+ [innerHTML]="i18n.denied"></span>
693
+ }
694
+ </ng-template>
695
+ }
696
+ }
697
+ `, encapsulation: ViewEncapsulation.None, 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"] }]
698
+ }], ctorParameters: () => [{ type: OAuthService }, { type: i2.Location }], propDecorators: { type: [{
699
+ type: Input
700
+ }], i18n: [{
701
+ type: Input
702
+ }], redirectUri: [{
703
+ type: Input
704
+ }], responseType: [{
705
+ type: Input
706
+ }], useLogoutUrl: [{
707
+ type: Input
708
+ }], state: [{
709
+ type: Input
710
+ }], stateChange: [{
711
+ type: Output
712
+ }], profileName$: [{
713
+ type: Input
714
+ }], loginTemplate: [{
715
+ type: ContentChild,
716
+ args: ['login', { static: false }]
717
+ }], keyboardEvent: [{
718
+ type: HostListener,
719
+ args: ['window:keydown.escape']
720
+ }] } });
721
+
722
+ /*
723
+ * Public API Surface of ngx-oauth
724
+ */
725
+
726
+ /**
727
+ * Generated bundle index. Do not edit.
728
+ */
729
+
730
+ export { HEADER_APPLICATION, OAUTH_CONFIG, OAuthConfig, OAuthHttpClient, OAuthInterceptor, OAuthLoginComponent, OAuthService, OAuthStatus, OAuthTokenService, OAuthType, defaultOAuthConfig, provideOAuthConfig, provideOAuthConfigFactory };
731
+ //# sourceMappingURL=ngx-oauth.mjs.map