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.
- package/README.md +79 -22
- package/esm2020/lib/components/login/oauth-login.component.mjs +30 -15
- package/esm2020/lib/models/index.mjs +2 -1
- package/esm2020/lib/oauth.module.mjs +6 -7
- package/esm2020/lib/services/oauth.interceptor.mjs +14 -11
- package/esm2020/lib/services/oauth.service.mjs +141 -88
- package/fesm2015/ngx-oauth.mjs +192 -124
- package/fesm2015/ngx-oauth.mjs.map +1 -1
- package/fesm2020/ngx-oauth.mjs +189 -118
- package/fesm2020/ngx-oauth.mjs.map +1 -1
- package/lib/components/login/oauth-login.component.d.ts +11 -6
- package/lib/models/index.d.ts +42 -1
- package/lib/services/oauth.service.d.ts +20 -5
- package/package.json +22 -1
package/fesm2020/ngx-oauth.mjs
CHANGED
|
@@ -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 {
|
|
6
|
-
import { ReplaySubject, EMPTY, from,
|
|
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.
|
|
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.
|
|
73
|
-
const isImplicitRedirect = 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
|
-
|
|
79
|
-
|
|
80
|
-
|
|
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
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
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
|
|
132
|
+
code: parameters?.code,
|
|
102
133
|
client_id: clientId,
|
|
103
134
|
...clientSecret && { client_secret: clientSecret } || {},
|
|
104
|
-
redirect_uri: `${origin}${pathname}
|
|
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 =
|
|
140
|
+
}), { headers: REQUEST_HEADER }).pipe(catchError((err) => {
|
|
141
|
+
this.token = err;
|
|
111
142
|
this.status = OAuthStatus.DENIED;
|
|
112
|
-
this.locationService.
|
|
143
|
+
this.locationService.replaceState(`${pathname}${newParametersString}`);
|
|
113
144
|
return EMPTY;
|
|
114
145
|
})).subscribe(token => {
|
|
115
146
|
this.token = token;
|
|
116
|
-
|
|
117
|
-
this.locationService.
|
|
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 (
|
|
133
|
-
this.
|
|
134
|
-
|
|
135
|
-
|
|
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.
|
|
164
|
+
this.status = error && OAuthStatus.DENIED || OAuthStatus.NOT_AUTHORIZED;
|
|
141
165
|
}
|
|
142
166
|
}
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
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)
|
|
177
|
+
await this.authorizationCodeLogin(parameters);
|
|
154
178
|
}
|
|
155
179
|
else if (this.isImplicitType(parameters)) {
|
|
156
|
-
this.implicitLogin(parameters)
|
|
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
|
|
252
|
+
...scope && { scope } || {},
|
|
223
253
|
username,
|
|
224
254
|
password
|
|
225
255
|
}
|
|
226
|
-
}), { headers: REQUEST_HEADER }).pipe(catchError(
|
|
227
|
-
this.token =
|
|
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.
|
|
267
|
+
this.location.replace(authUrl);
|
|
238
268
|
}
|
|
239
269
|
async implicitLogin(parameters) {
|
|
240
270
|
const authUrl = await this.toAuthorizationUrl(parameters, OAuthType.IMPLICIT);
|
|
241
|
-
this.
|
|
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
|
-
|
|
276
|
-
|
|
277
|
-
|
|
278
|
-
|
|
279
|
-
|
|
280
|
-
|
|
281
|
-
|
|
282
|
-
|
|
283
|
-
|
|
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
|
-
|
|
286
|
-
|
|
287
|
-
|
|
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
|
|
374
|
+
...clientSecret && { client_secret: clientSecret } || {},
|
|
322
375
|
grant_type: 'refresh_token',
|
|
323
376
|
refresh_token,
|
|
324
|
-
...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.
|
|
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
|
|
399
|
+
return searchString.length && `?${searchString}` || '';
|
|
347
400
|
}
|
|
348
401
|
cleanLocationHash() {
|
|
349
|
-
const { hash } = this.
|
|
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.
|
|
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.
|
|
366
|
-
OAuthService.ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "13.0.
|
|
367
|
-
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "13.0.
|
|
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
|
|
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
|
-
|
|
408
|
-
|
|
409
|
-
|
|
410
|
-
|
|
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.
|
|
420
|
-
OAuthInterceptor.ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "13.0.
|
|
421
|
-
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "13.0.
|
|
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
|
-
|
|
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.
|
|
486
|
-
OAuthLoginComponent.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "12.0.0", version: "13.0.
|
|
487
|
-
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "13.0.
|
|
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}} </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-
|
|
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}} </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.
|
|
595
|
-
OAuthModule.ɵmod = i0.ɵɵngDeclareNgModule({ minVersion: "12.0.0", version: "13.0.
|
|
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.
|
|
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.
|
|
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
|