oidc-auth-client 0.0.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/LICENSE +21 -0
- package/README.md +240 -0
- package/dist/index.d.ts +21 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +21 -0
- package/dist/index.js.map +1 -0
- package/dist/src/auth/Client.d.ts +135 -0
- package/dist/src/auth/Client.d.ts.map +1 -0
- package/dist/src/auth/Client.js +734 -0
- package/dist/src/auth/Client.js.map +1 -0
- package/dist/src/auth/Events.d.ts +51 -0
- package/dist/src/auth/Events.d.ts.map +1 -0
- package/dist/src/auth/Events.js +139 -0
- package/dist/src/auth/Events.js.map +1 -0
- package/dist/src/auth/Session.d.ts +132 -0
- package/dist/src/auth/Session.d.ts.map +1 -0
- package/dist/src/auth/Session.js +355 -0
- package/dist/src/auth/Session.js.map +1 -0
- package/dist/src/auth/Settings.d.ts +178 -0
- package/dist/src/auth/Settings.d.ts.map +1 -0
- package/dist/src/auth/Settings.js +210 -0
- package/dist/src/auth/Settings.js.map +1 -0
- package/dist/src/crypto/Crypto.d.ts +14 -0
- package/dist/src/crypto/Crypto.d.ts.map +1 -0
- package/dist/src/crypto/Crypto.js +107 -0
- package/dist/src/crypto/Crypto.js.map +1 -0
- package/dist/src/models/User.d.ts +31 -0
- package/dist/src/models/User.d.ts.map +1 -0
- package/dist/src/models/User.js +57 -0
- package/dist/src/models/User.js.map +1 -0
- package/dist/src/navigation/Navigator.d.ts +106 -0
- package/dist/src/navigation/Navigator.d.ts.map +1 -0
- package/dist/src/navigation/Navigator.js +441 -0
- package/dist/src/navigation/Navigator.js.map +1 -0
- package/dist/src/protocol/Requests.d.ts +50 -0
- package/dist/src/protocol/Requests.d.ts.map +1 -0
- package/dist/src/protocol/Requests.js +109 -0
- package/dist/src/protocol/Requests.js.map +1 -0
- package/dist/src/protocol/ResponseValidator.d.ts +39 -0
- package/dist/src/protocol/ResponseValidator.d.ts.map +1 -0
- package/dist/src/protocol/ResponseValidator.js +366 -0
- package/dist/src/protocol/ResponseValidator.js.map +1 -0
- package/dist/src/protocol/Responses.d.ts +44 -0
- package/dist/src/protocol/Responses.d.ts.map +1 -0
- package/dist/src/protocol/Responses.js +77 -0
- package/dist/src/protocol/Responses.js.map +1 -0
- package/dist/src/protocol/TokenService.d.ts +38 -0
- package/dist/src/protocol/TokenService.d.ts.map +1 -0
- package/dist/src/protocol/TokenService.js +259 -0
- package/dist/src/protocol/TokenService.js.map +1 -0
- package/dist/src/services/Http.d.ts +55 -0
- package/dist/src/services/Http.d.ts.map +1 -0
- package/dist/src/services/Http.js +276 -0
- package/dist/src/services/Http.js.map +1 -0
- package/dist/src/services/Timer.d.ts +18 -0
- package/dist/src/services/Timer.d.ts.map +1 -0
- package/dist/src/services/Timer.js +56 -0
- package/dist/src/services/Timer.js.map +1 -0
- package/dist/src/storage/Storage.d.ts +23 -0
- package/dist/src/storage/Storage.d.ts.map +1 -0
- package/dist/src/storage/Storage.js +65 -0
- package/dist/src/storage/Storage.js.map +1 -0
- package/dist/src/types/crypto.d.ts +32 -0
- package/dist/src/types/crypto.d.ts.map +1 -0
- package/dist/src/types/crypto.js +3 -0
- package/dist/src/types/crypto.js.map +1 -0
- package/dist/src/types/navigator.d.ts +33 -0
- package/dist/src/types/navigator.d.ts.map +1 -0
- package/dist/src/types/navigator.js +3 -0
- package/dist/src/types/navigator.js.map +1 -0
- package/dist/src/types/storage.d.ts +7 -0
- package/dist/src/types/storage.d.ts.map +1 -0
- package/dist/src/types/storage.js +3 -0
- package/dist/src/types/storage.js.map +1 -0
- package/dist/src/types/user.d.ts +6 -0
- package/dist/src/types/user.d.ts.map +1 -0
- package/dist/src/types/user.js +3 -0
- package/dist/src/types/user.js.map +1 -0
- package/dist/src/utils/Event.d.ts +10 -0
- package/dist/src/utils/Event.d.ts.map +1 -0
- package/dist/src/utils/Event.js +24 -0
- package/dist/src/utils/Event.js.map +1 -0
- package/dist/src/utils/Global.d.ts +14 -0
- package/dist/src/utils/Global.d.ts.map +1 -0
- package/dist/src/utils/Global.js +50 -0
- package/dist/src/utils/Global.js.map +1 -0
- package/dist/src/utils/Log.d.ts +30 -0
- package/dist/src/utils/Log.d.ts.map +1 -0
- package/dist/src/utils/Log.js +68 -0
- package/dist/src/utils/Log.js.map +1 -0
- package/package.json +91 -0
|
@@ -0,0 +1,734 @@
|
|
|
1
|
+
// Licensed under the Apache License, Version 2.0. See LICENSE in the project root for license information.
|
|
2
|
+
import { Log } from '../utils/Log.js';
|
|
3
|
+
import { OidcClientSettings, UserManagerSettings } from './Settings.js';
|
|
4
|
+
import { ErrorResponse } from '../protocol/Responses.js';
|
|
5
|
+
import { SigninRequest } from '../protocol/Requests.js';
|
|
6
|
+
import { SigninResponse } from '../protocol/Responses.js';
|
|
7
|
+
import { SignoutRequest } from '../protocol/Requests.js';
|
|
8
|
+
import { SignoutResponse } from '../protocol/Responses.js';
|
|
9
|
+
import { SigninState, State } from './Session.js';
|
|
10
|
+
import { User } from '../models/User.js';
|
|
11
|
+
import { UserManagerEvents } from './Events.js';
|
|
12
|
+
import { SilentRenewService, SessionMonitor } from './Session.js';
|
|
13
|
+
import { TokenRevocationClient, TokenClient } from '../protocol/TokenService.js';
|
|
14
|
+
import { JoseUtil, generateRandom } from '../crypto/Crypto.js';
|
|
15
|
+
export class OidcClient {
|
|
16
|
+
constructor(settings = {}) {
|
|
17
|
+
if (settings instanceof OidcClientSettings) {
|
|
18
|
+
this._settings = settings;
|
|
19
|
+
}
|
|
20
|
+
else {
|
|
21
|
+
this._settings = new OidcClientSettings(settings);
|
|
22
|
+
}
|
|
23
|
+
}
|
|
24
|
+
get _stateStore() {
|
|
25
|
+
return this.settings.stateStore;
|
|
26
|
+
}
|
|
27
|
+
get _validator() {
|
|
28
|
+
return this.settings.validator;
|
|
29
|
+
}
|
|
30
|
+
get _metadataService() {
|
|
31
|
+
return this.settings.metadataService;
|
|
32
|
+
}
|
|
33
|
+
get settings() {
|
|
34
|
+
return this._settings;
|
|
35
|
+
}
|
|
36
|
+
get metadataService() {
|
|
37
|
+
return this._metadataService;
|
|
38
|
+
}
|
|
39
|
+
async createSigninRequest({ response_type, scope, redirect_uri, data, state, prompt, display, max_age, ui_locales, id_token_hint, login_hint, acr_values, resource, request, request_uri, response_mode, extraQueryParams, extraTokenParams, request_type, skipUserInfo, } = {}, stateStore) {
|
|
40
|
+
Log.debug('OidcClient.createSigninRequest');
|
|
41
|
+
const client_id = this._settings.client_id;
|
|
42
|
+
response_type = response_type || this._settings.response_type;
|
|
43
|
+
scope = scope || this._settings.scope;
|
|
44
|
+
redirect_uri = redirect_uri || this._settings.redirect_uri;
|
|
45
|
+
prompt = prompt || this._settings.prompt;
|
|
46
|
+
display = display || this._settings.display;
|
|
47
|
+
max_age = max_age || this._settings.max_age;
|
|
48
|
+
ui_locales = ui_locales || this._settings.ui_locales;
|
|
49
|
+
acr_values = acr_values || this._settings.acr_values;
|
|
50
|
+
resource = resource || this._settings.resource;
|
|
51
|
+
response_mode = response_mode || this._settings.response_mode;
|
|
52
|
+
extraQueryParams = extraQueryParams || this._settings.extraQueryParams;
|
|
53
|
+
extraTokenParams = extraTokenParams || this._settings.extraTokenParams;
|
|
54
|
+
const authority = this._settings.authority;
|
|
55
|
+
if (SigninRequest.isCode(response_type) && response_type !== 'code') {
|
|
56
|
+
return Promise.reject(new Error('OpenID Connect hybrid flow is not supported'));
|
|
57
|
+
}
|
|
58
|
+
const url = await this._metadataService.getAuthorizationEndpoint();
|
|
59
|
+
Log.debug('OidcClient.createSigninRequest: Received authorization endpoint', url);
|
|
60
|
+
// Pre-compute PKCE code_verifier + code_challenge (jose requires async SHA-256)
|
|
61
|
+
let code_verifier;
|
|
62
|
+
let code_challenge;
|
|
63
|
+
if (SigninRequest.isCode(response_type)) {
|
|
64
|
+
code_verifier = generateRandom() + generateRandom() + generateRandom();
|
|
65
|
+
code_challenge = await JoseUtil.calculatePKCECodeChallenge(code_verifier);
|
|
66
|
+
}
|
|
67
|
+
const signinRequest = new SigninRequest({
|
|
68
|
+
url: url,
|
|
69
|
+
client_id: client_id,
|
|
70
|
+
redirect_uri: redirect_uri,
|
|
71
|
+
response_type: response_type,
|
|
72
|
+
scope: scope,
|
|
73
|
+
data: data || state,
|
|
74
|
+
authority: authority,
|
|
75
|
+
prompt,
|
|
76
|
+
display,
|
|
77
|
+
max_age,
|
|
78
|
+
ui_locales,
|
|
79
|
+
id_token_hint,
|
|
80
|
+
login_hint,
|
|
81
|
+
acr_values,
|
|
82
|
+
resource,
|
|
83
|
+
request,
|
|
84
|
+
request_uri,
|
|
85
|
+
extraQueryParams,
|
|
86
|
+
extraTokenParams,
|
|
87
|
+
request_type,
|
|
88
|
+
response_mode,
|
|
89
|
+
client_secret: this._settings.client_secret,
|
|
90
|
+
skipUserInfo,
|
|
91
|
+
code_verifier,
|
|
92
|
+
code_challenge,
|
|
93
|
+
});
|
|
94
|
+
const signinState = signinRequest.state;
|
|
95
|
+
const store = stateStore || this._stateStore;
|
|
96
|
+
await store.set(signinState.id, signinState.toStorageString());
|
|
97
|
+
return signinRequest;
|
|
98
|
+
}
|
|
99
|
+
readSigninResponseState(url, stateStore, removeState = false) {
|
|
100
|
+
Log.debug('OidcClient.readSigninResponseState');
|
|
101
|
+
const useQuery = this._settings.response_mode === 'query' ||
|
|
102
|
+
(!this._settings.response_mode && SigninRequest.isCode(this._settings.response_type));
|
|
103
|
+
const delimiter = useQuery ? '?' : '#';
|
|
104
|
+
const response = new SigninResponse(url, delimiter);
|
|
105
|
+
if (!response.state) {
|
|
106
|
+
Log.error('OidcClient.readSigninResponseState: No state in response');
|
|
107
|
+
return Promise.reject(new Error('No state in response'));
|
|
108
|
+
}
|
|
109
|
+
const store = stateStore || this._stateStore;
|
|
110
|
+
const stateApi = removeState
|
|
111
|
+
? store.remove.bind(store)
|
|
112
|
+
: store.get.bind(store);
|
|
113
|
+
return stateApi(response.state).then(storedStateString => {
|
|
114
|
+
if (!storedStateString) {
|
|
115
|
+
Log.error('OidcClient.readSigninResponseState: No matching state found in storage');
|
|
116
|
+
throw new Error('No matching state found in storage');
|
|
117
|
+
}
|
|
118
|
+
const state = SigninState.fromStorageString(storedStateString);
|
|
119
|
+
return { state, response };
|
|
120
|
+
});
|
|
121
|
+
}
|
|
122
|
+
processSigninResponse(url, stateStore) {
|
|
123
|
+
Log.debug('OidcClient.processSigninResponse');
|
|
124
|
+
return this.readSigninResponseState(url, stateStore, true).then(({ state, response }) => {
|
|
125
|
+
Log.debug('OidcClient.processSigninResponse: Received state from storage; validating response');
|
|
126
|
+
return this._validator.validateSigninResponse(state, response);
|
|
127
|
+
});
|
|
128
|
+
}
|
|
129
|
+
createSignoutRequest({ id_token_hint, data, state, post_logout_redirect_uri, extraQueryParams, request_type, } = {}, stateStore) {
|
|
130
|
+
Log.debug('OidcClient.createSignoutRequest');
|
|
131
|
+
post_logout_redirect_uri = post_logout_redirect_uri || this._settings.post_logout_redirect_uri;
|
|
132
|
+
extraQueryParams = extraQueryParams || this._settings.extraQueryParams;
|
|
133
|
+
return this._metadataService.getEndSessionEndpoint().then(url => {
|
|
134
|
+
if (!url) {
|
|
135
|
+
Log.error('OidcClient.createSignoutRequest: No end session endpoint url returned');
|
|
136
|
+
throw new Error('no end session endpoint');
|
|
137
|
+
}
|
|
138
|
+
Log.debug('OidcClient.createSignoutRequest: Received end session endpoint', url);
|
|
139
|
+
const request = new SignoutRequest({
|
|
140
|
+
url,
|
|
141
|
+
id_token_hint,
|
|
142
|
+
post_logout_redirect_uri,
|
|
143
|
+
data: data || state,
|
|
144
|
+
extraQueryParams,
|
|
145
|
+
request_type,
|
|
146
|
+
});
|
|
147
|
+
const signoutState = request.state;
|
|
148
|
+
if (signoutState) {
|
|
149
|
+
Log.debug('OidcClient.createSignoutRequest: Signout request has state to persist');
|
|
150
|
+
const store = stateStore || this._stateStore;
|
|
151
|
+
store.set(signoutState.id, signoutState.toStorageString());
|
|
152
|
+
}
|
|
153
|
+
return request;
|
|
154
|
+
});
|
|
155
|
+
}
|
|
156
|
+
readSignoutResponseState(url, stateStore, removeState = false) {
|
|
157
|
+
Log.debug('OidcClient.readSignoutResponseState');
|
|
158
|
+
const response = new SignoutResponse(url);
|
|
159
|
+
if (!response.state) {
|
|
160
|
+
Log.debug('OidcClient.readSignoutResponseState: No state in response');
|
|
161
|
+
if (response.error) {
|
|
162
|
+
Log.warn('OidcClient.readSignoutResponseState: Response was error: ', response.error);
|
|
163
|
+
return Promise.reject(new ErrorResponse({
|
|
164
|
+
error: response.error,
|
|
165
|
+
error_description: response.error_description,
|
|
166
|
+
error_uri: response.error_uri,
|
|
167
|
+
state: typeof response.state === 'string' ? response.state : undefined,
|
|
168
|
+
}));
|
|
169
|
+
}
|
|
170
|
+
return Promise.resolve({ state: undefined, response });
|
|
171
|
+
}
|
|
172
|
+
const stateKey = response.state;
|
|
173
|
+
const store = stateStore || this._stateStore;
|
|
174
|
+
const stateApi = removeState
|
|
175
|
+
? store.remove.bind(store)
|
|
176
|
+
: store.get.bind(store);
|
|
177
|
+
return stateApi(stateKey).then(storedStateString => {
|
|
178
|
+
if (!storedStateString) {
|
|
179
|
+
Log.error('OidcClient.readSignoutResponseState: No matching state found in storage');
|
|
180
|
+
throw new Error('No matching state found in storage');
|
|
181
|
+
}
|
|
182
|
+
const state = State.fromStorageString(storedStateString);
|
|
183
|
+
return { state, response };
|
|
184
|
+
});
|
|
185
|
+
}
|
|
186
|
+
processSignoutResponse(url, stateStore) {
|
|
187
|
+
Log.debug('OidcClient.processSignoutResponse');
|
|
188
|
+
return this.readSignoutResponseState(url, stateStore, true).then(({ state, response }) => {
|
|
189
|
+
if (state) {
|
|
190
|
+
Log.debug('OidcClient.processSignoutResponse: Received state from storage; validating response');
|
|
191
|
+
return this._validator.validateSignoutResponse(state, response);
|
|
192
|
+
}
|
|
193
|
+
else {
|
|
194
|
+
Log.debug('OidcClient.processSignoutResponse: No state from storage; skipping validating response');
|
|
195
|
+
return response;
|
|
196
|
+
}
|
|
197
|
+
});
|
|
198
|
+
}
|
|
199
|
+
clearStaleState(stateStore) {
|
|
200
|
+
Log.debug('OidcClient.clearStaleState');
|
|
201
|
+
const store = stateStore || this._stateStore;
|
|
202
|
+
return State.clearStaleState(store, this.settings.staleStateAge);
|
|
203
|
+
}
|
|
204
|
+
}
|
|
205
|
+
export class UserManager extends OidcClient {
|
|
206
|
+
constructor(settings = {}, SilentRenewServiceCtor = SilentRenewService, SessionMonitorCtor = SessionMonitor, TokenRevocationClientCtor = TokenRevocationClient, TokenClientCtor = TokenClient, joseUtil = JoseUtil) {
|
|
207
|
+
if (!(settings instanceof UserManagerSettings)) {
|
|
208
|
+
settings = new UserManagerSettings(settings);
|
|
209
|
+
}
|
|
210
|
+
super(settings);
|
|
211
|
+
this._settings = settings;
|
|
212
|
+
this._events = new UserManagerEvents(settings);
|
|
213
|
+
this._silentRenewService = new SilentRenewServiceCtor(this);
|
|
214
|
+
// order is important for the following properties; these services depend upon the events.
|
|
215
|
+
if (this.settings.automaticSilentRenew) {
|
|
216
|
+
Log.debug('UserManager.ctor: automaticSilentRenew is configured, setting up silent renew');
|
|
217
|
+
this.startSilentRenew();
|
|
218
|
+
}
|
|
219
|
+
if (this.settings.monitorSession) {
|
|
220
|
+
Log.debug('UserManager.ctor: monitorSession is configured, setting up session monitor');
|
|
221
|
+
this._sessionMonitor = new SessionMonitorCtor(this);
|
|
222
|
+
}
|
|
223
|
+
this._tokenRevocationClient = new TokenRevocationClientCtor(this._settings);
|
|
224
|
+
this._tokenClient = new TokenClientCtor(this._settings);
|
|
225
|
+
this._joseUtil = joseUtil;
|
|
226
|
+
}
|
|
227
|
+
get settings() {
|
|
228
|
+
return this._settings;
|
|
229
|
+
}
|
|
230
|
+
get _redirectNavigator() {
|
|
231
|
+
return this.settings.redirectNavigator;
|
|
232
|
+
}
|
|
233
|
+
get _popupNavigator() {
|
|
234
|
+
return this.settings.popupNavigator;
|
|
235
|
+
}
|
|
236
|
+
get _iframeNavigator() {
|
|
237
|
+
return this.settings.iframeNavigator;
|
|
238
|
+
}
|
|
239
|
+
get _userStore() {
|
|
240
|
+
return this.settings.userStore;
|
|
241
|
+
}
|
|
242
|
+
get events() {
|
|
243
|
+
return this._events;
|
|
244
|
+
}
|
|
245
|
+
getUser() {
|
|
246
|
+
return this._loadUser().then(user => {
|
|
247
|
+
if (user) {
|
|
248
|
+
Log.info('UserManager.getUser: user loaded');
|
|
249
|
+
this._events.load(user, false);
|
|
250
|
+
return user;
|
|
251
|
+
}
|
|
252
|
+
else {
|
|
253
|
+
Log.info('UserManager.getUser: user not found in storage');
|
|
254
|
+
return null;
|
|
255
|
+
}
|
|
256
|
+
});
|
|
257
|
+
}
|
|
258
|
+
removeUser() {
|
|
259
|
+
return this.storeUser(null).then(() => {
|
|
260
|
+
Log.info('UserManager.removeUser: user removed from storage');
|
|
261
|
+
this._events.unload();
|
|
262
|
+
});
|
|
263
|
+
}
|
|
264
|
+
signinRedirect(args = {}) {
|
|
265
|
+
args = Object.assign({}, args);
|
|
266
|
+
args.request_type = 'si:r';
|
|
267
|
+
const navParams = {
|
|
268
|
+
useReplaceToNavigate: args.useReplaceToNavigate,
|
|
269
|
+
};
|
|
270
|
+
return this._signinStart(args, this._redirectNavigator, navParams).then(() => {
|
|
271
|
+
Log.info('UserManager.signinRedirect: successful');
|
|
272
|
+
});
|
|
273
|
+
}
|
|
274
|
+
signinRedirectCallback(url) {
|
|
275
|
+
return this._signinEnd(url || this._redirectNavigator.url).then(user => {
|
|
276
|
+
if (user.profile && user.profile.sub) {
|
|
277
|
+
Log.info('UserManager.signinRedirectCallback: successful, signed in sub: ', user.profile.sub);
|
|
278
|
+
}
|
|
279
|
+
else {
|
|
280
|
+
Log.info('UserManager.signinRedirectCallback: no sub');
|
|
281
|
+
}
|
|
282
|
+
return user;
|
|
283
|
+
});
|
|
284
|
+
}
|
|
285
|
+
signinPopup(args = {}) {
|
|
286
|
+
args = Object.assign({}, args);
|
|
287
|
+
args.request_type = 'si:p';
|
|
288
|
+
const url = args.redirect_uri || this.settings.popup_redirect_uri || this.settings.redirect_uri;
|
|
289
|
+
if (!url) {
|
|
290
|
+
Log.error('UserManager.signinPopup: No popup_redirect_uri or redirect_uri configured');
|
|
291
|
+
return Promise.reject(new Error('No popup_redirect_uri or redirect_uri configured'));
|
|
292
|
+
}
|
|
293
|
+
args.redirect_uri = url;
|
|
294
|
+
args.display = 'popup';
|
|
295
|
+
return this._signin(args, this._popupNavigator, {
|
|
296
|
+
startUrl: url,
|
|
297
|
+
popupWindowFeatures: args.popupWindowFeatures || this.settings.popupWindowFeatures,
|
|
298
|
+
popupWindowTarget: args.popupWindowTarget || this.settings.popupWindowTarget,
|
|
299
|
+
}).then(user => {
|
|
300
|
+
if (user.profile && user.profile.sub) {
|
|
301
|
+
Log.info('UserManager.signinPopup: signinPopup successful, signed in sub: ', user.profile.sub);
|
|
302
|
+
}
|
|
303
|
+
else {
|
|
304
|
+
Log.info('UserManager.signinPopup: no sub');
|
|
305
|
+
}
|
|
306
|
+
return user;
|
|
307
|
+
});
|
|
308
|
+
}
|
|
309
|
+
signinPopupCallback(url) {
|
|
310
|
+
return this._signinCallback(url, this._popupNavigator)
|
|
311
|
+
.catch((err) => {
|
|
312
|
+
Log.error('UserManager.signinPopupCallback error: ' + err?.message);
|
|
313
|
+
return undefined;
|
|
314
|
+
});
|
|
315
|
+
}
|
|
316
|
+
signinSilent(args = {}) {
|
|
317
|
+
args = Object.assign({}, args);
|
|
318
|
+
// first determine if we have a refresh token, or need to use iframe
|
|
319
|
+
return this._loadUser().then(user => {
|
|
320
|
+
if (user && user.refresh_token) {
|
|
321
|
+
args.refresh_token = user.refresh_token;
|
|
322
|
+
return this._useRefreshToken(args);
|
|
323
|
+
}
|
|
324
|
+
else {
|
|
325
|
+
args.request_type = 'si:s';
|
|
326
|
+
args.id_token_hint =
|
|
327
|
+
args.id_token_hint ||
|
|
328
|
+
(this.settings.includeIdTokenInSilentRenew && user && user.id_token) ||
|
|
329
|
+
undefined;
|
|
330
|
+
if (user && this._settings.validateSubOnSilentRenew) {
|
|
331
|
+
Log.debug('UserManager.signinSilent, subject prior to silent renew: ', user.profile?.sub);
|
|
332
|
+
args.current_sub = user.profile?.sub;
|
|
333
|
+
}
|
|
334
|
+
return this._signinSilentIframe(args);
|
|
335
|
+
}
|
|
336
|
+
});
|
|
337
|
+
}
|
|
338
|
+
_useRefreshToken(args = {}) {
|
|
339
|
+
return this._tokenClient.exchangeRefreshToken(args).then(result => {
|
|
340
|
+
if (!result) {
|
|
341
|
+
Log.error('UserManager._useRefreshToken: No response returned from token endpoint');
|
|
342
|
+
return Promise.reject('No response returned from token endpoint');
|
|
343
|
+
}
|
|
344
|
+
if (!result.access_token) {
|
|
345
|
+
Log.error('UserManager._useRefreshToken: No access token returned from token endpoint');
|
|
346
|
+
return Promise.reject('No access token returned from token endpoint');
|
|
347
|
+
}
|
|
348
|
+
return this._loadUser().then(user => {
|
|
349
|
+
if (user) {
|
|
350
|
+
let idTokenValidation = Promise.resolve();
|
|
351
|
+
if (result.id_token) {
|
|
352
|
+
idTokenValidation = this._validateIdTokenFromTokenRefreshToken(user.profile, result.id_token);
|
|
353
|
+
}
|
|
354
|
+
return idTokenValidation.then(() => {
|
|
355
|
+
Log.debug('UserManager._useRefreshToken: refresh token response success');
|
|
356
|
+
user.id_token = result.id_token || user.id_token;
|
|
357
|
+
user.access_token = result.access_token;
|
|
358
|
+
user.refresh_token = result.refresh_token || user.refresh_token;
|
|
359
|
+
user.expires_in = result.expires_in;
|
|
360
|
+
return this.storeUser(user).then(() => {
|
|
361
|
+
this._events.load(user);
|
|
362
|
+
return user;
|
|
363
|
+
});
|
|
364
|
+
});
|
|
365
|
+
}
|
|
366
|
+
else {
|
|
367
|
+
return null;
|
|
368
|
+
}
|
|
369
|
+
});
|
|
370
|
+
});
|
|
371
|
+
}
|
|
372
|
+
_validateIdTokenFromTokenRefreshToken(profile, id_token) {
|
|
373
|
+
return this._metadataService.getIssuer().then(issuer => {
|
|
374
|
+
return this.settings.getEpochTime().then(now => {
|
|
375
|
+
return this._joseUtil
|
|
376
|
+
.validateJwtAttributes(id_token, issuer, this._settings.client_id, this._settings.clockSkew, now)
|
|
377
|
+
.then(payload => {
|
|
378
|
+
if (!payload) {
|
|
379
|
+
Log.error('UserManager._validateIdTokenFromTokenRefreshToken: Failed to validate id_token');
|
|
380
|
+
return Promise.reject(new Error('Failed to validate id_token'));
|
|
381
|
+
}
|
|
382
|
+
if (payload.sub !== profile.sub) {
|
|
383
|
+
Log.error('UserManager._validateIdTokenFromTokenRefreshToken: sub in id_token does not match current sub');
|
|
384
|
+
return Promise.reject(new Error('sub in id_token does not match current sub'));
|
|
385
|
+
}
|
|
386
|
+
if (payload.auth_time && payload.auth_time !== profile.auth_time) {
|
|
387
|
+
Log.error('UserManager._validateIdTokenFromTokenRefreshToken: auth_time in id_token does not match original auth_time');
|
|
388
|
+
return Promise.reject(new Error('auth_time in id_token does not match original auth_time'));
|
|
389
|
+
}
|
|
390
|
+
if (payload.azp && payload.azp !== profile.azp) {
|
|
391
|
+
Log.error('UserManager._validateIdTokenFromTokenRefreshToken: azp in id_token does not match original azp');
|
|
392
|
+
return Promise.reject(new Error('azp in id_token does not match original azp'));
|
|
393
|
+
}
|
|
394
|
+
if (!payload.azp && profile.azp) {
|
|
395
|
+
Log.error('UserManager._validateIdTokenFromTokenRefreshToken: azp not in id_token, but present in original id_token');
|
|
396
|
+
return Promise.reject(new Error('azp not in id_token, but present in original id_token'));
|
|
397
|
+
}
|
|
398
|
+
});
|
|
399
|
+
});
|
|
400
|
+
});
|
|
401
|
+
}
|
|
402
|
+
_signinSilentIframe(args = {}) {
|
|
403
|
+
const url = args.redirect_uri || this.settings.silent_redirect_uri || this.settings.redirect_uri;
|
|
404
|
+
if (!url) {
|
|
405
|
+
Log.error('UserManager.signinSilent: No silent_redirect_uri configured');
|
|
406
|
+
return Promise.reject(new Error('No silent_redirect_uri configured'));
|
|
407
|
+
}
|
|
408
|
+
args.redirect_uri = url;
|
|
409
|
+
args.prompt = args.prompt || 'none';
|
|
410
|
+
return this._signin(args, this._iframeNavigator, {
|
|
411
|
+
startUrl: url,
|
|
412
|
+
silentRequestTimeout: args.silentRequestTimeout || this.settings.silentRequestTimeout,
|
|
413
|
+
}).then(user => {
|
|
414
|
+
if (user.profile && user.profile.sub) {
|
|
415
|
+
Log.info('UserManager.signinSilent: successful, signed in sub: ', user.profile.sub);
|
|
416
|
+
}
|
|
417
|
+
else {
|
|
418
|
+
Log.info('UserManager.signinSilent: no sub');
|
|
419
|
+
}
|
|
420
|
+
return user;
|
|
421
|
+
});
|
|
422
|
+
}
|
|
423
|
+
signinSilentCallback(url) {
|
|
424
|
+
return this._signinCallback(url, this._iframeNavigator);
|
|
425
|
+
}
|
|
426
|
+
signinCallback(url) {
|
|
427
|
+
return this.readSigninResponseState(url).then(({ state, response }) => {
|
|
428
|
+
if (state.request_type === 'si:r') {
|
|
429
|
+
return this.signinRedirectCallback(url);
|
|
430
|
+
}
|
|
431
|
+
if (state.request_type === 'si:p') {
|
|
432
|
+
return this.signinPopupCallback(url);
|
|
433
|
+
}
|
|
434
|
+
if (state.request_type === 'si:s') {
|
|
435
|
+
return this.signinSilentCallback(url);
|
|
436
|
+
}
|
|
437
|
+
return Promise.reject(new Error('invalid response_type in state'));
|
|
438
|
+
});
|
|
439
|
+
}
|
|
440
|
+
signoutCallback(url, keepOpen) {
|
|
441
|
+
return this.readSignoutResponseState(url).then(({ state, response }) => {
|
|
442
|
+
if (state) {
|
|
443
|
+
if (state.request_type === 'so:r') {
|
|
444
|
+
return this.signoutRedirectCallback(url);
|
|
445
|
+
}
|
|
446
|
+
if (state.request_type === 'so:p') {
|
|
447
|
+
return this.signoutPopupCallback(url, keepOpen);
|
|
448
|
+
}
|
|
449
|
+
return Promise.reject(new Error('invalid response_type in state'));
|
|
450
|
+
}
|
|
451
|
+
return response;
|
|
452
|
+
});
|
|
453
|
+
}
|
|
454
|
+
querySessionStatus(args = {}) {
|
|
455
|
+
args = Object.assign({}, args);
|
|
456
|
+
args.request_type = 'si:s'; // this acts like a signin silent
|
|
457
|
+
const url = args.redirect_uri || this.settings.silent_redirect_uri || this.settings.redirect_uri;
|
|
458
|
+
if (!url) {
|
|
459
|
+
Log.error('UserManager.querySessionStatus: No silent_redirect_uri configured');
|
|
460
|
+
return Promise.reject(new Error('No silent_redirect_uri configured'));
|
|
461
|
+
}
|
|
462
|
+
args.redirect_uri = url;
|
|
463
|
+
args.prompt = 'none';
|
|
464
|
+
args.response_type = args.response_type || this.settings.query_status_response_type;
|
|
465
|
+
args.scope = args.scope || 'openid';
|
|
466
|
+
args.skipUserInfo = true;
|
|
467
|
+
return this._signinStart(args, this._iframeNavigator, {
|
|
468
|
+
startUrl: url,
|
|
469
|
+
silentRequestTimeout: args.silentRequestTimeout || this.settings.silentRequestTimeout,
|
|
470
|
+
}).then(navResponse => {
|
|
471
|
+
return this.processSigninResponse(navResponse.url)
|
|
472
|
+
.then(signinResponse => {
|
|
473
|
+
Log.debug('UserManager.querySessionStatus: got signin response');
|
|
474
|
+
if (signinResponse.session_state && signinResponse.profile?.sub) {
|
|
475
|
+
Log.info('UserManager.querySessionStatus: querySessionStatus success for sub: ', signinResponse.profile.sub);
|
|
476
|
+
return {
|
|
477
|
+
session_state: signinResponse.session_state,
|
|
478
|
+
sub: signinResponse.profile.sub,
|
|
479
|
+
sid: signinResponse.profile.sid,
|
|
480
|
+
};
|
|
481
|
+
}
|
|
482
|
+
else {
|
|
483
|
+
Log.info('querySessionStatus successful, user not authenticated');
|
|
484
|
+
return null;
|
|
485
|
+
}
|
|
486
|
+
})
|
|
487
|
+
.catch(err => {
|
|
488
|
+
if (err.session_state && this.settings.monitorAnonymousSession) {
|
|
489
|
+
if (err.message === 'login_required' ||
|
|
490
|
+
err.message === 'consent_required' ||
|
|
491
|
+
err.message === 'interaction_required' ||
|
|
492
|
+
err.message === 'account_selection_required') {
|
|
493
|
+
Log.info('UserManager.querySessionStatus: querySessionStatus success for anonymous user');
|
|
494
|
+
return { session_state: err.session_state };
|
|
495
|
+
}
|
|
496
|
+
}
|
|
497
|
+
throw err;
|
|
498
|
+
});
|
|
499
|
+
});
|
|
500
|
+
}
|
|
501
|
+
_signin(args, navigator, navigatorParams = {}) {
|
|
502
|
+
return this._signinStart(args, navigator, navigatorParams).then(navResponse => {
|
|
503
|
+
return this._signinEnd(navResponse.url, args);
|
|
504
|
+
});
|
|
505
|
+
}
|
|
506
|
+
_signinStart(args, navigator, navigatorParams = {}) {
|
|
507
|
+
return navigator.prepare(navigatorParams).then(handle => {
|
|
508
|
+
Log.debug('UserManager._signinStart: got navigator window handle');
|
|
509
|
+
return this.createSigninRequest(args)
|
|
510
|
+
.then(signinRequest => {
|
|
511
|
+
Log.debug('UserManager._signinStart: got signin request');
|
|
512
|
+
const navigateParams = {
|
|
513
|
+
...navigatorParams,
|
|
514
|
+
url: signinRequest.url,
|
|
515
|
+
id: signinRequest.state.id,
|
|
516
|
+
};
|
|
517
|
+
return handle.navigate(navigateParams);
|
|
518
|
+
})
|
|
519
|
+
.catch((err) => {
|
|
520
|
+
if (handle.close) {
|
|
521
|
+
Log.debug('UserManager._signinStart: Error after preparing navigator, closing navigator window');
|
|
522
|
+
handle.close();
|
|
523
|
+
}
|
|
524
|
+
throw err;
|
|
525
|
+
});
|
|
526
|
+
});
|
|
527
|
+
}
|
|
528
|
+
_signinEnd(url, args = {}) {
|
|
529
|
+
return this.processSigninResponse(url).then(signinResponse => {
|
|
530
|
+
Log.debug('UserManager._signinEnd: got signin response');
|
|
531
|
+
const user = new User(signinResponse);
|
|
532
|
+
if (args.current_sub) {
|
|
533
|
+
if (args.current_sub !== user.profile?.sub) {
|
|
534
|
+
Log.debug('UserManager._signinEnd: current user does not match user returned from signin. sub from signin: ', user.profile?.sub);
|
|
535
|
+
return Promise.reject(new Error('login_required'));
|
|
536
|
+
}
|
|
537
|
+
else {
|
|
538
|
+
Log.debug('UserManager._signinEnd: current user matches user returned from signin');
|
|
539
|
+
}
|
|
540
|
+
}
|
|
541
|
+
return this.storeUser(user).then(() => {
|
|
542
|
+
Log.debug('UserManager._signinEnd: user stored');
|
|
543
|
+
this._events.load(user);
|
|
544
|
+
return user;
|
|
545
|
+
});
|
|
546
|
+
});
|
|
547
|
+
}
|
|
548
|
+
_signinCallback(url, navigator) {
|
|
549
|
+
Log.debug('UserManager._signinCallback');
|
|
550
|
+
const useQuery = this._settings.response_mode === 'query' ||
|
|
551
|
+
(!this._settings.response_mode && SigninRequest.isCode(this._settings.response_type));
|
|
552
|
+
const delimiter = useQuery ? '?' : '#';
|
|
553
|
+
return navigator.callback(url, undefined, delimiter).then(() => undefined);
|
|
554
|
+
}
|
|
555
|
+
signoutRedirect(args = {}) {
|
|
556
|
+
args = Object.assign({}, args);
|
|
557
|
+
args.request_type = 'so:r';
|
|
558
|
+
const postLogoutRedirectUri = args.post_logout_redirect_uri || this.settings.post_logout_redirect_uri;
|
|
559
|
+
if (postLogoutRedirectUri) {
|
|
560
|
+
args.post_logout_redirect_uri = postLogoutRedirectUri;
|
|
561
|
+
}
|
|
562
|
+
const navParams = {
|
|
563
|
+
useReplaceToNavigate: args.useReplaceToNavigate,
|
|
564
|
+
};
|
|
565
|
+
return this._signoutStart(args, this._redirectNavigator, navParams).then(() => {
|
|
566
|
+
Log.info('UserManager.signoutRedirect: successful');
|
|
567
|
+
});
|
|
568
|
+
}
|
|
569
|
+
signoutRedirectCallback(url) {
|
|
570
|
+
return this._signoutEnd(url || this._redirectNavigator.url).then(response => {
|
|
571
|
+
Log.info('UserManager.signoutRedirectCallback: successful');
|
|
572
|
+
return response;
|
|
573
|
+
});
|
|
574
|
+
}
|
|
575
|
+
signoutPopup(args = {}) {
|
|
576
|
+
args = Object.assign({}, args);
|
|
577
|
+
args.request_type = 'so:p';
|
|
578
|
+
const url = args.post_logout_redirect_uri ||
|
|
579
|
+
this.settings.popup_post_logout_redirect_uri ||
|
|
580
|
+
this.settings.post_logout_redirect_uri;
|
|
581
|
+
args.post_logout_redirect_uri = url;
|
|
582
|
+
args.display = 'popup';
|
|
583
|
+
if (args.post_logout_redirect_uri) {
|
|
584
|
+
args.state = args.state || {};
|
|
585
|
+
}
|
|
586
|
+
return this._signout(args, this._popupNavigator, {
|
|
587
|
+
startUrl: url,
|
|
588
|
+
popupWindowFeatures: args.popupWindowFeatures || this.settings.popupWindowFeatures,
|
|
589
|
+
popupWindowTarget: args.popupWindowTarget || this.settings.popupWindowTarget,
|
|
590
|
+
}).then(() => {
|
|
591
|
+
Log.info('UserManager.signoutPopup: successful');
|
|
592
|
+
});
|
|
593
|
+
}
|
|
594
|
+
signoutPopupCallback(url, keepOpen) {
|
|
595
|
+
if (typeof keepOpen === 'undefined' && typeof url === 'boolean') {
|
|
596
|
+
keepOpen = url;
|
|
597
|
+
url = undefined;
|
|
598
|
+
}
|
|
599
|
+
const delimiter = '?';
|
|
600
|
+
return this._popupNavigator.callback(url, keepOpen, delimiter).then(() => {
|
|
601
|
+
Log.info('UserManager.signoutPopupCallback: successful');
|
|
602
|
+
});
|
|
603
|
+
}
|
|
604
|
+
_signout(args, navigator, navigatorParams = {}) {
|
|
605
|
+
return this._signoutStart(args, navigator, navigatorParams).then(navResponse => {
|
|
606
|
+
return this._signoutEnd(navResponse.url);
|
|
607
|
+
});
|
|
608
|
+
}
|
|
609
|
+
_signoutStart(args = {}, navigator, navigatorParams = {}) {
|
|
610
|
+
return navigator.prepare(navigatorParams).then(handle => {
|
|
611
|
+
Log.debug('UserManager._signoutStart: got navigator window handle');
|
|
612
|
+
return this._loadUser()
|
|
613
|
+
.then(user => {
|
|
614
|
+
Log.debug('UserManager._signoutStart: loaded current user from storage');
|
|
615
|
+
const revokePromise = this._settings.revokeAccessTokenOnSignout
|
|
616
|
+
? this._revokeInternal(user)
|
|
617
|
+
: Promise.resolve(false);
|
|
618
|
+
return revokePromise.then(() => {
|
|
619
|
+
const id_token = args.id_token_hint || (user && user.id_token);
|
|
620
|
+
if (id_token) {
|
|
621
|
+
Log.debug('UserManager._signoutStart: Setting id_token into signout request');
|
|
622
|
+
args.id_token_hint = id_token;
|
|
623
|
+
}
|
|
624
|
+
return this.removeUser().then(() => {
|
|
625
|
+
Log.debug('UserManager._signoutStart: user removed, creating signout request');
|
|
626
|
+
return this.createSignoutRequest(args).then(signoutRequest => {
|
|
627
|
+
Log.debug('UserManager._signoutStart: got signout request');
|
|
628
|
+
const navigateParams = {
|
|
629
|
+
...navigatorParams,
|
|
630
|
+
url: signoutRequest.url,
|
|
631
|
+
id: signoutRequest.state?.id,
|
|
632
|
+
};
|
|
633
|
+
return handle.navigate(navigateParams);
|
|
634
|
+
});
|
|
635
|
+
});
|
|
636
|
+
});
|
|
637
|
+
})
|
|
638
|
+
.catch((err) => {
|
|
639
|
+
if (handle.close) {
|
|
640
|
+
Log.debug('UserManager._signoutStart: Error after preparing navigator, closing navigator window');
|
|
641
|
+
handle.close();
|
|
642
|
+
}
|
|
643
|
+
throw err;
|
|
644
|
+
});
|
|
645
|
+
});
|
|
646
|
+
}
|
|
647
|
+
_signoutEnd(url) {
|
|
648
|
+
return this.processSignoutResponse(url).then(signoutResponse => {
|
|
649
|
+
Log.debug('UserManager._signoutEnd: got signout response');
|
|
650
|
+
return signoutResponse;
|
|
651
|
+
});
|
|
652
|
+
}
|
|
653
|
+
revokeAccessToken() {
|
|
654
|
+
return this._loadUser()
|
|
655
|
+
.then(user => {
|
|
656
|
+
return this._revokeInternal(user, true).then(success => {
|
|
657
|
+
if (success) {
|
|
658
|
+
Log.debug('UserManager.revokeAccessToken: removing token properties from user and re-storing');
|
|
659
|
+
user.access_token = '';
|
|
660
|
+
user.refresh_token = '';
|
|
661
|
+
user.expires_at = undefined;
|
|
662
|
+
user.token_type = '';
|
|
663
|
+
return this.storeUser(user).then(() => {
|
|
664
|
+
Log.debug('UserManager.revokeAccessToken: user stored');
|
|
665
|
+
this._events.load(user);
|
|
666
|
+
});
|
|
667
|
+
}
|
|
668
|
+
return;
|
|
669
|
+
});
|
|
670
|
+
})
|
|
671
|
+
.then(() => {
|
|
672
|
+
Log.info('UserManager.revokeAccessToken: access token revoked successfully');
|
|
673
|
+
});
|
|
674
|
+
}
|
|
675
|
+
_revokeInternal(user, required) {
|
|
676
|
+
if (user) {
|
|
677
|
+
const access_token = user.access_token;
|
|
678
|
+
const refresh_token = user.refresh_token;
|
|
679
|
+
return this._revokeAccessTokenInternal(access_token, required).then(atSuccess => {
|
|
680
|
+
return this._revokeRefreshTokenInternal(refresh_token, required).then(rtSuccess => {
|
|
681
|
+
if (!atSuccess && !rtSuccess) {
|
|
682
|
+
Log.debug('UserManager.revokeAccessToken: no need to revoke due to no token(s), or JWT format');
|
|
683
|
+
}
|
|
684
|
+
return atSuccess || rtSuccess;
|
|
685
|
+
});
|
|
686
|
+
});
|
|
687
|
+
}
|
|
688
|
+
return Promise.resolve(false);
|
|
689
|
+
}
|
|
690
|
+
_revokeAccessTokenInternal(access_token, required) {
|
|
691
|
+
// check for JWT vs. reference token
|
|
692
|
+
if (!access_token || access_token.indexOf('.') >= 0) {
|
|
693
|
+
return Promise.resolve(false);
|
|
694
|
+
}
|
|
695
|
+
return this._tokenRevocationClient.revoke(access_token, required).then(() => true);
|
|
696
|
+
}
|
|
697
|
+
_revokeRefreshTokenInternal(refresh_token, required) {
|
|
698
|
+
if (!refresh_token) {
|
|
699
|
+
return Promise.resolve(false);
|
|
700
|
+
}
|
|
701
|
+
return this._tokenRevocationClient.revoke(refresh_token, required, 'refresh_token').then(() => true);
|
|
702
|
+
}
|
|
703
|
+
startSilentRenew() {
|
|
704
|
+
this._silentRenewService.start();
|
|
705
|
+
}
|
|
706
|
+
stopSilentRenew() {
|
|
707
|
+
this._silentRenewService.stop();
|
|
708
|
+
}
|
|
709
|
+
get _userStoreKey() {
|
|
710
|
+
return `user:${this.settings.authority}:${this.settings.client_id}`;
|
|
711
|
+
}
|
|
712
|
+
_loadUser() {
|
|
713
|
+
return this._userStore.get(this._userStoreKey).then(storageString => {
|
|
714
|
+
if (storageString) {
|
|
715
|
+
Log.debug('UserManager._loadUser: user storageString loaded');
|
|
716
|
+
return User.fromStorageString(storageString);
|
|
717
|
+
}
|
|
718
|
+
Log.debug('UserManager._loadUser: no user storageString');
|
|
719
|
+
return null;
|
|
720
|
+
});
|
|
721
|
+
}
|
|
722
|
+
storeUser(user) {
|
|
723
|
+
if (user) {
|
|
724
|
+
Log.debug('UserManager.storeUser: storing user');
|
|
725
|
+
const storageString = user.toStorageString();
|
|
726
|
+
return this._userStore.set(this._userStoreKey, storageString);
|
|
727
|
+
}
|
|
728
|
+
else {
|
|
729
|
+
Log.debug('storeUser.storeUser: removing user');
|
|
730
|
+
return this._userStore.remove(this._userStoreKey).then(() => { });
|
|
731
|
+
}
|
|
732
|
+
}
|
|
733
|
+
}
|
|
734
|
+
//# sourceMappingURL=Client.js.map
|