ngx-oauth 2.0.0 → 2.1.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +33 -9
- package/esm2020/lib/components/login/oauth-login.component.mjs +25 -13
- package/esm2020/lib/models/index.mjs +2 -1
- package/esm2020/lib/oauth.module.mjs +2 -3
- package/esm2020/lib/services/oauth.interceptor.mjs +11 -8
- package/esm2020/lib/services/oauth.service.mjs +129 -83
- package/fesm2015/ngx-oauth.mjs +169 -111
- package/fesm2015/ngx-oauth.mjs.map +1 -1
- package/fesm2020/ngx-oauth.mjs +164 -104
- package/fesm2020/ngx-oauth.mjs.map +1 -1
- package/lib/components/login/oauth-login.component.d.ts +10 -6
- package/lib/models/index.d.ts +38 -1
- package/lib/services/oauth.service.d.ts +19 -4
- package/package.json +1 -1
package/fesm2015/ngx-oauth.mjs
CHANGED
|
@@ -1,10 +1,10 @@
|
|
|
1
1
|
import * as i0 from '@angular/core';
|
|
2
|
-
import { InjectionToken, Injectable, Inject, EventEmitter, Component, Input, Output, ContentChild, HostListener, PLATFORM_ID, Optional, NgModule } from '@angular/core';
|
|
2
|
+
import { InjectionToken, Injectable, Inject, EventEmitter, Component, ViewEncapsulation, Input, Output, ContentChild, HostListener, PLATFORM_ID, Optional, NgModule } from '@angular/core';
|
|
3
3
|
import { __awaiter } from 'tslib';
|
|
4
4
|
import * as i1 from '@angular/common/http';
|
|
5
5
|
import { HttpHeaders, HttpParams, HTTP_INTERCEPTORS, HttpClientModule } from '@angular/common/http';
|
|
6
|
-
import {
|
|
7
|
-
import { ReplaySubject, EMPTY, from,
|
|
6
|
+
import { filter, map, switchMap, shareReplay, tap, catchError, concatMap, delay } from 'rxjs/operators';
|
|
7
|
+
import { ReplaySubject, of, EMPTY, from, noop, throwError, Subscription, take } from 'rxjs';
|
|
8
8
|
import * as i2 from '@angular/common';
|
|
9
9
|
import { isPlatformBrowser, CommonModule } from '@angular/common';
|
|
10
10
|
import * as i3 from '@angular/forms';
|
|
@@ -16,6 +16,7 @@ const SERVER_PATH = new InjectionToken('SERVER_PATH');
|
|
|
16
16
|
const LOCATION = new InjectionToken('Location');
|
|
17
17
|
const STORAGE = new InjectionToken('Storage');
|
|
18
18
|
const OAUTH_CONFIG = new InjectionToken('OAuthConfig');
|
|
19
|
+
const OAUTH_TOKEN = new InjectionToken('OAuthToken');
|
|
19
20
|
var OAuthType;
|
|
20
21
|
(function (OAuthType) {
|
|
21
22
|
OAuthType["RESOURCE"] = "password";
|
|
@@ -57,100 +58,116 @@ const parseOauthUri = (hash) => {
|
|
|
57
58
|
}
|
|
58
59
|
return null;
|
|
59
60
|
};
|
|
61
|
+
const jwt = (token) => JSON.parse(atob(token.split('.')[1]));
|
|
60
62
|
class OAuthService {
|
|
61
|
-
constructor(http, zone, authConfig, locationService) {
|
|
63
|
+
constructor(http, zone, authConfig, location, locationService) {
|
|
62
64
|
this.http = http;
|
|
63
65
|
this.zone = zone;
|
|
64
66
|
this.authConfig = authConfig;
|
|
67
|
+
this.location = location;
|
|
65
68
|
this.locationService = locationService;
|
|
66
69
|
this._token = null;
|
|
67
70
|
this._status = OAuthStatus.NOT_AUTHORIZED;
|
|
68
71
|
this.state$ = new ReplaySubject(1);
|
|
69
72
|
this.status$ = new ReplaySubject(1);
|
|
70
|
-
this.
|
|
73
|
+
this.userInfo$ = this.status$.pipe(filter(s => s === OAuthStatus.AUTHORIZED), map(() => {
|
|
74
|
+
const { config } = this.authConfig;
|
|
75
|
+
return config.userPath;
|
|
76
|
+
}), filter(p => !!p), switchMap(path => this.http.get(path)), shareReplay());
|
|
77
|
+
setTimeout(() => this.init()); // decouple for http interceptor
|
|
78
|
+
}
|
|
79
|
+
/**
|
|
80
|
+
* Get the oauth config for initialize. If OpenId with issuerPath is configured then configure from server openid configuration.
|
|
81
|
+
* @protected
|
|
82
|
+
*/
|
|
83
|
+
get config$() {
|
|
84
|
+
let { config } = this.authConfig;
|
|
85
|
+
if (config && config.clientId) {
|
|
86
|
+
const { issuerPath, scope } = config;
|
|
87
|
+
if (issuerPath) {
|
|
88
|
+
return this.http.get(`${issuerPath}/.well-known/openid-configuration`).pipe(tap(v => this.set(this.type, Object.assign(Object.assign(Object.assign(Object.assign(Object.assign(Object.assign(Object.assign({}, v.authorization_endpoint && { authorizePath: v.authorization_endpoint } || {}), v.token_endpoint && { tokenPath: v.token_endpoint } || {}), v.revocation_endpoint && { revokePath: v.revocation_endpoint } || {}), v.code_challenge_methods_supported && { pkce: v.code_challenge_methods_supported.indexOf('S256') > -1 } || {}), v.userinfo_endpoint && { userPath: v.userinfo_endpoint } || {}), v.introspection_endpoint && { introspectionPath: v.introspection_endpoint } || {}), scope && {} || { scope: 'openid' }))), map(() => this.authConfig.config));
|
|
89
|
+
}
|
|
90
|
+
return of(config);
|
|
91
|
+
}
|
|
92
|
+
console.warn('clientId is missing in oauth config');
|
|
93
|
+
return EMPTY;
|
|
71
94
|
}
|
|
95
|
+
/**
|
|
96
|
+
* Init. Will check the url implicit or authorization flow or existing saved token.
|
|
97
|
+
* @protected
|
|
98
|
+
*/
|
|
72
99
|
init() {
|
|
73
|
-
const { hash, search, origin, pathname } = this.
|
|
74
|
-
const isImplicitRedirect = hash && /(
|
|
100
|
+
const { hash, search, origin, pathname } = this.location;
|
|
101
|
+
const isImplicitRedirect = hash && /(access_token=)|(error=)/.test(hash);
|
|
75
102
|
const isAuthCodeRedirect = search && /(code=)|(error=)/.test(search);
|
|
76
103
|
const { storageKey } = this.authConfig;
|
|
77
104
|
const savedToken = storageKey && this.authConfig.storage && this.authConfig.storage[storageKey] &&
|
|
78
105
|
JSON.parse(this.authConfig.storage[storageKey]);
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
this.cleanLocationHash();
|
|
83
|
-
if (!parameters || parameters.error) {
|
|
84
|
-
this.token = null;
|
|
85
|
-
this.status = OAuthStatus.DENIED;
|
|
86
|
-
}
|
|
87
|
-
else {
|
|
106
|
+
this.config$.subscribe(config => {
|
|
107
|
+
if (isImplicitRedirect) {
|
|
108
|
+
const parameters = parseOauthUri(hash.substr(1));
|
|
88
109
|
this.token = parameters;
|
|
89
|
-
this.status = OAuthStatus.AUTHORIZED;
|
|
110
|
+
this.status = this.checkResponse(savedToken, parameters) && OAuthStatus.AUTHORIZED || OAuthStatus.DENIED;
|
|
90
111
|
}
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
112
|
+
else if (isAuthCodeRedirect) {
|
|
113
|
+
const parameters = parseOauthUri(search.substr(1));
|
|
114
|
+
if (!this.checkResponse(savedToken, parameters)) {
|
|
115
|
+
this.token = parameters;
|
|
116
|
+
this.status = OAuthStatus.DENIED;
|
|
117
|
+
}
|
|
118
|
+
else {
|
|
119
|
+
const newParametersString = this.getCleanedUnSearchParameters();
|
|
120
|
+
const { clientId, clientSecret, tokenPath, scope } = config;
|
|
121
|
+
const codeVerifier = savedToken && savedToken.codeVerifier;
|
|
100
122
|
this.http.post(tokenPath, new HttpParams({
|
|
101
|
-
fromObject: Object.assign(Object.assign(Object.assign(Object.assign({ code: parameters.code, client_id: clientId }, clientSecret && { client_secret: clientSecret } || {}), { redirect_uri: `${origin}${pathname}${newParametersString}`, grant_type: 'authorization_code' }), scope && { scope } || {}), codeVerifier && { code_verifier: codeVerifier } || {})
|
|
102
|
-
}), { headers: REQUEST_HEADER }).pipe(catchError(() => {
|
|
103
|
-
this.token =
|
|
123
|
+
fromObject: Object.assign(Object.assign(Object.assign(Object.assign({ code: parameters === null || parameters === void 0 ? void 0 : parameters.code, client_id: clientId }, clientSecret && { client_secret: clientSecret } || {}), { redirect_uri: `${origin}${pathname}${newParametersString}`, grant_type: 'authorization_code' }), scope && { scope } || {}), codeVerifier && { code_verifier: codeVerifier } || {})
|
|
124
|
+
}), { headers: REQUEST_HEADER }).pipe(catchError((err) => {
|
|
125
|
+
this.token = err;
|
|
104
126
|
this.status = OAuthStatus.DENIED;
|
|
105
|
-
this.locationService.
|
|
127
|
+
this.locationService.replaceState(`${pathname}${newParametersString}`);
|
|
106
128
|
return EMPTY;
|
|
107
129
|
})).subscribe(token => {
|
|
108
130
|
this.token = token;
|
|
109
|
-
|
|
110
|
-
this.locationService.
|
|
131
|
+
this.status = OAuthStatus.AUTHORIZED;
|
|
132
|
+
this.locationService.replaceState(`${pathname}${newParametersString}`);
|
|
111
133
|
});
|
|
112
|
-
}
|
|
113
|
-
}
|
|
114
|
-
else {
|
|
115
|
-
this.token = null;
|
|
116
|
-
this.status = OAuthStatus.DENIED;
|
|
117
|
-
}
|
|
118
|
-
}
|
|
119
|
-
else if (savedToken) {
|
|
120
|
-
const { access_token, refresh_token, error } = savedToken;
|
|
121
|
-
if (error) {
|
|
122
|
-
this.token = null;
|
|
123
|
-
this.status = OAuthStatus.DENIED;
|
|
134
|
+
}
|
|
124
135
|
}
|
|
125
|
-
else if (
|
|
126
|
-
this.
|
|
127
|
-
|
|
128
|
-
|
|
136
|
+
else if (savedToken) {
|
|
137
|
+
this._token = savedToken;
|
|
138
|
+
const { access_token, refresh_token, error } = savedToken;
|
|
139
|
+
if (access_token) {
|
|
140
|
+
if (refresh_token) { // force refresh since might be a manual page refresh
|
|
129
141
|
this.refreshToken();
|
|
130
|
-
}
|
|
142
|
+
}
|
|
143
|
+
else {
|
|
144
|
+
this.status = OAuthStatus.AUTHORIZED;
|
|
145
|
+
}
|
|
131
146
|
}
|
|
132
147
|
else {
|
|
133
|
-
this.status = OAuthStatus.
|
|
148
|
+
this.status = error && OAuthStatus.DENIED || OAuthStatus.NOT_AUTHORIZED;
|
|
134
149
|
}
|
|
135
150
|
}
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
}
|
|
151
|
+
else {
|
|
152
|
+
this.status = OAuthStatus.NOT_AUTHORIZED;
|
|
153
|
+
}
|
|
154
|
+
});
|
|
140
155
|
}
|
|
141
156
|
login(parameters) {
|
|
142
|
-
|
|
143
|
-
this.
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
this.
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
this.
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
this.
|
|
153
|
-
|
|
157
|
+
return __awaiter(this, void 0, void 0, function* () {
|
|
158
|
+
if (this.isResourceType(parameters)) {
|
|
159
|
+
this.resourceLogin(parameters);
|
|
160
|
+
}
|
|
161
|
+
else if (this.isAuthorizationCodeType(parameters)) {
|
|
162
|
+
yield this.authorizationCodeLogin(parameters);
|
|
163
|
+
}
|
|
164
|
+
else if (this.isImplicitType(parameters)) {
|
|
165
|
+
yield this.implicitLogin(parameters);
|
|
166
|
+
}
|
|
167
|
+
else if (this.isClientCredentialType()) {
|
|
168
|
+
this.clientCredentialLogin();
|
|
169
|
+
}
|
|
170
|
+
});
|
|
154
171
|
}
|
|
155
172
|
logout() {
|
|
156
173
|
this.revoke();
|
|
@@ -195,10 +212,10 @@ class OAuthService {
|
|
|
195
212
|
const { clientId, clientSecret, tokenPath, scope } = this.authConfig.config;
|
|
196
213
|
const { username, password } = parameters;
|
|
197
214
|
this.http.post(tokenPath, new HttpParams({
|
|
198
|
-
fromObject: Object.assign(Object.assign({ client_id: clientId, client_secret: clientSecret, grant_type: OAuthType.RESOURCE }, scope
|
|
215
|
+
fromObject: Object.assign(Object.assign(Object.assign(Object.assign({ client_id: clientId }, clientSecret && { client_secret: clientSecret } || {}), { grant_type: OAuthType.RESOURCE }), scope && { scope } || {}), { username,
|
|
199
216
|
password })
|
|
200
|
-
}), { headers: REQUEST_HEADER }).pipe(catchError(
|
|
201
|
-
this.token =
|
|
217
|
+
}), { headers: REQUEST_HEADER }).pipe(catchError(err => {
|
|
218
|
+
this.token = err;
|
|
202
219
|
this.status = OAuthStatus.DENIED;
|
|
203
220
|
return EMPTY;
|
|
204
221
|
})).subscribe(params => {
|
|
@@ -209,13 +226,13 @@ class OAuthService {
|
|
|
209
226
|
authorizationCodeLogin(parameters) {
|
|
210
227
|
return __awaiter(this, void 0, void 0, function* () {
|
|
211
228
|
const authUrl = yield this.toAuthorizationUrl(parameters, OAuthType.AUTHORIZATION_CODE);
|
|
212
|
-
this.
|
|
229
|
+
this.location.replace(authUrl);
|
|
213
230
|
});
|
|
214
231
|
}
|
|
215
232
|
implicitLogin(parameters) {
|
|
216
233
|
return __awaiter(this, void 0, void 0, function* () {
|
|
217
234
|
const authUrl = yield this.toAuthorizationUrl(parameters, OAuthType.IMPLICIT);
|
|
218
|
-
this.
|
|
235
|
+
this.location.replace(authUrl);
|
|
219
236
|
});
|
|
220
237
|
}
|
|
221
238
|
clientCredentialLogin() {
|
|
@@ -246,21 +263,46 @@ class OAuthService {
|
|
|
246
263
|
toAuthorizationUrl(parameters, responseType) {
|
|
247
264
|
return __awaiter(this, void 0, void 0, function* () {
|
|
248
265
|
const { config } = this.authConfig;
|
|
249
|
-
|
|
250
|
-
|
|
251
|
-
|
|
252
|
-
|
|
253
|
-
|
|
254
|
-
|
|
255
|
-
|
|
256
|
-
|
|
257
|
-
|
|
266
|
+
let authorizationUrl = `${config.authorizePath}`;
|
|
267
|
+
authorizationUrl += config.authorizePath.includes('?') && '&' || '?';
|
|
268
|
+
authorizationUrl += `client_id=${config.clientId}`;
|
|
269
|
+
authorizationUrl += `&redirect_uri=${encodeURIComponent(parameters.redirectUri)}`;
|
|
270
|
+
authorizationUrl += `&response_type=${responseType}`;
|
|
271
|
+
authorizationUrl += `&scope=${encodeURIComponent(config.scope || '')}`;
|
|
272
|
+
authorizationUrl += `&state=${encodeURIComponent(parameters.state || '')}`;
|
|
273
|
+
return `${authorizationUrl}${this.generateNonce(config)}${yield this.generateCodeChallenge(config)}`;
|
|
274
|
+
});
|
|
275
|
+
}
|
|
276
|
+
generateCodeChallenge(config) {
|
|
277
|
+
return __awaiter(this, void 0, void 0, function* () {
|
|
278
|
+
if (config.pkce) {
|
|
279
|
+
const codeVerifier = randomString();
|
|
280
|
+
this.token = Object.assign(Object.assign({}, this.token), { codeVerifier });
|
|
281
|
+
return `&code_challenge=${yield pkce(codeVerifier)}&code_challenge_method=S256`;
|
|
258
282
|
}
|
|
259
|
-
|
|
260
|
-
const parametersString = `${clientId}${redirectUri}${responseTypeString}${scope}${state}${codeChallenge}`;
|
|
261
|
-
return `${config.authorizePath}${parametersString}`;
|
|
283
|
+
return '';
|
|
262
284
|
});
|
|
263
285
|
}
|
|
286
|
+
generateNonce(config) {
|
|
287
|
+
if (config && config.scope && config.scope.indexOf('openid') > -1) {
|
|
288
|
+
const nonce = randomString(10);
|
|
289
|
+
this.token = Object.assign(Object.assign({}, this.token), { nonce });
|
|
290
|
+
return `&nonce=${nonce}`;
|
|
291
|
+
}
|
|
292
|
+
return '';
|
|
293
|
+
}
|
|
294
|
+
checkResponse(token, parameters) {
|
|
295
|
+
this.emitState(parameters);
|
|
296
|
+
this.cleanLocationHash();
|
|
297
|
+
if (!parameters || parameters.error) {
|
|
298
|
+
return false;
|
|
299
|
+
}
|
|
300
|
+
if (token && token.nonce && parameters.access_token) {
|
|
301
|
+
const jwtToken = jwt(parameters.access_token);
|
|
302
|
+
return token.nonce === jwtToken.nonce;
|
|
303
|
+
}
|
|
304
|
+
return parameters.access_token || parameters.code;
|
|
305
|
+
}
|
|
264
306
|
set token(token) {
|
|
265
307
|
this._token = token;
|
|
266
308
|
const { storageKey } = this.authConfig;
|
|
@@ -292,7 +334,7 @@ class OAuthService {
|
|
|
292
334
|
const { refresh_token } = this.token;
|
|
293
335
|
if (tokenPath && refresh_token) {
|
|
294
336
|
this.http.post(tokenPath, new HttpParams({
|
|
295
|
-
fromObject: Object.assign(Object.assign(Object.assign({ client_id: clientId }, clientSecret
|
|
337
|
+
fromObject: Object.assign(Object.assign(Object.assign({ client_id: clientId }, clientSecret && { client_secret: clientSecret } || {}), { grant_type: 'refresh_token', refresh_token }), scope && { scope } || {})
|
|
296
338
|
}), { headers: REQUEST_HEADER }).pipe(catchError(() => {
|
|
297
339
|
this.logout();
|
|
298
340
|
return EMPTY;
|
|
@@ -303,24 +345,24 @@ class OAuthService {
|
|
|
303
345
|
}
|
|
304
346
|
}
|
|
305
347
|
getCleanedUnSearchParameters() {
|
|
306
|
-
const { search } = this.
|
|
348
|
+
const { search } = this.location;
|
|
307
349
|
let searchString = search.substr(1);
|
|
308
350
|
const hashKeys = ['code', 'state', 'error', 'error_description', 'session_state'];
|
|
309
351
|
hashKeys.forEach((hashKey) => {
|
|
310
352
|
const re = new RegExp('&' + hashKey + '(=[^&]*)?|^' + hashKey + '(=[^&]*)?&?');
|
|
311
353
|
searchString = searchString.replace(re, '');
|
|
312
354
|
});
|
|
313
|
-
return searchString.length
|
|
355
|
+
return searchString.length && `?${searchString}` || '';
|
|
314
356
|
}
|
|
315
357
|
cleanLocationHash() {
|
|
316
|
-
const { hash } = this.
|
|
358
|
+
const { hash } = this.location;
|
|
317
359
|
let curHash = hash.substr(1);
|
|
318
|
-
const hashKeys = ['access_token', 'token_type', 'expires_in', 'scope', 'state', 'error', 'error_description', 'session_state'];
|
|
360
|
+
const hashKeys = ['access_token', 'token_type', 'expires_in', 'scope', 'state', 'error', 'error_description', 'session_state', 'nonce'];
|
|
319
361
|
hashKeys.forEach((hashKey) => {
|
|
320
362
|
const re = new RegExp('&' + hashKey + '(=[^&]*)?|^' + hashKey + '(=[^&]*)?&?');
|
|
321
363
|
curHash = curHash.replace(re, '');
|
|
322
364
|
});
|
|
323
|
-
this.
|
|
365
|
+
this.location.hash = curHash;
|
|
324
366
|
}
|
|
325
367
|
emitState(parameters) {
|
|
326
368
|
const { state } = parameters;
|
|
@@ -329,7 +371,7 @@ class OAuthService {
|
|
|
329
371
|
}
|
|
330
372
|
}
|
|
331
373
|
}
|
|
332
|
-
OAuthService.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "13.0.2", ngImport: i0, type: OAuthService, deps: [{ token: i1.HttpClient }, { token: i0.NgZone }, { token: OAUTH_CONFIG }, { token: LOCATION }], target: i0.ɵɵFactoryTarget.Injectable });
|
|
374
|
+
OAuthService.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "13.0.2", ngImport: i0, type: OAuthService, deps: [{ token: i1.HttpClient }, { token: i0.NgZone }, { token: OAUTH_CONFIG }, { token: LOCATION }, { token: i2.Location }], target: i0.ɵɵFactoryTarget.Injectable });
|
|
333
375
|
OAuthService.ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "13.0.2", ngImport: i0, type: OAuthService });
|
|
334
376
|
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "13.0.2", ngImport: i0, type: OAuthService, decorators: [{
|
|
335
377
|
type: Injectable
|
|
@@ -340,7 +382,7 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "13.0.2", ngImpor
|
|
|
340
382
|
}] }, { type: Location, decorators: [{
|
|
341
383
|
type: Inject,
|
|
342
384
|
args: [LOCATION]
|
|
343
|
-
}] }];
|
|
385
|
+
}] }, { type: i2.Location }];
|
|
344
386
|
} });
|
|
345
387
|
|
|
346
388
|
class OAuthInterceptor {
|
|
@@ -350,7 +392,7 @@ class OAuthInterceptor {
|
|
|
350
392
|
intercept(req, next) {
|
|
351
393
|
if (this.oauthService) {
|
|
352
394
|
if (!this.isPathExcepted(req)) {
|
|
353
|
-
const token = this.oauthService
|
|
395
|
+
const { token } = this.oauthService;
|
|
354
396
|
if (token && token.access_token) {
|
|
355
397
|
req = req.clone({
|
|
356
398
|
setHeaders: {
|
|
@@ -373,13 +415,16 @@ class OAuthInterceptor {
|
|
|
373
415
|
}
|
|
374
416
|
}
|
|
375
417
|
isPathExcepted(req) {
|
|
376
|
-
|
|
377
|
-
|
|
378
|
-
|
|
379
|
-
|
|
418
|
+
const { ignorePaths } = this.oauthService;
|
|
419
|
+
if (ignorePaths) {
|
|
420
|
+
for (const ignorePath of this.oauthService.ignorePaths) {
|
|
421
|
+
try {
|
|
422
|
+
if (req.url.match(ignorePath)) {
|
|
423
|
+
return true;
|
|
424
|
+
}
|
|
425
|
+
}
|
|
426
|
+
catch (err) {
|
|
380
427
|
}
|
|
381
|
-
}
|
|
382
|
-
catch (err) {
|
|
383
428
|
}
|
|
384
429
|
}
|
|
385
430
|
return false;
|
|
@@ -392,8 +437,9 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "13.0.2", ngImpor
|
|
|
392
437
|
}], ctorParameters: function () { return [{ type: OAuthService }]; } });
|
|
393
438
|
|
|
394
439
|
class OAuthLoginComponent {
|
|
395
|
-
constructor(oauthService, location) {
|
|
440
|
+
constructor(oauthService, locationService, location) {
|
|
396
441
|
this.oauthService = oauthService;
|
|
442
|
+
this.locationService = locationService;
|
|
397
443
|
this.location = location;
|
|
398
444
|
this.subscription = new Subscription();
|
|
399
445
|
this._i18n = {
|
|
@@ -412,14 +458,15 @@ class OAuthLoginComponent {
|
|
|
412
458
|
this.OAuthType = OAuthType;
|
|
413
459
|
this.collapse = false;
|
|
414
460
|
this.type = this.oauthService.type;
|
|
415
|
-
this.redirectUri = this.location.href;
|
|
416
461
|
this.state$ = this.oauthService.state$.pipe(tap(s => this.stateChange.emit(s)));
|
|
417
462
|
this.status$ = this.oauthService.status$.pipe(tap(s => {
|
|
418
463
|
if (s === OAuthStatus.AUTHORIZED && this.profileName$) {
|
|
419
|
-
this.subscription.add(this.profileName$.subscribe(n => this.profileName = n));
|
|
464
|
+
this.subscription.add(this.profileName$.pipe(take(1)).subscribe(n => this.profileName = n));
|
|
420
465
|
}
|
|
421
466
|
else {
|
|
422
|
-
|
|
467
|
+
const { token } = this.oauthService;
|
|
468
|
+
const userInfo = token && token.id_token && JSON.parse(atob(token.id_token.split('.')[1])) || {};
|
|
469
|
+
this.profileName = userInfo.name || userInfo.username || userInfo.email || userInfo.sub || '';
|
|
423
470
|
}
|
|
424
471
|
}));
|
|
425
472
|
this.loginFunction = (p) => this.login(p);
|
|
@@ -431,6 +478,14 @@ class OAuthLoginComponent {
|
|
|
431
478
|
set i18n(i18n) {
|
|
432
479
|
this._i18n = Object.assign(Object.assign({}, this._i18n), i18n);
|
|
433
480
|
}
|
|
481
|
+
set redirectUri(redirectUri) {
|
|
482
|
+
if (redirectUri) {
|
|
483
|
+
this._redirectUri = redirectUri;
|
|
484
|
+
}
|
|
485
|
+
}
|
|
486
|
+
get redirectUri() {
|
|
487
|
+
return this._redirectUri || `${this.location.origin}${this.locationService.path(true) || '/'}`;
|
|
488
|
+
}
|
|
434
489
|
ngOnDestroy() {
|
|
435
490
|
this.subscription.unsubscribe();
|
|
436
491
|
}
|
|
@@ -438,8 +493,10 @@ class OAuthLoginComponent {
|
|
|
438
493
|
this.oauthService.logout();
|
|
439
494
|
}
|
|
440
495
|
login(parameters) {
|
|
441
|
-
this
|
|
442
|
-
|
|
496
|
+
return __awaiter(this, void 0, void 0, function* () {
|
|
497
|
+
yield this.oauthService.login(parameters);
|
|
498
|
+
this.collapse = false;
|
|
499
|
+
});
|
|
443
500
|
}
|
|
444
501
|
toggleCollapse() {
|
|
445
502
|
this.collapse = !this.collapse;
|
|
@@ -448,18 +505,20 @@ class OAuthLoginComponent {
|
|
|
448
505
|
this.collapse = false;
|
|
449
506
|
}
|
|
450
507
|
}
|
|
451
|
-
OAuthLoginComponent.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "13.0.2", ngImport: i0, type: OAuthLoginComponent, deps: [{ token: OAuthService }, { token: LOCATION }], target: i0.ɵɵFactoryTarget.Component });
|
|
452
|
-
OAuthLoginComponent.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "12.0.0", version: "13.0.2", type: OAuthLoginComponent, selector: "oauth-login", inputs: { i18n: "i18n", state: "state", profileName$: "profileName$" }, outputs: { stateChange: "stateChange" }, host: { listeners: { "window:keydown.escape": "keyboardEvent()" } }, queries: [{ propertyName: "loginTemplate", first: true, predicate: ["login"], descendants: true }], ngImport: i0, template: "<ng-container *ngIf=\"state$ | async\"></ng-container>\r\n<ng-container *ngIf=\"loginTemplate; else defaultLogin\"\r\n [ngTemplateOutlet]=\"loginTemplate\"\r\n [ngTemplateOutletContext]=\"{login: loginFunction, logout: logoutFunction, status: status$ | async}\">\r\n</ng-container>\r\n<ng-template #defaultLogin>\r\n <ng-container *ngIf=\"status$ | async as status\">\r\n <ng-container *ngIf=\"type === OAuthType.RESOURCE; else noResource\">\r\n <div class=\"oauth dropdown text-
|
|
508
|
+
OAuthLoginComponent.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "13.0.2", ngImport: i0, type: OAuthLoginComponent, deps: [{ token: OAuthService }, { token: i2.Location }, { token: LOCATION }], target: i0.ɵɵFactoryTarget.Component });
|
|
509
|
+
OAuthLoginComponent.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "12.0.0", version: "13.0.2", type: OAuthLoginComponent, selector: "oauth-login", inputs: { i18n: "i18n", redirectUri: "redirectUri", state: "state", profileName$: "profileName$" }, outputs: { stateChange: "stateChange" }, host: { listeners: { "window:keydown.escape": "keyboardEvent()" } }, queries: [{ propertyName: "loginTemplate", first: true, predicate: ["login"], descendants: true }], ngImport: i0, template: "<ng-container *ngIf=\"state$ | async\"></ng-container>\r\n<ng-container *ngIf=\"loginTemplate; else defaultLogin\"\r\n [ngTemplateOutlet]=\"loginTemplate\"\r\n [ngTemplateOutletContext]=\"{login: loginFunction, logout: logoutFunction, status: status$ | async}\">\r\n</ng-container>\r\n<ng-template #defaultLogin>\r\n <ng-container *ngIf=\"status$ | async as status\">\r\n <ng-container *ngIf=\"type === OAuthType.RESOURCE; else noResource\">\r\n <div class=\"oauth dropdown text-end p-3 {{collapse ? 'show': ''}}\">\r\n <button class=\"btn btn-link p-0 dropdown-toggle\"\r\n (click)=\"status === OAuthStatus.AUTHORIZED ? logout() : toggleCollapse()\">\r\n <ng-container *ngTemplateOutlet=\"message\"></ng-container>\r\n </button>\r\n <div class=\"dropdown-menu mr-3 {{collapse ? 'show': ''}}\">\r\n <form class=\"p-3\" #form=\"ngForm\"\r\n *ngIf=\"status === OAuthStatus.NOT_AUTHORIZED || status === OAuthStatus.DENIED\"\r\n (submit)=\"login({username: username, password: password})\">\r\n <div class=\"mb-3\">\r\n <input type=\"text\"\r\n class=\"form-control\"\r\n name=\"username\"\r\n required\r\n [(ngModel)]=\"username\"\r\n [placeholder]=\"i18n.username\">\r\n </div>\r\n <div class=\"mb-3\">\r\n <input type=\"password\"\r\n class=\"form-control\"\r\n name=\"password\"\r\n required\r\n [(ngModel)]=\"password\"\r\n [placeholder]=\"i18n.password\">\r\n </div>\r\n <div class=\"text-end\">\r\n <button type=\"submit\"\r\n class=\"btn btn-primary\"\r\n [disabled]=\"form.invalid\">{{i18n.submit}}</button>\r\n </div>\r\n </form>\r\n </div>\r\n </div>\r\n </ng-container>\r\n\r\n <ng-template #noResource>\r\n <a role=\"button\" class=\"oauth\"\r\n (click)=\"status === OAuthStatus.AUTHORIZED ? logout() : login({redirectUri: redirectUri, state:state})\">\r\n <ng-container *ngTemplateOutlet=\"message\"></ng-container>\r\n </a>\r\n </ng-template>\r\n\r\n <ng-template #message>\r\n <span *ngIf=\"status === OAuthStatus.NOT_AUTHORIZED\">{{i18n.notAuthorized}}</span>\r\n <span *ngIf=\"status === OAuthStatus.AUTHORIZED\">\r\n {{i18n.authorized}}<strong> {{profileName}}</strong>\r\n </span>\r\n <span *ngIf=\"status === OAuthStatus.DENIED\">{{i18n.denied}}</span>\r\n </ng-template>\r\n </ng-container>\r\n</ng-template>\r\n\r\n", styles: [".oauth .dropdown-menu{left:auto;right:0;box-shadow:0 5px 10px #0003;min-width:250px}.oauth .dropdown-menu:before{content:\"\";display:inline-block;border-left:7px solid transparent;border-right:7px solid transparent;border-bottom:7px solid #ccc;border-bottom-color:#0003;position:absolute;top:-7px;left:auto;right:15px}.oauth .dropdown-menu:after{content:\"\";display:inline-block;border-left:6px solid transparent;border-right:6px solid transparent;border-bottom:6px solid #ffffff;position:absolute;top:-6px;left:auto;right:16px}\n"], directives: [{ type: i2.NgIf, selector: "[ngIf]", inputs: ["ngIf", "ngIfThen", "ngIfElse"] }, { type: i2.NgTemplateOutlet, selector: "[ngTemplateOutlet]", inputs: ["ngTemplateOutletContext", "ngTemplateOutlet"] }, { type: i3.ɵNgNoValidate, selector: "form:not([ngNoForm]):not([ngNativeValidate])" }, { type: i3.NgControlStatusGroup, selector: "[formGroupName],[formArrayName],[ngModelGroup],[formGroup],form:not([ngNoForm]),[ngForm]" }, { type: i3.NgForm, selector: "form:not([ngNoForm]):not([formGroup]),ng-form,[ngForm]", inputs: ["ngFormOptions"], outputs: ["ngSubmit"], exportAs: ["ngForm"] }, { type: i3.DefaultValueAccessor, selector: "input:not([type=checkbox])[formControlName],textarea[formControlName],input:not([type=checkbox])[formControl],textarea[formControl],input:not([type=checkbox])[ngModel],textarea[ngModel],[ngDefaultControl]" }, { type: i3.RequiredValidator, selector: ":not([type=checkbox])[required][formControlName],:not([type=checkbox])[required][formControl],:not([type=checkbox])[required][ngModel]", inputs: ["required"] }, { type: i3.NgControlStatus, selector: "[formControlName],[ngModel],[formControl]" }, { type: i3.NgModel, selector: "[ngModel]:not([formControlName]):not([formControl])", inputs: ["name", "disabled", "ngModel", "ngModelOptions"], outputs: ["ngModelChange"], exportAs: ["ngModel"] }], pipes: { "async": i2.AsyncPipe }, encapsulation: i0.ViewEncapsulation.None });
|
|
453
510
|
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "13.0.2", ngImport: i0, type: OAuthLoginComponent, decorators: [{
|
|
454
511
|
type: Component,
|
|
455
|
-
args: [{ selector: 'oauth-login', template: "<ng-container *ngIf=\"state$ | async\"></ng-container>\r\n<ng-container *ngIf=\"loginTemplate; else defaultLogin\"\r\n [ngTemplateOutlet]=\"loginTemplate\"\r\n [ngTemplateOutletContext]=\"{login: loginFunction, logout: logoutFunction, status: status$ | async}\">\r\n</ng-container>\r\n<ng-template #defaultLogin>\r\n <ng-container *ngIf=\"status$ | async as status\">\r\n <ng-container *ngIf=\"type === OAuthType.RESOURCE; else noResource\">\r\n <div class=\"oauth dropdown text-
|
|
512
|
+
args: [{ selector: 'oauth-login', encapsulation: ViewEncapsulation.None, template: "<ng-container *ngIf=\"state$ | async\"></ng-container>\r\n<ng-container *ngIf=\"loginTemplate; else defaultLogin\"\r\n [ngTemplateOutlet]=\"loginTemplate\"\r\n [ngTemplateOutletContext]=\"{login: loginFunction, logout: logoutFunction, status: status$ | async}\">\r\n</ng-container>\r\n<ng-template #defaultLogin>\r\n <ng-container *ngIf=\"status$ | async as status\">\r\n <ng-container *ngIf=\"type === OAuthType.RESOURCE; else noResource\">\r\n <div class=\"oauth dropdown text-end p-3 {{collapse ? 'show': ''}}\">\r\n <button class=\"btn btn-link p-0 dropdown-toggle\"\r\n (click)=\"status === OAuthStatus.AUTHORIZED ? logout() : toggleCollapse()\">\r\n <ng-container *ngTemplateOutlet=\"message\"></ng-container>\r\n </button>\r\n <div class=\"dropdown-menu mr-3 {{collapse ? 'show': ''}}\">\r\n <form class=\"p-3\" #form=\"ngForm\"\r\n *ngIf=\"status === OAuthStatus.NOT_AUTHORIZED || status === OAuthStatus.DENIED\"\r\n (submit)=\"login({username: username, password: password})\">\r\n <div class=\"mb-3\">\r\n <input type=\"text\"\r\n class=\"form-control\"\r\n name=\"username\"\r\n required\r\n [(ngModel)]=\"username\"\r\n [placeholder]=\"i18n.username\">\r\n </div>\r\n <div class=\"mb-3\">\r\n <input type=\"password\"\r\n class=\"form-control\"\r\n name=\"password\"\r\n required\r\n [(ngModel)]=\"password\"\r\n [placeholder]=\"i18n.password\">\r\n </div>\r\n <div class=\"text-end\">\r\n <button type=\"submit\"\r\n class=\"btn btn-primary\"\r\n [disabled]=\"form.invalid\">{{i18n.submit}}</button>\r\n </div>\r\n </form>\r\n </div>\r\n </div>\r\n </ng-container>\r\n\r\n <ng-template #noResource>\r\n <a role=\"button\" class=\"oauth\"\r\n (click)=\"status === OAuthStatus.AUTHORIZED ? logout() : login({redirectUri: redirectUri, state:state})\">\r\n <ng-container *ngTemplateOutlet=\"message\"></ng-container>\r\n </a>\r\n </ng-template>\r\n\r\n <ng-template #message>\r\n <span *ngIf=\"status === OAuthStatus.NOT_AUTHORIZED\">{{i18n.notAuthorized}}</span>\r\n <span *ngIf=\"status === OAuthStatus.AUTHORIZED\">\r\n {{i18n.authorized}}<strong> {{profileName}}</strong>\r\n </span>\r\n <span *ngIf=\"status === OAuthStatus.DENIED\">{{i18n.denied}}</span>\r\n </ng-template>\r\n </ng-container>\r\n</ng-template>\r\n\r\n", styles: [".oauth .dropdown-menu{left:auto;right:0;box-shadow:0 5px 10px #0003;min-width:250px}.oauth .dropdown-menu:before{content:\"\";display:inline-block;border-left:7px solid transparent;border-right:7px solid transparent;border-bottom:7px solid #ccc;border-bottom-color:#0003;position:absolute;top:-7px;left:auto;right:15px}.oauth .dropdown-menu:after{content:\"\";display:inline-block;border-left:6px solid transparent;border-right:6px solid transparent;border-bottom:6px solid #ffffff;position:absolute;top:-6px;left:auto;right:16px}\n"] }]
|
|
456
513
|
}], ctorParameters: function () {
|
|
457
|
-
return [{ type: OAuthService }, { type: Location, decorators: [{
|
|
514
|
+
return [{ type: OAuthService }, { type: i2.Location }, { type: Location, decorators: [{
|
|
458
515
|
type: Inject,
|
|
459
516
|
args: [LOCATION]
|
|
460
517
|
}] }];
|
|
461
518
|
}, propDecorators: { i18n: [{
|
|
462
519
|
type: Input
|
|
520
|
+
}], redirectUri: [{
|
|
521
|
+
type: Input
|
|
463
522
|
}], state: [{
|
|
464
523
|
type: Input
|
|
465
524
|
}], stateChange: [{
|
|
@@ -530,8 +589,7 @@ const defaultConfig = (storage) => {
|
|
|
530
589
|
return {
|
|
531
590
|
storage,
|
|
532
591
|
storageKey: 'token',
|
|
533
|
-
ignorePaths: []
|
|
534
|
-
pkce: false
|
|
592
|
+
ignorePaths: []
|
|
535
593
|
};
|
|
536
594
|
};
|
|
537
595
|
class OAuthModule {
|
|
@@ -600,4 +658,4 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "13.0.2", ngImpor
|
|
|
600
658
|
* Generated bundle index. Do not edit.
|
|
601
659
|
*/
|
|
602
660
|
|
|
603
|
-
export { LOCATION, OAUTH_CONFIG, OAuthInterceptor, OAuthLoginComponent, OAuthModule, OAuthService, OAuthStatus, OAuthType, SERVER_HOST, SERVER_PATH, STORAGE };
|
|
661
|
+
export { LOCATION, OAUTH_CONFIG, OAUTH_TOKEN, OAuthInterceptor, OAuthLoginComponent, OAuthModule, OAuthService, OAuthStatus, OAuthType, SERVER_HOST, SERVER_PATH, STORAGE };
|