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/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,105 +58,127 @@ 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(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 } || {}), v.end_session_endpoint && { logoutPath: v.end_session_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}
|
|
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}`, 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
|
-
logout() {
|
|
172
|
+
logout(useLogoutUrl) {
|
|
156
173
|
this.revoke();
|
|
157
174
|
this.token = null;
|
|
158
175
|
this.status = OAuthStatus.NOT_AUTHORIZED;
|
|
176
|
+
const { logoutPath, logoutRedirectUri } = this.authConfig.config;
|
|
177
|
+
if (useLogoutUrl && logoutPath) {
|
|
178
|
+
const { origin, pathname } = this.location;
|
|
179
|
+
const currentPath = `${origin}${pathname}`;
|
|
180
|
+
this.location.replace(`${logoutPath}?post_logout_redirect_uri=${logoutRedirectUri || currentPath}`);
|
|
181
|
+
}
|
|
159
182
|
}
|
|
160
183
|
revoke() {
|
|
161
184
|
const { revokePath, clientId, clientSecret } = this.authConfig.config;
|
|
@@ -195,10 +218,10 @@ class OAuthService {
|
|
|
195
218
|
const { clientId, clientSecret, tokenPath, scope } = this.authConfig.config;
|
|
196
219
|
const { username, password } = parameters;
|
|
197
220
|
this.http.post(tokenPath, new HttpParams({
|
|
198
|
-
fromObject: Object.assign(Object.assign({ client_id: clientId, client_secret: clientSecret, grant_type: OAuthType.RESOURCE }, scope
|
|
221
|
+
fromObject: Object.assign(Object.assign(Object.assign(Object.assign({ client_id: clientId }, clientSecret && { client_secret: clientSecret } || {}), { grant_type: OAuthType.RESOURCE }), scope && { scope } || {}), { username,
|
|
199
222
|
password })
|
|
200
|
-
}), { headers: REQUEST_HEADER }).pipe(catchError(
|
|
201
|
-
this.token =
|
|
223
|
+
}), { headers: REQUEST_HEADER }).pipe(catchError(err => {
|
|
224
|
+
this.token = err;
|
|
202
225
|
this.status = OAuthStatus.DENIED;
|
|
203
226
|
return EMPTY;
|
|
204
227
|
})).subscribe(params => {
|
|
@@ -209,13 +232,13 @@ class OAuthService {
|
|
|
209
232
|
authorizationCodeLogin(parameters) {
|
|
210
233
|
return __awaiter(this, void 0, void 0, function* () {
|
|
211
234
|
const authUrl = yield this.toAuthorizationUrl(parameters, OAuthType.AUTHORIZATION_CODE);
|
|
212
|
-
this.
|
|
235
|
+
this.location.replace(authUrl);
|
|
213
236
|
});
|
|
214
237
|
}
|
|
215
238
|
implicitLogin(parameters) {
|
|
216
239
|
return __awaiter(this, void 0, void 0, function* () {
|
|
217
240
|
const authUrl = yield this.toAuthorizationUrl(parameters, OAuthType.IMPLICIT);
|
|
218
|
-
this.
|
|
241
|
+
this.location.replace(authUrl);
|
|
219
242
|
});
|
|
220
243
|
}
|
|
221
244
|
clientCredentialLogin() {
|
|
@@ -246,21 +269,46 @@ class OAuthService {
|
|
|
246
269
|
toAuthorizationUrl(parameters, responseType) {
|
|
247
270
|
return __awaiter(this, void 0, void 0, function* () {
|
|
248
271
|
const { config } = this.authConfig;
|
|
249
|
-
|
|
250
|
-
|
|
251
|
-
|
|
252
|
-
|
|
253
|
-
|
|
254
|
-
|
|
255
|
-
|
|
256
|
-
|
|
257
|
-
|
|
272
|
+
let authorizationUrl = `${config.authorizePath}`;
|
|
273
|
+
authorizationUrl += config.authorizePath.includes('?') && '&' || '?';
|
|
274
|
+
authorizationUrl += `client_id=${config.clientId}`;
|
|
275
|
+
authorizationUrl += `&redirect_uri=${encodeURIComponent(parameters.redirectUri)}`;
|
|
276
|
+
authorizationUrl += `&response_type=${responseType}`;
|
|
277
|
+
authorizationUrl += `&scope=${encodeURIComponent(config.scope || '')}`;
|
|
278
|
+
authorizationUrl += `&state=${encodeURIComponent(parameters.state || '')}`;
|
|
279
|
+
return `${authorizationUrl}${this.generateNonce(config)}${yield this.generateCodeChallenge(config)}`;
|
|
280
|
+
});
|
|
281
|
+
}
|
|
282
|
+
generateCodeChallenge(config) {
|
|
283
|
+
return __awaiter(this, void 0, void 0, function* () {
|
|
284
|
+
if (config.pkce) {
|
|
285
|
+
const codeVerifier = randomString();
|
|
286
|
+
this.token = Object.assign(Object.assign({}, this.token), { codeVerifier });
|
|
287
|
+
return `&code_challenge=${yield pkce(codeVerifier)}&code_challenge_method=S256`;
|
|
258
288
|
}
|
|
259
|
-
|
|
260
|
-
const parametersString = `${clientId}${redirectUri}${responseTypeString}${scope}${state}${codeChallenge}`;
|
|
261
|
-
return `${config.authorizePath}${parametersString}`;
|
|
289
|
+
return '';
|
|
262
290
|
});
|
|
263
291
|
}
|
|
292
|
+
generateNonce(config) {
|
|
293
|
+
if (config && config.scope && config.scope.indexOf('openid') > -1) {
|
|
294
|
+
const nonce = randomString(10);
|
|
295
|
+
this.token = Object.assign(Object.assign({}, this.token), { nonce });
|
|
296
|
+
return `&nonce=${nonce}`;
|
|
297
|
+
}
|
|
298
|
+
return '';
|
|
299
|
+
}
|
|
300
|
+
checkResponse(token, parameters) {
|
|
301
|
+
this.emitState(parameters);
|
|
302
|
+
this.cleanLocationHash();
|
|
303
|
+
if (!parameters || parameters.error) {
|
|
304
|
+
return false;
|
|
305
|
+
}
|
|
306
|
+
if (token && token.nonce && parameters.access_token) {
|
|
307
|
+
const jwtToken = jwt(parameters.access_token);
|
|
308
|
+
return token.nonce === jwtToken.nonce;
|
|
309
|
+
}
|
|
310
|
+
return parameters.access_token || parameters.code;
|
|
311
|
+
}
|
|
264
312
|
set token(token) {
|
|
265
313
|
this._token = token;
|
|
266
314
|
const { storageKey } = this.authConfig;
|
|
@@ -292,7 +340,7 @@ class OAuthService {
|
|
|
292
340
|
const { refresh_token } = this.token;
|
|
293
341
|
if (tokenPath && refresh_token) {
|
|
294
342
|
this.http.post(tokenPath, new HttpParams({
|
|
295
|
-
fromObject: Object.assign(Object.assign(Object.assign({ client_id: clientId }, clientSecret
|
|
343
|
+
fromObject: Object.assign(Object.assign(Object.assign({ client_id: clientId }, clientSecret && { client_secret: clientSecret } || {}), { grant_type: 'refresh_token', refresh_token }), scope && { scope } || {})
|
|
296
344
|
}), { headers: REQUEST_HEADER }).pipe(catchError(() => {
|
|
297
345
|
this.logout();
|
|
298
346
|
return EMPTY;
|
|
@@ -303,24 +351,24 @@ class OAuthService {
|
|
|
303
351
|
}
|
|
304
352
|
}
|
|
305
353
|
getCleanedUnSearchParameters() {
|
|
306
|
-
const { search } = this.
|
|
354
|
+
const { search } = this.location;
|
|
307
355
|
let searchString = search.substr(1);
|
|
308
|
-
const hashKeys = ['code', 'state', 'error', 'error_description', 'session_state'];
|
|
356
|
+
const hashKeys = ['code', 'state', 'error', 'error_description', 'session_state', 'scope', 'authuser', 'prompt'];
|
|
309
357
|
hashKeys.forEach((hashKey) => {
|
|
310
358
|
const re = new RegExp('&' + hashKey + '(=[^&]*)?|^' + hashKey + '(=[^&]*)?&?');
|
|
311
359
|
searchString = searchString.replace(re, '');
|
|
312
360
|
});
|
|
313
|
-
return searchString.length
|
|
361
|
+
return searchString.length && `?${searchString}` || '';
|
|
314
362
|
}
|
|
315
363
|
cleanLocationHash() {
|
|
316
|
-
const { hash } = this.
|
|
364
|
+
const { hash } = this.location;
|
|
317
365
|
let curHash = hash.substr(1);
|
|
318
|
-
const hashKeys = ['access_token', 'token_type', 'expires_in', 'scope', 'state', 'error', 'error_description', 'session_state'];
|
|
366
|
+
const hashKeys = ['access_token', 'token_type', 'expires_in', 'scope', 'state', 'error', 'error_description', 'session_state', 'nonce'];
|
|
319
367
|
hashKeys.forEach((hashKey) => {
|
|
320
368
|
const re = new RegExp('&' + hashKey + '(=[^&]*)?|^' + hashKey + '(=[^&]*)?&?');
|
|
321
369
|
curHash = curHash.replace(re, '');
|
|
322
370
|
});
|
|
323
|
-
this.
|
|
371
|
+
this.location.hash = curHash;
|
|
324
372
|
}
|
|
325
373
|
emitState(parameters) {
|
|
326
374
|
const { state } = parameters;
|
|
@@ -329,9 +377,9 @@ class OAuthService {
|
|
|
329
377
|
}
|
|
330
378
|
}
|
|
331
379
|
}
|
|
332
|
-
OAuthService.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "13.0.
|
|
333
|
-
OAuthService.ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "13.0.
|
|
334
|
-
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "13.0.
|
|
380
|
+
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 });
|
|
381
|
+
OAuthService.ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "13.0.3", ngImport: i0, type: OAuthService });
|
|
382
|
+
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "13.0.3", ngImport: i0, type: OAuthService, decorators: [{
|
|
335
383
|
type: Injectable
|
|
336
384
|
}], ctorParameters: function () {
|
|
337
385
|
return [{ type: i1.HttpClient }, { type: i0.NgZone }, { type: undefined, decorators: [{
|
|
@@ -340,7 +388,7 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "13.0.2", ngImpor
|
|
|
340
388
|
}] }, { type: Location, decorators: [{
|
|
341
389
|
type: Inject,
|
|
342
390
|
args: [LOCATION]
|
|
343
|
-
}] }];
|
|
391
|
+
}] }, { type: i2.Location }];
|
|
344
392
|
} });
|
|
345
393
|
|
|
346
394
|
class OAuthInterceptor {
|
|
@@ -350,7 +398,7 @@ class OAuthInterceptor {
|
|
|
350
398
|
intercept(req, next) {
|
|
351
399
|
if (this.oauthService) {
|
|
352
400
|
if (!this.isPathExcepted(req)) {
|
|
353
|
-
const token = this.oauthService
|
|
401
|
+
const { token } = this.oauthService;
|
|
354
402
|
if (token && token.access_token) {
|
|
355
403
|
req = req.clone({
|
|
356
404
|
setHeaders: {
|
|
@@ -373,27 +421,31 @@ class OAuthInterceptor {
|
|
|
373
421
|
}
|
|
374
422
|
}
|
|
375
423
|
isPathExcepted(req) {
|
|
376
|
-
|
|
377
|
-
|
|
378
|
-
|
|
379
|
-
|
|
424
|
+
const { ignorePaths } = this.oauthService;
|
|
425
|
+
if (ignorePaths) {
|
|
426
|
+
for (const ignorePath of this.oauthService.ignorePaths) {
|
|
427
|
+
try {
|
|
428
|
+
if (req.url.match(ignorePath)) {
|
|
429
|
+
return true;
|
|
430
|
+
}
|
|
431
|
+
}
|
|
432
|
+
catch (err) {
|
|
380
433
|
}
|
|
381
|
-
}
|
|
382
|
-
catch (err) {
|
|
383
434
|
}
|
|
384
435
|
}
|
|
385
436
|
return false;
|
|
386
437
|
}
|
|
387
438
|
}
|
|
388
|
-
OAuthInterceptor.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "13.0.
|
|
389
|
-
OAuthInterceptor.ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "13.0.
|
|
390
|
-
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "13.0.
|
|
439
|
+
OAuthInterceptor.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "13.0.3", ngImport: i0, type: OAuthInterceptor, deps: [{ token: OAuthService }], target: i0.ɵɵFactoryTarget.Injectable });
|
|
440
|
+
OAuthInterceptor.ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "13.0.3", ngImport: i0, type: OAuthInterceptor });
|
|
441
|
+
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "13.0.3", ngImport: i0, type: OAuthInterceptor, decorators: [{
|
|
391
442
|
type: Injectable
|
|
392
443
|
}], ctorParameters: function () { return [{ type: OAuthService }]; } });
|
|
393
444
|
|
|
394
445
|
class OAuthLoginComponent {
|
|
395
|
-
constructor(oauthService, location) {
|
|
446
|
+
constructor(oauthService, locationService, location) {
|
|
396
447
|
this.oauthService = oauthService;
|
|
448
|
+
this.locationService = locationService;
|
|
397
449
|
this.location = location;
|
|
398
450
|
this.subscription = new Subscription();
|
|
399
451
|
this._i18n = {
|
|
@@ -404,6 +456,7 @@ class OAuthLoginComponent {
|
|
|
404
456
|
authorized: 'Welcome',
|
|
405
457
|
denied: 'Access Denied. Try again!'
|
|
406
458
|
};
|
|
459
|
+
this.useLogoutUrl = false;
|
|
407
460
|
this.state = '';
|
|
408
461
|
this.stateChange = new EventEmitter();
|
|
409
462
|
this.username = '';
|
|
@@ -412,14 +465,15 @@ class OAuthLoginComponent {
|
|
|
412
465
|
this.OAuthType = OAuthType;
|
|
413
466
|
this.collapse = false;
|
|
414
467
|
this.type = this.oauthService.type;
|
|
415
|
-
this.redirectUri = this.location.href;
|
|
416
468
|
this.state$ = this.oauthService.state$.pipe(tap(s => this.stateChange.emit(s)));
|
|
417
469
|
this.status$ = this.oauthService.status$.pipe(tap(s => {
|
|
418
470
|
if (s === OAuthStatus.AUTHORIZED && this.profileName$) {
|
|
419
|
-
this.subscription.add(this.profileName$.subscribe(n => this.profileName = n));
|
|
471
|
+
this.subscription.add(this.profileName$.pipe(take(1)).subscribe(n => this.profileName = n));
|
|
420
472
|
}
|
|
421
473
|
else {
|
|
422
|
-
|
|
474
|
+
const { token } = this.oauthService;
|
|
475
|
+
const userInfo = token && token.id_token && JSON.parse(atob(token.id_token.split('.')[1])) || {};
|
|
476
|
+
this.profileName = userInfo.name || userInfo.username || userInfo.email || userInfo.sub || '';
|
|
423
477
|
}
|
|
424
478
|
}));
|
|
425
479
|
this.loginFunction = (p) => this.login(p);
|
|
@@ -431,15 +485,25 @@ class OAuthLoginComponent {
|
|
|
431
485
|
set i18n(i18n) {
|
|
432
486
|
this._i18n = Object.assign(Object.assign({}, this._i18n), i18n);
|
|
433
487
|
}
|
|
488
|
+
set redirectUri(redirectUri) {
|
|
489
|
+
if (redirectUri) {
|
|
490
|
+
this._redirectUri = redirectUri;
|
|
491
|
+
}
|
|
492
|
+
}
|
|
493
|
+
get redirectUri() {
|
|
494
|
+
return this._redirectUri || `${this.location.origin}${this.locationService.path(true) || '/'}`;
|
|
495
|
+
}
|
|
434
496
|
ngOnDestroy() {
|
|
435
497
|
this.subscription.unsubscribe();
|
|
436
498
|
}
|
|
437
499
|
logout() {
|
|
438
|
-
this.oauthService.logout();
|
|
500
|
+
this.oauthService.logout(this.useLogoutUrl);
|
|
439
501
|
}
|
|
440
502
|
login(parameters) {
|
|
441
|
-
this
|
|
442
|
-
|
|
503
|
+
return __awaiter(this, void 0, void 0, function* () {
|
|
504
|
+
yield this.oauthService.login(parameters);
|
|
505
|
+
this.collapse = false;
|
|
506
|
+
});
|
|
443
507
|
}
|
|
444
508
|
toggleCollapse() {
|
|
445
509
|
this.collapse = !this.collapse;
|
|
@@ -448,18 +512,22 @@ class OAuthLoginComponent {
|
|
|
448
512
|
this.collapse = false;
|
|
449
513
|
}
|
|
450
514
|
}
|
|
451
|
-
OAuthLoginComponent.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "13.0.
|
|
452
|
-
OAuthLoginComponent.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "12.0.0", version: "13.0.
|
|
453
|
-
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "13.0.
|
|
515
|
+
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 });
|
|
516
|
+
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 });
|
|
517
|
+
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "13.0.3", ngImport: i0, type: OAuthLoginComponent, decorators: [{
|
|
454
518
|
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-
|
|
519
|
+
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"] }]
|
|
456
520
|
}], ctorParameters: function () {
|
|
457
|
-
return [{ type: OAuthService }, { type: Location, decorators: [{
|
|
521
|
+
return [{ type: OAuthService }, { type: i2.Location }, { type: Location, decorators: [{
|
|
458
522
|
type: Inject,
|
|
459
523
|
args: [LOCATION]
|
|
460
524
|
}] }];
|
|
461
525
|
}, propDecorators: { i18n: [{
|
|
462
526
|
type: Input
|
|
527
|
+
}], redirectUri: [{
|
|
528
|
+
type: Input
|
|
529
|
+
}], useLogoutUrl: [{
|
|
530
|
+
type: Input
|
|
463
531
|
}], state: [{
|
|
464
532
|
type: Input
|
|
465
533
|
}], stateChange: [{
|
|
@@ -530,8 +598,7 @@ const defaultConfig = (storage) => {
|
|
|
530
598
|
return {
|
|
531
599
|
storage,
|
|
532
600
|
storageKey: 'token',
|
|
533
|
-
ignorePaths: []
|
|
534
|
-
pkce: false
|
|
601
|
+
ignorePaths: []
|
|
535
602
|
};
|
|
536
603
|
};
|
|
537
604
|
class OAuthModule {
|
|
@@ -556,12 +623,12 @@ class OAuthModule {
|
|
|
556
623
|
};
|
|
557
624
|
}
|
|
558
625
|
}
|
|
559
|
-
OAuthModule.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "13.0.
|
|
560
|
-
OAuthModule.ɵmod = i0.ɵɵngDeclareNgModule({ minVersion: "12.0.0", version: "13.0.
|
|
626
|
+
OAuthModule.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "13.0.3", ngImport: i0, type: OAuthModule, deps: [], target: i0.ɵɵFactoryTarget.NgModule });
|
|
627
|
+
OAuthModule.ɵmod = i0.ɵɵngDeclareNgModule({ minVersion: "12.0.0", version: "13.0.3", ngImport: i0, type: OAuthModule, declarations: [OAuthLoginComponent], imports: [CommonModule,
|
|
561
628
|
FormsModule,
|
|
562
629
|
HttpClientModule,
|
|
563
630
|
RouterModule], exports: [OAuthLoginComponent] });
|
|
564
|
-
OAuthModule.ɵinj = i0.ɵɵngDeclareInjector({ minVersion: "12.0.0", version: "13.0.
|
|
631
|
+
OAuthModule.ɵinj = i0.ɵɵngDeclareInjector({ minVersion: "12.0.0", version: "13.0.3", ngImport: i0, type: OAuthModule, providers: [
|
|
565
632
|
LocationService,
|
|
566
633
|
StorageService,
|
|
567
634
|
OAuthService,
|
|
@@ -572,7 +639,7 @@ OAuthModule.ɵinj = i0.ɵɵngDeclareInjector({ minVersion: "12.0.0", version: "1
|
|
|
572
639
|
HttpClientModule,
|
|
573
640
|
RouterModule,
|
|
574
641
|
]] });
|
|
575
|
-
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "13.0.
|
|
642
|
+
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "13.0.3", ngImport: i0, type: OAuthModule, decorators: [{
|
|
576
643
|
type: NgModule,
|
|
577
644
|
args: [{
|
|
578
645
|
imports: [
|
|
@@ -600,4 +667,5 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "13.0.2", ngImpor
|
|
|
600
667
|
* Generated bundle index. Do not edit.
|
|
601
668
|
*/
|
|
602
669
|
|
|
603
|
-
export { LOCATION, OAUTH_CONFIG, OAuthInterceptor, OAuthLoginComponent, OAuthModule, OAuthService, OAuthStatus, OAuthType, SERVER_HOST, SERVER_PATH, STORAGE };
|
|
670
|
+
export { LOCATION, OAUTH_CONFIG, OAUTH_TOKEN, OAuthInterceptor, OAuthLoginComponent, OAuthModule, OAuthService, OAuthStatus, OAuthType, SERVER_HOST, SERVER_PATH, STORAGE };
|
|
671
|
+
//# sourceMappingURL=ngx-oauth.mjs.map
|