ngx-oauth 1.1.0 → 2.1.2

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