@inrupt/solid-client-authn-browser 1.17.1 → 1.17.3
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/dist/ClientAuthentication.d.ts +7 -0
- package/dist/Session.d.ts +174 -0
- package/dist/constant.d.ts +2 -2
- package/dist/defaultSession.d.ts +70 -0
- package/dist/dependencies.d.ts +12 -0
- package/dist/index.js +833 -34
- package/dist/index.js.map +1 -1
- package/dist/index.mjs +833 -34
- package/dist/index.mjs.map +1 -1
- package/dist/login/oidc/AggregateOidcHandler.d.ts +10 -0
- package/dist/login/oidc/AggregateRedirectHandler.d.ts +10 -0
- package/dist/login/oidc/ClientRegistrar.d.ts +7 -0
- package/dist/login/oidc/IssuerConfigFetcher.d.ts +10 -0
- package/dist/login/oidc/OidcLoginHandler.d.ts +10 -0
- package/dist/login/oidc/Redirector.d.ts +7 -0
- package/dist/login/oidc/incomingRedirectHandler/AuthCodeRedirectHandler.d.ts +3 -0
- package/dist/login/oidc/incomingRedirectHandler/ErrorOidcHandler.d.ts +10 -0
- package/dist/login/oidc/incomingRedirectHandler/FallbackRedirectHandler.d.ts +10 -0
- package/dist/login/oidc/oidcHandlers/AuthorizationCodeWithPkceOidcHandler.d.ts +12 -0
- package/dist/login/oidc/refresh/TokenRefresher.d.ts +7 -0
- package/dist/logout/buildRpInitiatedLogout.d.ts +7 -0
- package/dist/sessionInfo/SessionInfoManager.d.ts +18 -0
- package/dist/solid-client-authn.bundle.js +7 -7
- package/dist/solid-client-authn.bundle.js.map +1 -1
- package/dist/storage/BrowserStorage.d.ts +7 -0
- package/dist/storage/StorageUtility.d.ts +14 -0
- package/dist/util/UuidGenerator.d.ts +6 -0
- package/package.json +8 -8
- package/rollup.config.mjs +1 -1
- package/src/ClientAuthentication.spec.ts +106 -57
- package/src/ClientAuthentication.ts +18 -7
- package/src/Session.spec.ts +51 -54
- package/src/Session.ts +24 -18
- package/src/__mocks__/ClientAuthentication.ts +2 -2
- package/src/defaultSession.ts +4 -3
- package/src/dependencies.ts +5 -5
- package/src/login/oidc/ClientRegistrar.spec.ts +22 -22
- package/src/login/oidc/ClientRegistrar.ts +4 -2
- package/src/login/oidc/IssuerConfigFetcher.spec.ts +25 -25
- package/src/login/oidc/IssuerConfigFetcher.ts +8 -6
- package/src/login/oidc/OidcLoginHandler.spec.ts +15 -15
- package/src/login/oidc/OidcLoginHandler.ts +14 -9
- package/src/login/oidc/Redirector.spec.ts +1 -1
- package/src/login/oidc/__mocks__/ClientRegistrar.ts +3 -3
- package/src/login/oidc/__mocks__/IssuerConfigFetcher.ts +3 -3
- package/src/login/oidc/incomingRedirectHandler/AuthCodeRedirectHandler.spec.ts +42 -42
- package/src/login/oidc/incomingRedirectHandler/AuthCodeRedirectHandler.ts +19 -13
- package/src/login/oidc/incomingRedirectHandler/ErrorOidcHandler.spec.ts +12 -12
- package/src/login/oidc/incomingRedirectHandler/ErrorOidcHandler.ts +2 -2
- package/src/login/oidc/incomingRedirectHandler/FallbackRedirectHandler.spec.ts +7 -7
- package/src/login/oidc/incomingRedirectHandler/FallbackRedirectHandler.ts +2 -2
- package/src/login/oidc/oidcHandlers/AuthorizationCodeWithPkceOidcHandler.spec.ts +17 -17
- package/src/login/oidc/refresh/TokenRefresher.spec.ts +14 -14
- package/src/login/oidc/refresh/TokenRefresher.ts +12 -8
- package/src/login/oidc/refresh/__mocks__/TokenRefresher.ts +1 -1
- package/src/sessionInfo/SessionInfoManager.spec.ts +37 -15
- package/src/sessionInfo/SessionInfoManager.ts +46 -50
- package/src/sessionInfo/__mocks__/SessionInfoManager.ts +3 -3
package/dist/index.js
CHANGED
|
@@ -6,29 +6,98 @@ var EventEmitter = require('events');
|
|
|
6
6
|
var oidcClientExt = require('@inrupt/oidc-client-ext');
|
|
7
7
|
var universalFetch = require('@inrupt/universal-fetch');
|
|
8
8
|
|
|
9
|
+
//
|
|
10
|
+
// Copyright Inrupt Inc.
|
|
11
|
+
//
|
|
12
|
+
// Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
13
|
+
// of this software and associated documentation files (the "Software"), to deal in
|
|
14
|
+
// the Software without restriction, including without limitation the rights to use,
|
|
15
|
+
// copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the
|
|
16
|
+
// Software, and to permit persons to whom the Software is furnished to do so,
|
|
17
|
+
// subject to the following conditions:
|
|
18
|
+
//
|
|
19
|
+
// The above copyright notice and this permission notice shall be included in
|
|
20
|
+
// all copies or substantial portions of the Software.
|
|
21
|
+
//
|
|
22
|
+
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED,
|
|
23
|
+
// INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A
|
|
24
|
+
// PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT
|
|
25
|
+
// HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
|
|
26
|
+
// OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
|
|
27
|
+
// SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
|
28
|
+
//
|
|
29
|
+
/**
|
|
30
|
+
* This class in a no-value-added extension of StorageUtility from the core module.
|
|
31
|
+
* The reason it has to be declared is for TSyringe to find the decorators in the
|
|
32
|
+
* same modules as where the dependency container is declared (in this case,
|
|
33
|
+
* the browser module, with the dependancy container in dependencies.ts).
|
|
34
|
+
* @hidden
|
|
35
|
+
*/
|
|
9
36
|
class StorageUtilityBrowser extends solidClientAuthnCore.StorageUtility {
|
|
10
37
|
constructor(secureStorage, insecureStorage) {
|
|
11
38
|
super(secureStorage, insecureStorage);
|
|
12
39
|
}
|
|
13
40
|
}
|
|
14
41
|
|
|
42
|
+
//
|
|
43
|
+
// Copyright Inrupt Inc.
|
|
44
|
+
//
|
|
45
|
+
// Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
46
|
+
// of this software and associated documentation files (the "Software"), to deal in
|
|
47
|
+
// the Software without restriction, including without limitation the rights to use,
|
|
48
|
+
// copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the
|
|
49
|
+
// Software, and to permit persons to whom the Software is furnished to do so,
|
|
50
|
+
// subject to the following conditions:
|
|
51
|
+
//
|
|
52
|
+
// The above copyright notice and this permission notice shall be included in
|
|
53
|
+
// all copies or substantial portions of the Software.
|
|
54
|
+
//
|
|
55
|
+
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED,
|
|
56
|
+
// INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A
|
|
57
|
+
// PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT
|
|
58
|
+
// HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
|
|
59
|
+
// OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
|
|
60
|
+
// SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
|
61
|
+
//
|
|
62
|
+
/**
|
|
63
|
+
* @hidden
|
|
64
|
+
*/
|
|
15
65
|
class ClientAuthentication extends solidClientAuthnCore.ClientAuthentication {
|
|
16
66
|
constructor() {
|
|
17
67
|
super(...arguments);
|
|
68
|
+
// Define these functions as properties so that they don't get accidentally re-bound.
|
|
69
|
+
// Isn't Javascript fun?
|
|
18
70
|
this.login = async (options, eventEmitter) => {
|
|
19
71
|
var _a, _b;
|
|
72
|
+
// In order to get a clean start, make sure that the session is logged out
|
|
73
|
+
// on login.
|
|
74
|
+
// But we may want to preserve our client application info, particularly if
|
|
75
|
+
// we used Dynamic Client Registration to register (since we don't
|
|
76
|
+
// necessarily want the user to have to register this app each time they
|
|
77
|
+
// login).
|
|
20
78
|
await this.sessionInfoManager.clear(options.sessionId);
|
|
79
|
+
// In the case of the user hitting the 'back' button in their browser, they
|
|
80
|
+
// could return to a previous redirect URL that contains OIDC params that
|
|
81
|
+
// are now longer valid. To be safe, strip relevant params now.
|
|
82
|
+
// If the user is providing a redirect IRI, it should not be modified, so
|
|
83
|
+
// normalization only applies if we default to the current location (which is
|
|
84
|
+
// a bad practice and should be discouraged).
|
|
21
85
|
const redirectUrl = (_a = options.redirectUrl) !== null && _a !== void 0 ? _a : oidcClientExt.removeOidcQueryParam(window.location.href);
|
|
22
86
|
if (!solidClientAuthnCore.isValidRedirectUrl(redirectUrl)) {
|
|
23
|
-
throw new Error(`${redirectUrl} is not a valid redirect URL, it is either a malformed IRI
|
|
87
|
+
throw new Error(`${redirectUrl} is not a valid redirect URL, it is either a malformed IRI, includes a hash fragment, or reserved query parameters ('code' or 'state').`);
|
|
24
88
|
}
|
|
25
89
|
await this.loginHandler.handle({
|
|
26
90
|
...options,
|
|
27
91
|
redirectUrl,
|
|
92
|
+
// If no clientName is provided, the clientId may be used instead.
|
|
28
93
|
clientName: (_b = options.clientName) !== null && _b !== void 0 ? _b : options.clientId,
|
|
29
94
|
eventEmitter,
|
|
30
95
|
});
|
|
31
96
|
};
|
|
97
|
+
// Collects session information from storage, and returns them. Returns null
|
|
98
|
+
// if the expected information cannot be found.
|
|
99
|
+
// Note that the ID token is not stored, which means the session information
|
|
100
|
+
// cannot be validated at this point.
|
|
32
101
|
this.validateCurrentSession = async (currentSessionId) => {
|
|
33
102
|
const sessionInfo = await this.sessionInfoManager.get(currentSessionId);
|
|
34
103
|
if (sessionInfo === undefined ||
|
|
@@ -41,9 +110,14 @@ class ClientAuthentication extends solidClientAuthnCore.ClientAuthentication {
|
|
|
41
110
|
this.handleIncomingRedirect = async (url, eventEmitter) => {
|
|
42
111
|
try {
|
|
43
112
|
const redirectInfo = await this.redirectHandler.handle(url, eventEmitter);
|
|
113
|
+
// The `FallbackRedirectHandler` directly returns the global `fetch` for
|
|
114
|
+
// his value, so we should ensure it's bound to `window` rather than to
|
|
115
|
+
// ClientAuthentication, to avoid the following error:
|
|
116
|
+
// > 'fetch' called on an object that does not implement interface Window.
|
|
44
117
|
this.fetch = redirectInfo.fetch.bind(window);
|
|
45
118
|
this.boundLogout = redirectInfo.getLogoutUrl;
|
|
46
|
-
|
|
119
|
+
// Strip the oauth params:
|
|
120
|
+
await this.cleanUrlAfterRedirect(url);
|
|
47
121
|
return {
|
|
48
122
|
isLoggedIn: redirectInfo.isLoggedIn,
|
|
49
123
|
webId: redirectInfo.webId,
|
|
@@ -52,37 +126,89 @@ class ClientAuthentication extends solidClientAuthnCore.ClientAuthentication {
|
|
|
52
126
|
};
|
|
53
127
|
}
|
|
54
128
|
catch (err) {
|
|
55
|
-
|
|
129
|
+
// Strip the oauth params:
|
|
130
|
+
await this.cleanUrlAfterRedirect(url);
|
|
131
|
+
// FIXME: EVENTS.ERROR should be errorCode, errorDescription
|
|
132
|
+
//
|
|
133
|
+
// I'm not sure if "redirect" is a good error code, and in theory `err`
|
|
134
|
+
// maybe an Error object and not a string; Maybe we want to just hardcode
|
|
135
|
+
// a description instead?
|
|
56
136
|
eventEmitter.emit(solidClientAuthnCore.EVENTS.ERROR, "redirect", err);
|
|
57
137
|
return undefined;
|
|
58
138
|
}
|
|
59
139
|
};
|
|
60
140
|
}
|
|
61
|
-
cleanUrlAfterRedirect(url) {
|
|
141
|
+
async cleanUrlAfterRedirect(url) {
|
|
62
142
|
const cleanedUpUrl = new URL(url);
|
|
63
143
|
cleanedUpUrl.searchParams.delete("state");
|
|
144
|
+
// For auth code flow
|
|
64
145
|
cleanedUpUrl.searchParams.delete("code");
|
|
146
|
+
// For implicit flow
|
|
65
147
|
cleanedUpUrl.searchParams.delete("id_token");
|
|
66
148
|
cleanedUpUrl.searchParams.delete("access_token");
|
|
149
|
+
// For login error
|
|
67
150
|
cleanedUpUrl.searchParams.delete("error");
|
|
68
151
|
cleanedUpUrl.searchParams.delete("error_description");
|
|
69
152
|
cleanedUpUrl.searchParams.delete("iss");
|
|
153
|
+
// Remove OAuth-specific query params (since the login flow finishes with
|
|
154
|
+
// the browser being redirected back with OAuth2 query params (e.g. for
|
|
155
|
+
// 'code' and 'state'), and so if the user simply refreshes this page our
|
|
156
|
+
// authentication library will be called again with what are now invalid
|
|
157
|
+
// query parameters!).
|
|
70
158
|
window.history.replaceState(null, "", cleanedUpUrl.toString());
|
|
159
|
+
while (window.location.href !== cleanedUpUrl.href) {
|
|
160
|
+
// Poll the current URL every ms. Active polling is required because
|
|
161
|
+
// window.history.replaceState is asynchronous, but the associated
|
|
162
|
+
// 'popstate' event which should be listened to is only sent on active
|
|
163
|
+
// navigation, which we will not have here.
|
|
164
|
+
// See https://developer.mozilla.org/en-US/docs/Web/API/Window/popstate_event#when_popstate_is_sent
|
|
165
|
+
// eslint-disable-next-line no-await-in-loop
|
|
166
|
+
await new Promise((resolve) => {
|
|
167
|
+
setTimeout(() => resolve(), 1);
|
|
168
|
+
});
|
|
169
|
+
}
|
|
71
170
|
}
|
|
72
171
|
}
|
|
73
172
|
|
|
173
|
+
//
|
|
174
|
+
// Copyright Inrupt Inc.
|
|
175
|
+
//
|
|
176
|
+
// Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
177
|
+
// of this software and associated documentation files (the "Software"), to deal in
|
|
178
|
+
// the Software without restriction, including without limitation the rights to use,
|
|
179
|
+
// copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the
|
|
180
|
+
// Software, and to permit persons to whom the Software is furnished to do so,
|
|
181
|
+
// subject to the following conditions:
|
|
182
|
+
//
|
|
183
|
+
// The above copyright notice and this permission notice shall be included in
|
|
184
|
+
// all copies or substantial portions of the Software.
|
|
185
|
+
//
|
|
186
|
+
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED,
|
|
187
|
+
// INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A
|
|
188
|
+
// PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT
|
|
189
|
+
// HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
|
|
190
|
+
// OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
|
|
191
|
+
// SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
|
192
|
+
//
|
|
74
193
|
function hasIssuer(options) {
|
|
75
194
|
return typeof options.oidcIssuer === "string";
|
|
76
195
|
}
|
|
77
196
|
function hasRedirectUrl(options) {
|
|
78
197
|
return typeof options.redirectUrl === "string";
|
|
79
198
|
}
|
|
199
|
+
/**
|
|
200
|
+
* @hidden
|
|
201
|
+
*/
|
|
80
202
|
class OidcLoginHandler {
|
|
81
203
|
constructor(storageUtility, oidcHandler, issuerConfigFetcher, clientRegistrar) {
|
|
82
204
|
this.storageUtility = storageUtility;
|
|
83
205
|
this.oidcHandler = oidcHandler;
|
|
84
206
|
this.issuerConfigFetcher = issuerConfigFetcher;
|
|
85
207
|
this.clientRegistrar = clientRegistrar;
|
|
208
|
+
this.storageUtility = storageUtility;
|
|
209
|
+
this.oidcHandler = oidcHandler;
|
|
210
|
+
this.issuerConfigFetcher = issuerConfigFetcher;
|
|
211
|
+
this.clientRegistrar = clientRegistrar;
|
|
86
212
|
}
|
|
87
213
|
async canHandle(options) {
|
|
88
214
|
return hasIssuer(options) && hasRedirectUrl(options);
|
|
@@ -94,22 +220,57 @@ class OidcLoginHandler {
|
|
|
94
220
|
if (!hasRedirectUrl(options)) {
|
|
95
221
|
throw new solidClientAuthnCore.ConfigurationError(`OidcLoginHandler requires a redirect URL: missing property 'redirectUrl' in ${JSON.stringify(options)}`);
|
|
96
222
|
}
|
|
223
|
+
// Fetch issuer config.
|
|
97
224
|
const issuerConfig = await this.issuerConfigFetcher.fetchConfig(options.oidcIssuer);
|
|
98
225
|
const clientRegistration = await solidClientAuthnCore.handleRegistration(options, issuerConfig, this.storageUtility, this.clientRegistrar);
|
|
226
|
+
// Construct OIDC Options
|
|
99
227
|
const OidcOptions = {
|
|
228
|
+
// Note that here, the issuer is not the one from the received options, but
|
|
229
|
+
// from the issuer's config. This enforces the canonical URL is used and stored,
|
|
230
|
+
// which is also the one present in the ID token, so storing a technically
|
|
231
|
+
// valid, but different issuer URL (e.g. using a trailing slash or not) now
|
|
232
|
+
// could prevent from validating the ID token later.
|
|
100
233
|
issuer: issuerConfig.issuer,
|
|
234
|
+
// TODO: differentiate if DPoP should be true
|
|
101
235
|
dpop: options.tokenType.toLowerCase() === "dpop",
|
|
102
236
|
...options,
|
|
103
237
|
issuerConfiguration: issuerConfig,
|
|
104
238
|
client: clientRegistration,
|
|
105
239
|
};
|
|
240
|
+
// Call proper OIDC Handler
|
|
106
241
|
return this.oidcHandler.handle(OidcOptions);
|
|
107
242
|
}
|
|
108
243
|
}
|
|
109
244
|
|
|
245
|
+
//
|
|
246
|
+
// Copyright Inrupt Inc.
|
|
247
|
+
//
|
|
248
|
+
// Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
249
|
+
// of this software and associated documentation files (the "Software"), to deal in
|
|
250
|
+
// the Software without restriction, including without limitation the rights to use,
|
|
251
|
+
// copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the
|
|
252
|
+
// Software, and to permit persons to whom the Software is furnished to do so,
|
|
253
|
+
// subject to the following conditions:
|
|
254
|
+
//
|
|
255
|
+
// The above copyright notice and this permission notice shall be included in
|
|
256
|
+
// all copies or substantial portions of the Software.
|
|
257
|
+
//
|
|
258
|
+
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED,
|
|
259
|
+
// INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A
|
|
260
|
+
// PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT
|
|
261
|
+
// HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
|
|
262
|
+
// OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
|
|
263
|
+
// SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
|
264
|
+
//
|
|
265
|
+
/**
|
|
266
|
+
* @hidden
|
|
267
|
+
* Authorization code flow spec: https://openid.net/specs/openid-connect-core-1_0.html#CodeFlowAuth
|
|
268
|
+
* PKCE: https://tools.ietf.org/html/rfc7636
|
|
269
|
+
*/
|
|
110
270
|
class AuthorizationCodeWithPkceOidcHandler extends solidClientAuthnCore.AuthorizationCodeWithPkceOidcHandlerBase {
|
|
111
271
|
async handle(oidcLoginOptions) {
|
|
112
272
|
var _a;
|
|
273
|
+
/* eslint-disable camelcase */
|
|
113
274
|
const oidcOptions = {
|
|
114
275
|
authority: oidcLoginOptions.issuer.toString(),
|
|
115
276
|
client_id: oidcLoginOptions.client.clientId,
|
|
@@ -119,28 +280,58 @@ class AuthorizationCodeWithPkceOidcHandler extends solidClientAuthnCore.Authoriz
|
|
|
119
280
|
response_type: "code",
|
|
120
281
|
scope: solidClientAuthnCore.DEFAULT_SCOPES,
|
|
121
282
|
filterProtocolClaims: true,
|
|
283
|
+
// The userinfo endpoint on NSS fails, so disable this for now
|
|
284
|
+
// Note that in Solid, information should be retrieved from the
|
|
285
|
+
// profile referenced by the WebId.
|
|
122
286
|
loadUserInfo: false,
|
|
123
287
|
code_verifier: true,
|
|
124
288
|
prompt: (_a = oidcLoginOptions.prompt) !== null && _a !== void 0 ? _a : "consent",
|
|
125
289
|
};
|
|
290
|
+
/* eslint-enable camelcase */
|
|
126
291
|
const oidcClientLibrary = new oidcClientExt.OidcClient(oidcOptions);
|
|
127
292
|
try {
|
|
128
293
|
const signingRequest = await oidcClientLibrary.createSigninRequest();
|
|
294
|
+
// Make sure to await the promise before returning so that the error is caught.
|
|
129
295
|
return await this.handleRedirect({
|
|
130
296
|
oidcLoginOptions,
|
|
297
|
+
// eslint-disable-next-line no-underscore-dangle
|
|
131
298
|
state: signingRequest.state._id,
|
|
299
|
+
// eslint-disable-next-line no-underscore-dangle
|
|
132
300
|
codeVerifier: signingRequest.state._code_verifier,
|
|
133
301
|
targetUrl: signingRequest.url.toString(),
|
|
134
302
|
});
|
|
135
303
|
}
|
|
136
304
|
catch (err) {
|
|
305
|
+
// eslint-disable-next-line no-console
|
|
137
306
|
console.error(err);
|
|
138
307
|
}
|
|
308
|
+
// The login is only completed AFTER redirect, so nothing to return here.
|
|
139
309
|
return undefined;
|
|
140
310
|
}
|
|
141
311
|
}
|
|
142
312
|
|
|
313
|
+
//
|
|
314
|
+
// Copyright Inrupt Inc.
|
|
315
|
+
//
|
|
316
|
+
// Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
317
|
+
// of this software and associated documentation files (the "Software"), to deal in
|
|
318
|
+
// the Software without restriction, including without limitation the rights to use,
|
|
319
|
+
// copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the
|
|
320
|
+
// Software, and to permit persons to whom the Software is furnished to do so,
|
|
321
|
+
// subject to the following conditions:
|
|
322
|
+
//
|
|
323
|
+
// The above copyright notice and this permission notice shall be included in
|
|
324
|
+
// all copies or substantial portions of the Software.
|
|
325
|
+
//
|
|
326
|
+
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED,
|
|
327
|
+
// INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A
|
|
328
|
+
// PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT
|
|
329
|
+
// HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
|
|
330
|
+
// OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
|
|
331
|
+
// SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
|
332
|
+
//
|
|
143
333
|
const WELL_KNOWN_OPENID_CONFIG = ".well-known/openid-configuration";
|
|
334
|
+
/* eslint-disable camelcase */
|
|
144
335
|
const issuerConfigKeyMap = {
|
|
145
336
|
issuer: {
|
|
146
337
|
toKey: "issuer",
|
|
@@ -228,10 +419,15 @@ const issuerConfigKeyMap = {
|
|
|
228
419
|
convertToUrl: true,
|
|
229
420
|
},
|
|
230
421
|
};
|
|
422
|
+
/* eslint-enable camelcase */
|
|
231
423
|
function processConfig(config) {
|
|
232
424
|
const parsedConfig = {};
|
|
233
425
|
Object.keys(config).forEach((key) => {
|
|
234
426
|
if (issuerConfigKeyMap[key]) {
|
|
427
|
+
// TODO: PMcB55: Validate URL if "issuerConfigKeyMap[key].convertToUrl"
|
|
428
|
+
// if (issuerConfigKeyMap[key].convertToUrl) {
|
|
429
|
+
// validateUrl(config[key]);
|
|
430
|
+
// }
|
|
235
431
|
parsedConfig[issuerConfigKeyMap[key].toKey] = config[key];
|
|
236
432
|
}
|
|
237
433
|
});
|
|
@@ -240,60 +436,110 @@ function processConfig(config) {
|
|
|
240
436
|
}
|
|
241
437
|
return parsedConfig;
|
|
242
438
|
}
|
|
439
|
+
/**
|
|
440
|
+
* @hidden
|
|
441
|
+
*/
|
|
243
442
|
class IssuerConfigFetcher {
|
|
244
443
|
constructor(storageUtility) {
|
|
245
444
|
this.storageUtility = storageUtility;
|
|
445
|
+
this.storageUtility = storageUtility;
|
|
246
446
|
}
|
|
447
|
+
// This method needs no state (so can be static), and can be exposed to allow
|
|
448
|
+
// callers to know where this implementation puts state it needs.
|
|
247
449
|
static getLocalStorageKey(issuer) {
|
|
248
450
|
return `issuerConfig:${issuer}`;
|
|
249
451
|
}
|
|
250
452
|
async fetchConfig(issuer) {
|
|
251
453
|
let issuerConfig;
|
|
252
|
-
const openIdConfigUrl = new URL(WELL_KNOWN_OPENID_CONFIG,
|
|
454
|
+
const openIdConfigUrl = new URL(WELL_KNOWN_OPENID_CONFIG,
|
|
455
|
+
// Make sure to append a slash at issuer URL, so that the .well-known URL
|
|
456
|
+
// includes the full issuer path. See https://openid.net/specs/openid-connect-discovery-1_0.html#ProviderConfig.
|
|
457
|
+
issuer.endsWith("/") ? issuer : `${issuer}/`).href;
|
|
253
458
|
const issuerConfigRequestBody = await universalFetch.fetch.call(globalThis, openIdConfigUrl);
|
|
459
|
+
// Check the validity of the fetched config
|
|
254
460
|
try {
|
|
255
461
|
issuerConfig = processConfig(await issuerConfigRequestBody.json());
|
|
256
462
|
}
|
|
257
463
|
catch (err) {
|
|
258
464
|
throw new solidClientAuthnCore.ConfigurationError(`[${issuer.toString()}] has an invalid configuration: ${err.message}`);
|
|
259
465
|
}
|
|
466
|
+
// Update store with fetched config
|
|
260
467
|
await this.storageUtility.set(IssuerConfigFetcher.getLocalStorageKey(issuer), JSON.stringify(issuerConfig));
|
|
261
468
|
return issuerConfig;
|
|
262
469
|
}
|
|
263
470
|
}
|
|
264
471
|
|
|
472
|
+
//
|
|
473
|
+
// Copyright Inrupt Inc.
|
|
474
|
+
//
|
|
475
|
+
// Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
476
|
+
// of this software and associated documentation files (the "Software"), to deal in
|
|
477
|
+
// the Software without restriction, including without limitation the rights to use,
|
|
478
|
+
// copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the
|
|
479
|
+
// Software, and to permit persons to whom the Software is furnished to do so,
|
|
480
|
+
// subject to the following conditions:
|
|
481
|
+
//
|
|
482
|
+
// The above copyright notice and this permission notice shall be included in
|
|
483
|
+
// all copies or substantial portions of the Software.
|
|
484
|
+
//
|
|
485
|
+
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED,
|
|
486
|
+
// INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A
|
|
487
|
+
// PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT
|
|
488
|
+
// HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
|
|
489
|
+
// OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
|
|
490
|
+
// SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
|
491
|
+
//
|
|
492
|
+
/**
|
|
493
|
+
* @param sessionId
|
|
494
|
+
* @param storage
|
|
495
|
+
* @hidden
|
|
496
|
+
*/
|
|
265
497
|
async function clear(sessionId, storage) {
|
|
266
498
|
await solidClientAuthnCore.clear(sessionId, storage);
|
|
267
499
|
await oidcClientExt.clearOidcPersistentStorage();
|
|
268
500
|
}
|
|
501
|
+
/**
|
|
502
|
+
* @hidden
|
|
503
|
+
*/
|
|
269
504
|
class SessionInfoManager extends solidClientAuthnCore.SessionInfoManagerBase {
|
|
270
505
|
async get(sessionId) {
|
|
271
|
-
|
|
272
|
-
|
|
273
|
-
|
|
274
|
-
|
|
275
|
-
|
|
276
|
-
|
|
277
|
-
|
|
278
|
-
|
|
279
|
-
|
|
280
|
-
|
|
281
|
-
|
|
282
|
-
|
|
283
|
-
|
|
284
|
-
|
|
285
|
-
|
|
286
|
-
|
|
287
|
-
|
|
288
|
-
|
|
289
|
-
|
|
290
|
-
|
|
291
|
-
|
|
292
|
-
|
|
293
|
-
|
|
294
|
-
|
|
295
|
-
|
|
296
|
-
|
|
506
|
+
const [isLoggedIn, webId, clientId, clientSecret, redirectUrl, refreshToken, issuer, tokenType,] = await Promise.all([
|
|
507
|
+
this.storageUtility.getForUser(sessionId, "isLoggedIn", {
|
|
508
|
+
secure: true,
|
|
509
|
+
}),
|
|
510
|
+
this.storageUtility.getForUser(sessionId, "webId", {
|
|
511
|
+
secure: true,
|
|
512
|
+
}),
|
|
513
|
+
this.storageUtility.getForUser(sessionId, "clientId", {
|
|
514
|
+
secure: false,
|
|
515
|
+
}),
|
|
516
|
+
this.storageUtility.getForUser(sessionId, "clientSecret", {
|
|
517
|
+
secure: false,
|
|
518
|
+
}),
|
|
519
|
+
this.storageUtility.getForUser(sessionId, "redirectUrl", {
|
|
520
|
+
secure: false,
|
|
521
|
+
}),
|
|
522
|
+
this.storageUtility.getForUser(sessionId, "refreshToken", {
|
|
523
|
+
secure: true,
|
|
524
|
+
}),
|
|
525
|
+
this.storageUtility.getForUser(sessionId, "issuer", {
|
|
526
|
+
secure: false,
|
|
527
|
+
}),
|
|
528
|
+
this.storageUtility.getForUser(sessionId, "tokenType", {
|
|
529
|
+
secure: false,
|
|
530
|
+
}),
|
|
531
|
+
]);
|
|
532
|
+
if (typeof redirectUrl === "string" && !solidClientAuthnCore.isValidRedirectUrl(redirectUrl)) {
|
|
533
|
+
// This resolves the issue for people experiencing https://github.com/inrupt/solid-client-authn-js/issues/2891.
|
|
534
|
+
// An invalid redirect URL is present in the storage, and the session should
|
|
535
|
+
// be cleared to get a fresh start. This will require the user to log back in.
|
|
536
|
+
await Promise.all([
|
|
537
|
+
this.storageUtility.deleteAllUserData(sessionId, { secure: false }),
|
|
538
|
+
this.storageUtility.deleteAllUserData(sessionId, { secure: true }),
|
|
539
|
+
]);
|
|
540
|
+
return undefined;
|
|
541
|
+
}
|
|
542
|
+
if (tokenType !== undefined && !solidClientAuthnCore.isSupportedTokenType(tokenType)) {
|
|
297
543
|
throw new Error(`Tokens of type [${tokenType}] are not supported.`);
|
|
298
544
|
}
|
|
299
545
|
if (clientId === undefined &&
|
|
@@ -311,17 +557,52 @@ class SessionInfoManager extends solidClientAuthnCore.SessionInfoManagerBase {
|
|
|
311
557
|
issuer,
|
|
312
558
|
clientAppId: clientId,
|
|
313
559
|
clientAppSecret: clientSecret,
|
|
314
|
-
|
|
560
|
+
// Default the token type to DPoP if unspecified.
|
|
561
|
+
tokenType: tokenType !== null && tokenType !== void 0 ? tokenType : "DPoP",
|
|
315
562
|
};
|
|
316
563
|
}
|
|
564
|
+
/**
|
|
565
|
+
* This function removes all session-related information from storage.
|
|
566
|
+
* @param sessionId the session identifier
|
|
567
|
+
* @param storage the storage where session info is stored
|
|
568
|
+
* @hidden
|
|
569
|
+
*/
|
|
317
570
|
async clear(sessionId) {
|
|
318
571
|
return clear(sessionId, this.storageUtility);
|
|
319
572
|
}
|
|
320
573
|
}
|
|
321
574
|
|
|
575
|
+
//
|
|
576
|
+
// Copyright Inrupt Inc.
|
|
577
|
+
//
|
|
578
|
+
// Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
579
|
+
// of this software and associated documentation files (the "Software"), to deal in
|
|
580
|
+
// the Software without restriction, including without limitation the rights to use,
|
|
581
|
+
// copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the
|
|
582
|
+
// Software, and to permit persons to whom the Software is furnished to do so,
|
|
583
|
+
// subject to the following conditions:
|
|
584
|
+
//
|
|
585
|
+
// The above copyright notice and this permission notice shall be included in
|
|
586
|
+
// all copies or substantial portions of the Software.
|
|
587
|
+
//
|
|
588
|
+
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED,
|
|
589
|
+
// INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A
|
|
590
|
+
// PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT
|
|
591
|
+
// HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
|
|
592
|
+
// OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
|
|
593
|
+
// SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
|
594
|
+
//
|
|
595
|
+
/**
|
|
596
|
+
* This class handles redirect IRIs without any query params, and returns an unauthenticated
|
|
597
|
+
* session. It serves as a fallback so that consuming libraries don't have to test
|
|
598
|
+
* for the query params themselves, and can always try to use them as a redirect IRI.
|
|
599
|
+
* @hidden
|
|
600
|
+
*/
|
|
322
601
|
class FallbackRedirectHandler {
|
|
323
602
|
async canHandle(redirectUrl) {
|
|
324
603
|
try {
|
|
604
|
+
// The next URL object is built for validating it.
|
|
605
|
+
// eslint-disable-next-line no-new
|
|
325
606
|
new URL(redirectUrl);
|
|
326
607
|
return true;
|
|
327
608
|
}
|
|
@@ -329,12 +610,44 @@ class FallbackRedirectHandler {
|
|
|
329
610
|
throw new Error(`[${redirectUrl}] is not a valid URL, and cannot be used as a redirect URL: ${e}`);
|
|
330
611
|
}
|
|
331
612
|
}
|
|
332
|
-
async handle(
|
|
613
|
+
async handle(
|
|
614
|
+
// The argument is ignored, but must be present to implement the interface
|
|
615
|
+
_redirectUrl) {
|
|
333
616
|
return solidClientAuthnCore.getUnauthenticatedSession();
|
|
334
617
|
}
|
|
335
618
|
}
|
|
336
619
|
|
|
620
|
+
//
|
|
621
|
+
// Copyright Inrupt Inc.
|
|
622
|
+
//
|
|
623
|
+
// Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
624
|
+
// of this software and associated documentation files (the "Software"), to deal in
|
|
625
|
+
// the Software without restriction, including without limitation the rights to use,
|
|
626
|
+
// copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the
|
|
627
|
+
// Software, and to permit persons to whom the Software is furnished to do so,
|
|
628
|
+
// subject to the following conditions:
|
|
629
|
+
//
|
|
630
|
+
// The above copyright notice and this permission notice shall be included in
|
|
631
|
+
// all copies or substantial portions of the Software.
|
|
632
|
+
//
|
|
633
|
+
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED,
|
|
634
|
+
// INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A
|
|
635
|
+
// PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT
|
|
636
|
+
// HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
|
|
637
|
+
// OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
|
|
638
|
+
// SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
|
639
|
+
//
|
|
640
|
+
/**
|
|
641
|
+
* @hidden
|
|
642
|
+
* @packageDocumentation
|
|
643
|
+
*/
|
|
644
|
+
// FIXME: The following doesn't work in the browser, it results in all the fetches
|
|
645
|
+
// being unauthenticated. This should be looked into when migrating to universal-fetch.
|
|
646
|
+
// import { fetch } from "cross-fetch";
|
|
337
647
|
const globalFetch = (...args) => universalFetch.fetch.call(globalThis, ...args);
|
|
648
|
+
/**
|
|
649
|
+
* @hidden
|
|
650
|
+
*/
|
|
338
651
|
class AuthCodeRedirectHandler {
|
|
339
652
|
constructor(storageUtility, sessionInfoManager, issuerConfigFetcher, clientRegistrar, tokerRefresher) {
|
|
340
653
|
this.storageUtility = storageUtility;
|
|
@@ -342,6 +655,11 @@ class AuthCodeRedirectHandler {
|
|
|
342
655
|
this.issuerConfigFetcher = issuerConfigFetcher;
|
|
343
656
|
this.clientRegistrar = clientRegistrar;
|
|
344
657
|
this.tokerRefresher = tokerRefresher;
|
|
658
|
+
this.storageUtility = storageUtility;
|
|
659
|
+
this.sessionInfoManager = sessionInfoManager;
|
|
660
|
+
this.issuerConfigFetcher = issuerConfigFetcher;
|
|
661
|
+
this.clientRegistrar = clientRegistrar;
|
|
662
|
+
this.tokerRefresher = tokerRefresher;
|
|
345
663
|
}
|
|
346
664
|
async canHandle(redirectUrl) {
|
|
347
665
|
try {
|
|
@@ -379,10 +697,15 @@ class AuthCodeRedirectHandler {
|
|
|
379
697
|
if (isDpop) {
|
|
380
698
|
tokens = await oidcClientExt.getDpopToken(issuerConfig, client, {
|
|
381
699
|
grantType: "authorization_code",
|
|
700
|
+
// We rely on our 'canHandle' function checking that the OAuth 'code'
|
|
701
|
+
// parameter is present in our query string.
|
|
382
702
|
code: url.searchParams.get("code"),
|
|
383
703
|
codeVerifier,
|
|
384
704
|
redirectUrl: storedRedirectIri,
|
|
385
705
|
});
|
|
706
|
+
// Delete oidc-client-specific session information from storage. This is
|
|
707
|
+
// done automatically when retrieving a bearer token, but since the DPoP
|
|
708
|
+
// binding uses our custom code, this needs to be done manually.
|
|
386
709
|
window.localStorage.removeItem(`oidc.${oauthState}`);
|
|
387
710
|
}
|
|
388
711
|
else {
|
|
@@ -423,12 +746,58 @@ class AuthCodeRedirectHandler {
|
|
|
423
746
|
}
|
|
424
747
|
}
|
|
425
748
|
|
|
749
|
+
//
|
|
750
|
+
// Copyright Inrupt Inc.
|
|
751
|
+
//
|
|
752
|
+
// Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
753
|
+
// of this software and associated documentation files (the "Software"), to deal in
|
|
754
|
+
// the Software without restriction, including without limitation the rights to use,
|
|
755
|
+
// copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the
|
|
756
|
+
// Software, and to permit persons to whom the Software is furnished to do so,
|
|
757
|
+
// subject to the following conditions:
|
|
758
|
+
//
|
|
759
|
+
// The above copyright notice and this permission notice shall be included in
|
|
760
|
+
// all copies or substantial portions of the Software.
|
|
761
|
+
//
|
|
762
|
+
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED,
|
|
763
|
+
// INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A
|
|
764
|
+
// PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT
|
|
765
|
+
// HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
|
|
766
|
+
// OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
|
|
767
|
+
// SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
|
768
|
+
//
|
|
769
|
+
/**
|
|
770
|
+
* @hidden
|
|
771
|
+
*/
|
|
426
772
|
class AggregateRedirectHandler extends solidClientAuthnCore.AggregateHandler {
|
|
427
773
|
constructor(redirectHandlers) {
|
|
428
774
|
super(redirectHandlers);
|
|
429
775
|
}
|
|
430
776
|
}
|
|
431
777
|
|
|
778
|
+
//
|
|
779
|
+
// Copyright Inrupt Inc.
|
|
780
|
+
//
|
|
781
|
+
// Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
782
|
+
// of this software and associated documentation files (the "Software"), to deal in
|
|
783
|
+
// the Software without restriction, including without limitation the rights to use,
|
|
784
|
+
// copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the
|
|
785
|
+
// Software, and to permit persons to whom the Software is furnished to do so,
|
|
786
|
+
// subject to the following conditions:
|
|
787
|
+
//
|
|
788
|
+
// The above copyright notice and this permission notice shall be included in
|
|
789
|
+
// all copies or substantial portions of the Software.
|
|
790
|
+
//
|
|
791
|
+
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED,
|
|
792
|
+
// INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A
|
|
793
|
+
// PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT
|
|
794
|
+
// HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
|
|
795
|
+
// OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
|
|
796
|
+
// SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
|
797
|
+
//
|
|
798
|
+
/**
|
|
799
|
+
* @hidden
|
|
800
|
+
*/
|
|
432
801
|
class BrowserStorage {
|
|
433
802
|
get storage() {
|
|
434
803
|
return window.localStorage;
|
|
@@ -444,6 +813,29 @@ class BrowserStorage {
|
|
|
444
813
|
}
|
|
445
814
|
}
|
|
446
815
|
|
|
816
|
+
//
|
|
817
|
+
// Copyright Inrupt Inc.
|
|
818
|
+
//
|
|
819
|
+
// Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
820
|
+
// of this software and associated documentation files (the "Software"), to deal in
|
|
821
|
+
// the Software without restriction, including without limitation the rights to use,
|
|
822
|
+
// copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the
|
|
823
|
+
// Software, and to permit persons to whom the Software is furnished to do so,
|
|
824
|
+
// subject to the following conditions:
|
|
825
|
+
//
|
|
826
|
+
// The above copyright notice and this permission notice shall be included in
|
|
827
|
+
// all copies or substantial portions of the Software.
|
|
828
|
+
//
|
|
829
|
+
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED,
|
|
830
|
+
// INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A
|
|
831
|
+
// PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT
|
|
832
|
+
// HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
|
|
833
|
+
// OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
|
|
834
|
+
// SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
|
835
|
+
//
|
|
836
|
+
/**
|
|
837
|
+
* @hidden
|
|
838
|
+
*/
|
|
447
839
|
class Redirector {
|
|
448
840
|
redirect(redirectUrl, options) {
|
|
449
841
|
if (options && options.handleRedirect) {
|
|
@@ -458,18 +850,49 @@ class Redirector {
|
|
|
458
850
|
}
|
|
459
851
|
}
|
|
460
852
|
|
|
853
|
+
//
|
|
854
|
+
// Copyright Inrupt Inc.
|
|
855
|
+
//
|
|
856
|
+
// Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
857
|
+
// of this software and associated documentation files (the "Software"), to deal in
|
|
858
|
+
// the Software without restriction, including without limitation the rights to use,
|
|
859
|
+
// copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the
|
|
860
|
+
// Software, and to permit persons to whom the Software is furnished to do so,
|
|
861
|
+
// subject to the following conditions:
|
|
862
|
+
//
|
|
863
|
+
// The above copyright notice and this permission notice shall be included in
|
|
864
|
+
// all copies or substantial portions of the Software.
|
|
865
|
+
//
|
|
866
|
+
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED,
|
|
867
|
+
// INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A
|
|
868
|
+
// PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT
|
|
869
|
+
// HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
|
|
870
|
+
// OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
|
|
871
|
+
// SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
|
872
|
+
//
|
|
873
|
+
/**
|
|
874
|
+
* @hidden
|
|
875
|
+
*/
|
|
461
876
|
class ClientRegistrar {
|
|
462
877
|
constructor(storageUtility) {
|
|
463
878
|
this.storageUtility = storageUtility;
|
|
879
|
+
this.storageUtility = storageUtility;
|
|
464
880
|
}
|
|
465
881
|
async getClient(options, issuerConfig) {
|
|
466
|
-
|
|
882
|
+
// If client secret and/or client id are stored in storage, use those.
|
|
883
|
+
const [storedClientId, storedClientSecret,
|
|
884
|
+
// storedClientName,
|
|
885
|
+
] = await Promise.all([
|
|
467
886
|
this.storageUtility.getForUser(options.sessionId, "clientId", {
|
|
468
887
|
secure: false,
|
|
469
888
|
}),
|
|
470
889
|
this.storageUtility.getForUser(options.sessionId, "clientSecret", {
|
|
471
890
|
secure: false,
|
|
472
891
|
}),
|
|
892
|
+
// this.storageUtility.getForUser(options.sessionId, "clientName", {
|
|
893
|
+
// // FIXME: figure out how to persist secure storage at reload
|
|
894
|
+
// secure: false,
|
|
895
|
+
// }),
|
|
473
896
|
]);
|
|
474
897
|
if (storedClientId) {
|
|
475
898
|
return {
|
|
@@ -480,6 +903,7 @@ class ClientRegistrar {
|
|
|
480
903
|
}
|
|
481
904
|
try {
|
|
482
905
|
const registeredClient = await oidcClientExt.registerClient(options, issuerConfig);
|
|
906
|
+
// Save info
|
|
483
907
|
const infoToSave = {
|
|
484
908
|
clientId: registeredClient.clientId,
|
|
485
909
|
};
|
|
@@ -491,6 +915,9 @@ class ClientRegistrar {
|
|
|
491
915
|
registeredClient.idTokenSignedResponseAlg;
|
|
492
916
|
}
|
|
493
917
|
await this.storageUtility.setForUser(options.sessionId, infoToSave, {
|
|
918
|
+
// FIXME: figure out how to persist secure storage at reload
|
|
919
|
+
// Otherwise, the client info cannot be retrieved from storage, and
|
|
920
|
+
// the lib tries to re-register the client on each fetch
|
|
494
921
|
secure: false,
|
|
495
922
|
});
|
|
496
923
|
return registeredClient;
|
|
@@ -501,9 +928,36 @@ class ClientRegistrar {
|
|
|
501
928
|
}
|
|
502
929
|
}
|
|
503
930
|
|
|
931
|
+
//
|
|
932
|
+
// Copyright Inrupt Inc.
|
|
933
|
+
//
|
|
934
|
+
// Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
935
|
+
// of this software and associated documentation files (the "Software"), to deal in
|
|
936
|
+
// the Software without restriction, including without limitation the rights to use,
|
|
937
|
+
// copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the
|
|
938
|
+
// Software, and to permit persons to whom the Software is furnished to do so,
|
|
939
|
+
// subject to the following conditions:
|
|
940
|
+
//
|
|
941
|
+
// The above copyright notice and this permission notice shall be included in
|
|
942
|
+
// all copies or substantial portions of the Software.
|
|
943
|
+
//
|
|
944
|
+
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED,
|
|
945
|
+
// INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A
|
|
946
|
+
// PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT
|
|
947
|
+
// HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
|
|
948
|
+
// OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
|
|
949
|
+
// SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
|
950
|
+
//
|
|
951
|
+
/**
|
|
952
|
+
* This class handles redirect IRIs without any query params, and returns an unauthenticated
|
|
953
|
+
* session. It serves as a fallback so that consuming libraries don't have to test
|
|
954
|
+
* for the query params themselves, and can always try to use them as a redirect IRI.
|
|
955
|
+
* @hidden
|
|
956
|
+
*/
|
|
504
957
|
class ErrorOidcHandler {
|
|
505
958
|
async canHandle(redirectUrl) {
|
|
506
959
|
try {
|
|
960
|
+
// eslint-disable-next-line no-new
|
|
507
961
|
return new URL(redirectUrl).searchParams.has("error");
|
|
508
962
|
}
|
|
509
963
|
catch (e) {
|
|
@@ -521,16 +975,47 @@ class ErrorOidcHandler {
|
|
|
521
975
|
}
|
|
522
976
|
}
|
|
523
977
|
|
|
978
|
+
//
|
|
979
|
+
// Copyright Inrupt Inc.
|
|
980
|
+
//
|
|
981
|
+
// Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
982
|
+
// of this software and associated documentation files (the "Software"), to deal in
|
|
983
|
+
// the Software without restriction, including without limitation the rights to use,
|
|
984
|
+
// copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the
|
|
985
|
+
// Software, and to permit persons to whom the Software is furnished to do so,
|
|
986
|
+
// subject to the following conditions:
|
|
987
|
+
//
|
|
988
|
+
// The above copyright notice and this permission notice shall be included in
|
|
989
|
+
// all copies or substantial portions of the Software.
|
|
990
|
+
//
|
|
991
|
+
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED,
|
|
992
|
+
// INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A
|
|
993
|
+
// PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT
|
|
994
|
+
// HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
|
|
995
|
+
// OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
|
|
996
|
+
// SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
|
997
|
+
//
|
|
998
|
+
// Some identifiers are not in camelcase on purpose, as they are named using the
|
|
999
|
+
// official names from the OIDC/OAuth2 specifications.
|
|
1000
|
+
/* eslint-disable camelcase */
|
|
1001
|
+
/**
|
|
1002
|
+
* @hidden
|
|
1003
|
+
*/
|
|
524
1004
|
class TokenRefresher {
|
|
525
1005
|
constructor(storageUtility, issuerConfigFetcher, clientRegistrar) {
|
|
526
1006
|
this.storageUtility = storageUtility;
|
|
527
1007
|
this.issuerConfigFetcher = issuerConfigFetcher;
|
|
528
1008
|
this.clientRegistrar = clientRegistrar;
|
|
1009
|
+
this.storageUtility = storageUtility;
|
|
1010
|
+
this.issuerConfigFetcher = issuerConfigFetcher;
|
|
1011
|
+
this.clientRegistrar = clientRegistrar;
|
|
529
1012
|
}
|
|
530
1013
|
async refresh(sessionId, refreshToken, dpopKey, eventEmitter) {
|
|
531
1014
|
const oidcContext = await solidClientAuthnCore.loadOidcContextFromStorage(sessionId, this.storageUtility, this.issuerConfigFetcher);
|
|
1015
|
+
// This should also retrieve the client from storage
|
|
532
1016
|
const clientInfo = await this.clientRegistrar.getClient({ sessionId }, oidcContext.issuerConfig);
|
|
533
1017
|
if (refreshToken === undefined) {
|
|
1018
|
+
// TODO: in a next PR, look up storage for a refresh token
|
|
534
1019
|
throw new Error(`Session [${sessionId}] has no refresh token to allow it to refresh its access token.`);
|
|
535
1020
|
}
|
|
536
1021
|
if (oidcContext.dpop && dpopKey === undefined) {
|
|
@@ -547,6 +1032,31 @@ class TokenRefresher {
|
|
|
547
1032
|
}
|
|
548
1033
|
}
|
|
549
1034
|
|
|
1035
|
+
//
|
|
1036
|
+
// Copyright Inrupt Inc.
|
|
1037
|
+
//
|
|
1038
|
+
// Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
1039
|
+
// of this software and associated documentation files (the "Software"), to deal in
|
|
1040
|
+
// the Software without restriction, including without limitation the rights to use,
|
|
1041
|
+
// copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the
|
|
1042
|
+
// Software, and to permit persons to whom the Software is furnished to do so,
|
|
1043
|
+
// subject to the following conditions:
|
|
1044
|
+
//
|
|
1045
|
+
// The above copyright notice and this permission notice shall be included in
|
|
1046
|
+
// all copies or substantial portions of the Software.
|
|
1047
|
+
//
|
|
1048
|
+
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED,
|
|
1049
|
+
// INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A
|
|
1050
|
+
// PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT
|
|
1051
|
+
// HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
|
|
1052
|
+
// OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
|
|
1053
|
+
// SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
|
1054
|
+
//
|
|
1055
|
+
/**
|
|
1056
|
+
*
|
|
1057
|
+
* @param dependencies
|
|
1058
|
+
* @deprecated This function will be removed from the external API in an upcoming release.
|
|
1059
|
+
*/
|
|
550
1060
|
function getClientAuthenticationWithDependencies(dependencies) {
|
|
551
1061
|
const inMemoryStorage = new solidClientAuthnCore.InMemoryStorage();
|
|
552
1062
|
const secureStorage = dependencies.secureStorage || inMemoryStorage;
|
|
@@ -557,22 +1067,69 @@ function getClientAuthenticationWithDependencies(dependencies) {
|
|
|
557
1067
|
const sessionInfoManager = new SessionInfoManager(storageUtility);
|
|
558
1068
|
const tokenRefresher = new TokenRefresher(storageUtility, issuerConfigFetcher, clientRegistrar);
|
|
559
1069
|
const redirector = new Redirector();
|
|
1070
|
+
// make new handler for redirect and login
|
|
560
1071
|
const loginHandler = new OidcLoginHandler(storageUtility, new AuthorizationCodeWithPkceOidcHandler(storageUtility, redirector), issuerConfigFetcher, clientRegistrar);
|
|
561
1072
|
const redirectHandler = new AggregateRedirectHandler([
|
|
562
1073
|
new ErrorOidcHandler(),
|
|
563
1074
|
new AuthCodeRedirectHandler(storageUtility, sessionInfoManager, issuerConfigFetcher, clientRegistrar, tokenRefresher),
|
|
1075
|
+
// This catch-all class will always be able to handle the
|
|
1076
|
+
// redirect IRI, so it must be registered last.
|
|
564
1077
|
new FallbackRedirectHandler(),
|
|
565
1078
|
]);
|
|
566
1079
|
return new ClientAuthentication(loginHandler, redirectHandler, new solidClientAuthnCore.IWaterfallLogoutHandler(sessionInfoManager, redirector), sessionInfoManager, issuerConfigFetcher);
|
|
567
1080
|
}
|
|
568
1081
|
|
|
1082
|
+
//
|
|
1083
|
+
// Copyright Inrupt Inc.
|
|
1084
|
+
//
|
|
1085
|
+
// Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
1086
|
+
// of this software and associated documentation files (the "Software"), to deal in
|
|
1087
|
+
// the Software without restriction, including without limitation the rights to use,
|
|
1088
|
+
// copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the
|
|
1089
|
+
// Software, and to permit persons to whom the Software is furnished to do so,
|
|
1090
|
+
// subject to the following conditions:
|
|
1091
|
+
//
|
|
1092
|
+
// The above copyright notice and this permission notice shall be included in
|
|
1093
|
+
// all copies or substantial portions of the Software.
|
|
1094
|
+
//
|
|
1095
|
+
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED,
|
|
1096
|
+
// INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A
|
|
1097
|
+
// PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT
|
|
1098
|
+
// HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
|
|
1099
|
+
// OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
|
|
1100
|
+
// SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
|
1101
|
+
//
|
|
569
1102
|
const KEY_CURRENT_SESSION = `${solidClientAuthnCore.SOLID_CLIENT_AUTHN_KEY_PREFIX}currentSession`;
|
|
570
1103
|
const KEY_CURRENT_URL = `${solidClientAuthnCore.SOLID_CLIENT_AUTHN_KEY_PREFIX}currentUrl`;
|
|
571
1104
|
|
|
1105
|
+
//
|
|
1106
|
+
// Copyright Inrupt Inc.
|
|
1107
|
+
//
|
|
1108
|
+
// Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
1109
|
+
// of this software and associated documentation files (the "Software"), to deal in
|
|
1110
|
+
// the Software without restriction, including without limitation the rights to use,
|
|
1111
|
+
// copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the
|
|
1112
|
+
// Software, and to permit persons to whom the Software is furnished to do so,
|
|
1113
|
+
// subject to the following conditions:
|
|
1114
|
+
//
|
|
1115
|
+
// The above copyright notice and this permission notice shall be included in
|
|
1116
|
+
// all copies or substantial portions of the Software.
|
|
1117
|
+
//
|
|
1118
|
+
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED,
|
|
1119
|
+
// INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A
|
|
1120
|
+
// PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT
|
|
1121
|
+
// HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
|
|
1122
|
+
// OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
|
|
1123
|
+
// SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
|
1124
|
+
//
|
|
572
1125
|
async function silentlyAuthenticate(sessionId, clientAuthn, session) {
|
|
573
1126
|
var _a;
|
|
574
1127
|
const storedSessionInfo = await clientAuthn.validateCurrentSession(sessionId);
|
|
575
1128
|
if (storedSessionInfo !== null) {
|
|
1129
|
+
// It can be really useful to save the user's current browser location,
|
|
1130
|
+
// so that we can restore it after completing the silent authentication
|
|
1131
|
+
// on incoming redirect. This way, the user is eventually redirected back
|
|
1132
|
+
// to the page they were on and not to the app's redirect page.
|
|
576
1133
|
window.localStorage.setItem(KEY_CURRENT_URL, window.location.href);
|
|
577
1134
|
await clientAuthn.login({
|
|
578
1135
|
sessionId,
|
|
@@ -590,21 +1147,68 @@ async function silentlyAuthenticate(sessionId, clientAuthn, session) {
|
|
|
590
1147
|
function isLoggedIn(sessionInfo) {
|
|
591
1148
|
return !!(sessionInfo === null || sessionInfo === void 0 ? void 0 : sessionInfo.isLoggedIn);
|
|
592
1149
|
}
|
|
1150
|
+
/**
|
|
1151
|
+
* A {@link Session} object represents a user's session on an application. The session holds state, as it stores information enabling acces to private resources after login for instance.
|
|
1152
|
+
*/
|
|
593
1153
|
class Session extends EventEmitter {
|
|
1154
|
+
/**
|
|
1155
|
+
* Session object constructor. Typically called as follows:
|
|
1156
|
+
*
|
|
1157
|
+
* ```typescript
|
|
1158
|
+
* const session = new Session();
|
|
1159
|
+
* ```
|
|
1160
|
+
*
|
|
1161
|
+
* See also [getDefaultSession](https://docs.inrupt.com/developer-tools/api/javascript/solid-client-authn-browser/functions.html#getdefaultsession).
|
|
1162
|
+
*
|
|
1163
|
+
* @param sessionOptions The options enabling the correct instantiation of
|
|
1164
|
+
* the session. Either both storages or clientAuthentication are required. For
|
|
1165
|
+
* more information, see {@link ISessionOptions}.
|
|
1166
|
+
* @param sessionId A string uniquely identifying the session.
|
|
1167
|
+
*
|
|
1168
|
+
*/
|
|
594
1169
|
constructor(sessionOptions = {}, sessionId = undefined) {
|
|
595
1170
|
super();
|
|
596
1171
|
this.tokenRequestInProgress = false;
|
|
1172
|
+
/**
|
|
1173
|
+
* Triggers the login process. Note that this method will redirect the user away from your app.
|
|
1174
|
+
*
|
|
1175
|
+
* @param options Parameter to customize the login behaviour. In particular, two options are mandatory: `options.oidcIssuer`, the user's identity provider, and `options.redirectUrl`, the URL to which the user will be redirected after logging in their identity provider.
|
|
1176
|
+
* @returns This method should redirect the user away from the app: it does not return anything. The login process is completed by {@linkcode handleIncomingRedirect}.
|
|
1177
|
+
*/
|
|
1178
|
+
// Define these functions as properties so that they don't get accidentally re-bound.
|
|
1179
|
+
// Isn't Javascript fun?
|
|
597
1180
|
this.login = async (options) => {
|
|
598
1181
|
var _a;
|
|
599
1182
|
await this.clientAuthentication.login({
|
|
600
1183
|
sessionId: this.info.sessionId,
|
|
601
1184
|
...options,
|
|
1185
|
+
// Defaults the token type to DPoP
|
|
602
1186
|
tokenType: (_a = options.tokenType) !== null && _a !== void 0 ? _a : "DPoP",
|
|
603
1187
|
}, this.events);
|
|
1188
|
+
// `login` redirects the user away from the app,
|
|
1189
|
+
// so unless it throws an error, there is no code that should run afterwards
|
|
1190
|
+
// (since there is no "after" in the lifetime of the script).
|
|
1191
|
+
// Hence, this Promise never resolves:
|
|
604
1192
|
return new Promise(() => { });
|
|
605
1193
|
};
|
|
1194
|
+
/**
|
|
1195
|
+
* Fetches data using available login information. If the user is not logged in, this will behave as a regular `fetch`. The signature of this method is identical to the [canonical `fetch`](https://developer.mozilla.org/en-US/docs/Web/API/Fetch_API).
|
|
1196
|
+
*
|
|
1197
|
+
* @param url The URL from which data should be fetched.
|
|
1198
|
+
* @param init Optional parameters customizing the request, by specifying an HTTP method, headers, a body, etc. Follows the [WHATWG Fetch Standard](https://fetch.spec.whatwg.org/).
|
|
1199
|
+
*/
|
|
606
1200
|
this.fetch = (url, init) => this.clientAuthentication.fetch(url, init);
|
|
1201
|
+
/**
|
|
1202
|
+
* An internal logout function, to control whether or not the logout signal
|
|
1203
|
+
* should be sent, i.e. if the logout was user-initiated or is the result of
|
|
1204
|
+
* an external event.
|
|
1205
|
+
*
|
|
1206
|
+
* @hidden
|
|
1207
|
+
*/
|
|
607
1208
|
this.internalLogout = async (emitSignal, options) => {
|
|
1209
|
+
// Clearing this value means that silent refresh will no longer be attempted.
|
|
1210
|
+
// In particular, in the case of a silent authentication error it prevents
|
|
1211
|
+
// from getting stuck in an authentication retries loop.
|
|
608
1212
|
window.localStorage.removeItem(KEY_CURRENT_SESSION);
|
|
609
1213
|
await this.clientAuthentication.logout(this.info.sessionId, options);
|
|
610
1214
|
this.info.isLoggedIn = false;
|
|
@@ -612,7 +1216,50 @@ class Session extends EventEmitter {
|
|
|
612
1216
|
this.events.emit(solidClientAuthnCore.EVENTS.LOGOUT);
|
|
613
1217
|
}
|
|
614
1218
|
};
|
|
1219
|
+
/**
|
|
1220
|
+
* Logs the user out of the application.
|
|
1221
|
+
*
|
|
1222
|
+
* There are 2 types of logout supported by this library,
|
|
1223
|
+
* `app` logout and `idp` logout.
|
|
1224
|
+
*
|
|
1225
|
+
* App logout will log the user out within the application
|
|
1226
|
+
* by clearing any session data from the browser. It does
|
|
1227
|
+
* not log the user out of their Solid identity provider,
|
|
1228
|
+
* and should not redirect the user away.
|
|
1229
|
+
* App logout can be performed as follows:
|
|
1230
|
+
* ```typescript
|
|
1231
|
+
* await session.logout({ logoutType: 'app' });
|
|
1232
|
+
* ```
|
|
1233
|
+
*
|
|
1234
|
+
* IDP logout will log the user out of their Solid identity provider,
|
|
1235
|
+
* and will redirect the user away from the application to do so. In order
|
|
1236
|
+
* for users to be redirected back to `postLogoutUrl` you MUST include the
|
|
1237
|
+
* `postLogoutUrl` value in the `post_logout_redirect_uris` field in the
|
|
1238
|
+
* [Client ID Document](https://docs.inrupt.com/ess/latest/security/authentication/#client-identifier-client-id).
|
|
1239
|
+
* IDP logout can be performed as follows:
|
|
1240
|
+
* ```typescript
|
|
1241
|
+
* await session.logout({
|
|
1242
|
+
* logoutType: 'idp',
|
|
1243
|
+
* // An optional URL to redirect to after logout has completed;
|
|
1244
|
+
* // this MUST match a logout URL listed in the Client ID Document
|
|
1245
|
+
* // of the application that is logged in.
|
|
1246
|
+
* // If the application is logged in with a Client ID that is not
|
|
1247
|
+
* // a URI dereferencing to a Client ID Document then users will
|
|
1248
|
+
* // not be redirected back to the `postLogoutUrl` after logout.
|
|
1249
|
+
* postLogoutUrl: 'https://example.com/logout',
|
|
1250
|
+
* // An optional value to be included in the query parameters
|
|
1251
|
+
* // when the IDP provider redirects the user to the postLogoutRedirectUrl.
|
|
1252
|
+
* state: "my-state"
|
|
1253
|
+
* });
|
|
1254
|
+
* ```
|
|
1255
|
+
*/
|
|
615
1256
|
this.logout = async (options) => this.internalLogout(true, options);
|
|
1257
|
+
/**
|
|
1258
|
+
* Completes the login process by processing the information provided by the
|
|
1259
|
+
* Solid identity provider through redirect.
|
|
1260
|
+
*
|
|
1261
|
+
* @param options See {@see IHandleIncomingRedirectOptions}.
|
|
1262
|
+
*/
|
|
616
1263
|
this.handleIncomingRedirect = async (inputOptions = {}) => {
|
|
617
1264
|
var _a;
|
|
618
1265
|
if (this.info.isLoggedIn) {
|
|
@@ -629,17 +1276,33 @@ class Session extends EventEmitter {
|
|
|
629
1276
|
this.setSessionInfo(sessionInfo);
|
|
630
1277
|
const currentUrl = window.localStorage.getItem(KEY_CURRENT_URL);
|
|
631
1278
|
if (currentUrl === null) {
|
|
1279
|
+
// The login event can only be triggered **after** the user has been
|
|
1280
|
+
// redirected from the IdP with access and ID tokens.
|
|
632
1281
|
this.events.emit(solidClientAuthnCore.EVENTS.LOGIN);
|
|
633
1282
|
}
|
|
634
1283
|
else {
|
|
1284
|
+
// If an URL is stored in local storage, we are being logged in after a
|
|
1285
|
+
// silent authentication, so remove our currently stored URL location
|
|
1286
|
+
// to clean up our state now that we are completing the re-login process.
|
|
635
1287
|
window.localStorage.removeItem(KEY_CURRENT_URL);
|
|
636
1288
|
this.events.emit(solidClientAuthnCore.EVENTS.SESSION_RESTORED, currentUrl);
|
|
637
1289
|
}
|
|
638
1290
|
}
|
|
639
1291
|
else if (options.restorePreviousSession === true) {
|
|
1292
|
+
// Silent authentication happens after a refresh, which means there are no
|
|
1293
|
+
// OAuth params in the current location IRI. It can only succeed if a session
|
|
1294
|
+
// was previously logged in, in which case its ID will be present with a known
|
|
1295
|
+
// identifier in local storage.
|
|
1296
|
+
// Check if we have a locally stored session ID...
|
|
640
1297
|
const storedSessionId = window.localStorage.getItem(KEY_CURRENT_SESSION);
|
|
1298
|
+
// ...if not, then there is no ID token, and so silent authentication cannot happen, but
|
|
1299
|
+
// if we do have a stored session ID, attempt to re-authenticate now silently.
|
|
641
1300
|
if (storedSessionId !== null) {
|
|
642
1301
|
const attemptedSilentAuthentication = await silentlyAuthenticate(storedSessionId, this.clientAuthentication, this);
|
|
1302
|
+
// At this point, we know that the main window will imminently be redirected.
|
|
1303
|
+
// However, this redirect is asynchronous and there is no way to halt execution
|
|
1304
|
+
// until it happens precisely. That's why the current Promise simply does not
|
|
1305
|
+
// resolve.
|
|
643
1306
|
if (attemptedSilentAuthentication) {
|
|
644
1307
|
return new Promise(() => { });
|
|
645
1308
|
}
|
|
@@ -648,6 +1311,10 @@ class Session extends EventEmitter {
|
|
|
648
1311
|
this.tokenRequestInProgress = false;
|
|
649
1312
|
return sessionInfo;
|
|
650
1313
|
};
|
|
1314
|
+
// Until Session no longer implements EventEmitter, this.events is just a proxy
|
|
1315
|
+
// to this (with some interface filtering). When we make the breaking change,
|
|
1316
|
+
// this.events will be a regular EventEmitter (implementing ISessionEventEmitter):
|
|
1317
|
+
// this.events = new EventEmitter();
|
|
651
1318
|
this.events = new Proxy(this, solidClientAuthnCore.buildProxyHandler(Session.prototype, "events only implements ISessionEventListener"));
|
|
652
1319
|
if (sessionOptions.clientAuthentication) {
|
|
653
1320
|
this.clientAuthentication = sessionOptions.clientAuthentication;
|
|
@@ -674,22 +1341,63 @@ class Session extends EventEmitter {
|
|
|
674
1341
|
isLoggedIn: false,
|
|
675
1342
|
};
|
|
676
1343
|
}
|
|
1344
|
+
// When a session is logged in, we want to track its ID in local storage to
|
|
1345
|
+
// enable silent refresh. The current session ID specifically stored in 'localStorage'
|
|
1346
|
+
// (as opposed to using our storage abstraction layer) because it is only
|
|
1347
|
+
// used in a browser-specific mechanism.
|
|
677
1348
|
this.events.on(solidClientAuthnCore.EVENTS.LOGIN, () => window.localStorage.setItem(KEY_CURRENT_SESSION, this.info.sessionId));
|
|
678
1349
|
this.events.on(solidClientAuthnCore.EVENTS.SESSION_EXPIRED, () => this.internalLogout(false));
|
|
679
1350
|
this.events.on(solidClientAuthnCore.EVENTS.ERROR, () => this.internalLogout(false));
|
|
680
1351
|
}
|
|
1352
|
+
/**
|
|
1353
|
+
* Register a callback function to be called when a user completes login.
|
|
1354
|
+
*
|
|
1355
|
+
* The callback is called when {@link handleIncomingRedirect} completes successfully.
|
|
1356
|
+
*
|
|
1357
|
+
* @param callback The function called when a user completes login.
|
|
1358
|
+
* @deprecated Prefer session.events.on(EVENTS.LOGIN, callback)
|
|
1359
|
+
*/
|
|
681
1360
|
onLogin(callback) {
|
|
682
1361
|
this.events.on(solidClientAuthnCore.EVENTS.LOGIN, callback);
|
|
683
1362
|
}
|
|
1363
|
+
/**
|
|
1364
|
+
* Register a callback function to be called when a user logs out:
|
|
1365
|
+
*
|
|
1366
|
+
* @param callback The function called when a user completes logout.
|
|
1367
|
+
* @deprecated Prefer session.events.on(EVENTS.LOGOUT, callback)
|
|
1368
|
+
*/
|
|
684
1369
|
onLogout(callback) {
|
|
685
1370
|
this.events.on(solidClientAuthnCore.EVENTS.LOGOUT, callback);
|
|
686
1371
|
}
|
|
1372
|
+
/**
|
|
1373
|
+
* Register a callback function to be called when a user logs out:
|
|
1374
|
+
*
|
|
1375
|
+
* @param callback The function called when an error occurs.
|
|
1376
|
+
* @since 1.11.0
|
|
1377
|
+
* @deprecated Prefer session.events.on(EVENTS.ERROR, callback)
|
|
1378
|
+
*/
|
|
687
1379
|
onError(callback) {
|
|
688
1380
|
this.events.on(solidClientAuthnCore.EVENTS.ERROR, callback);
|
|
689
1381
|
}
|
|
1382
|
+
/**
|
|
1383
|
+
* Register a callback function to be called when a session is restored.
|
|
1384
|
+
*
|
|
1385
|
+
* Note: the callback will be called with the saved value of the 'current URL'
|
|
1386
|
+
* at the time the session was restored.
|
|
1387
|
+
*
|
|
1388
|
+
* @param callback The function called when a user's already logged-in session is restored, e.g., after a silent authentication is completed after a page refresh.
|
|
1389
|
+
* @deprecated Prefer session.events.on(EVENTS.SESSION_RESTORED, callback)
|
|
1390
|
+
*/
|
|
690
1391
|
onSessionRestore(callback) {
|
|
691
1392
|
this.events.on(solidClientAuthnCore.EVENTS.SESSION_RESTORED, callback);
|
|
692
1393
|
}
|
|
1394
|
+
/**
|
|
1395
|
+
* Register a callback that runs when the session expires and can no longer
|
|
1396
|
+
* make authenticated requests, but following a user logout.
|
|
1397
|
+
* @param callback The function that runs on session expiration.
|
|
1398
|
+
* @since 1.11.0
|
|
1399
|
+
* @deprecated Prefer session.events.on(EVENTS.SESSION_EXPIRED, callback)
|
|
1400
|
+
*/
|
|
693
1401
|
onSessionExpiration(callback) {
|
|
694
1402
|
this.events.on(solidClientAuthnCore.EVENTS.SESSION_EXPIRED, callback);
|
|
695
1403
|
}
|
|
@@ -704,41 +1412,132 @@ class Session extends EventEmitter {
|
|
|
704
1412
|
}
|
|
705
1413
|
}
|
|
706
1414
|
|
|
1415
|
+
//
|
|
1416
|
+
// Copyright Inrupt Inc.
|
|
1417
|
+
//
|
|
1418
|
+
// Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
1419
|
+
// of this software and associated documentation files (the "Software"), to deal in
|
|
1420
|
+
// the Software without restriction, including without limitation the rights to use,
|
|
1421
|
+
// copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the
|
|
1422
|
+
// Software, and to permit persons to whom the Software is furnished to do so,
|
|
1423
|
+
// subject to the following conditions:
|
|
1424
|
+
//
|
|
1425
|
+
// The above copyright notice and this permission notice shall be included in
|
|
1426
|
+
// all copies or substantial portions of the Software.
|
|
1427
|
+
//
|
|
1428
|
+
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED,
|
|
1429
|
+
// INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A
|
|
1430
|
+
// PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT
|
|
1431
|
+
// HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
|
|
1432
|
+
// OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
|
|
1433
|
+
// SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
|
1434
|
+
//
|
|
707
1435
|
let defaultSession;
|
|
1436
|
+
/**
|
|
1437
|
+
* Obtain the {@link Session} used when not explicitly instantiating one yourself.
|
|
1438
|
+
*
|
|
1439
|
+
* When using the top-level exports {@link fetch}, {@link login}, {@link logout},
|
|
1440
|
+
* {@link handleIncomingRedirect}, {@link onLogin} and {@link onLogout}, these apply to an
|
|
1441
|
+
* implicitly-instantiated {@link Session}.
|
|
1442
|
+
* This function returns a reference to that Session in order to obtain e.g. the current user's
|
|
1443
|
+
* WebID.
|
|
1444
|
+
* @since 1.3.0
|
|
1445
|
+
*/
|
|
708
1446
|
function getDefaultSession() {
|
|
709
1447
|
if (typeof defaultSession === "undefined") {
|
|
710
1448
|
defaultSession = new Session();
|
|
711
1449
|
}
|
|
712
1450
|
return defaultSession;
|
|
713
1451
|
}
|
|
1452
|
+
/**
|
|
1453
|
+
* This function's signature is equal to `window.fetch`, but if the current user is authenticated
|
|
1454
|
+
* (see [[login]] and [[handleIncomingRedirect]]), requests made using it will include that user's
|
|
1455
|
+
* credentials. If not, this will behave just like the regular `window.fetch`.
|
|
1456
|
+
*
|
|
1457
|
+
* @see {@link https://developer.mozilla.org/en-US/docs/Web/API/WindowOrWorkerGlobalScope/fetch}
|
|
1458
|
+
* @since 1.3.0
|
|
1459
|
+
*/
|
|
1460
|
+
/* eslint-disable-next-line no-shadow */
|
|
714
1461
|
const fetch = (...args) => {
|
|
715
1462
|
const session = getDefaultSession();
|
|
716
1463
|
return session.fetch(...args);
|
|
717
1464
|
};
|
|
1465
|
+
/**
|
|
1466
|
+
* Triggers the login process. Note that this method will redirect the user away from your app.
|
|
1467
|
+
*
|
|
1468
|
+
* @param options Parameter to customize the login behaviour. In particular, two options are mandatory: `options.oidcIssuer`, the user's identity provider, and `options.redirectUrl`, the URL to which the user will be redirected after logging in their identity provider.
|
|
1469
|
+
* @returns This method should redirect the user away from the app: it does not return anything. The login process is completed by [[handleIncomingRedirect]].
|
|
1470
|
+
* @since 1.3.0
|
|
1471
|
+
*/
|
|
718
1472
|
const login = (...args) => {
|
|
719
1473
|
const session = getDefaultSession();
|
|
720
1474
|
return session.login(...args);
|
|
721
1475
|
};
|
|
1476
|
+
/**
|
|
1477
|
+
* Logs the user out of the application.
|
|
1478
|
+
*
|
|
1479
|
+
* By default this does not log the user out of their Solid identity provider.
|
|
1480
|
+
* In order to do so, you must set the logoutType to `idp`. For usage details
|
|
1481
|
+
* see {@link Session.logout}.
|
|
1482
|
+
*
|
|
1483
|
+
* @since 1.3.0
|
|
1484
|
+
*/
|
|
722
1485
|
const logout = (...args) => {
|
|
723
1486
|
const session = getDefaultSession();
|
|
724
1487
|
return session.logout(...args);
|
|
725
1488
|
};
|
|
1489
|
+
/**
|
|
1490
|
+
* Completes the login process by processing the information provided by the Solid identity provider through redirect.
|
|
1491
|
+
*
|
|
1492
|
+
* @param url The URL of the page handling the redirect, including the query parameters — these contain the information to process the login.
|
|
1493
|
+
* @since 1.3.0
|
|
1494
|
+
*/
|
|
726
1495
|
const handleIncomingRedirect = (...args) => {
|
|
727
1496
|
const session = getDefaultSession();
|
|
728
1497
|
return session.handleIncomingRedirect(...args);
|
|
729
1498
|
};
|
|
1499
|
+
/**
|
|
1500
|
+
* Register a callback function to be called when a user completes login.
|
|
1501
|
+
*
|
|
1502
|
+
* The callback is called when {@link handleIncomingRedirect} completes successfully.
|
|
1503
|
+
* @since 1.3.0
|
|
1504
|
+
*
|
|
1505
|
+
* @param callback The function called when a user completes login.
|
|
1506
|
+
* @deprecated Prefer events.on(EVENTS.LOGIN, callback)
|
|
1507
|
+
|
|
1508
|
+
*/
|
|
730
1509
|
const onLogin = (...args) => {
|
|
731
1510
|
const session = getDefaultSession();
|
|
732
1511
|
return session.onLogin(...args);
|
|
733
1512
|
};
|
|
1513
|
+
/**
|
|
1514
|
+
* Register a callback function to be called when a user logs out:
|
|
1515
|
+
*
|
|
1516
|
+
* @param callback The function called when a user completes logout.
|
|
1517
|
+
* @since 1.3.0
|
|
1518
|
+
* @deprecated Prefer events.on(EVENTS.LOGOUT, callback)
|
|
1519
|
+
*
|
|
1520
|
+
*/
|
|
734
1521
|
const onLogout = (...args) => {
|
|
735
1522
|
const session = getDefaultSession();
|
|
736
1523
|
return session.onLogout(...args);
|
|
737
1524
|
};
|
|
1525
|
+
/**
|
|
1526
|
+
* Register a callback function to be called when a session is restored:
|
|
1527
|
+
*
|
|
1528
|
+
* @param callback The function called when a session is restored.
|
|
1529
|
+
* @since 1.3.0
|
|
1530
|
+
* @deprecated Prefer events.on(EVENTS.SESSION_RESTORED, callback)
|
|
1531
|
+
*/
|
|
738
1532
|
const onSessionRestore = (...args) => {
|
|
739
1533
|
const session = getDefaultSession();
|
|
740
1534
|
return session.onSessionRestore(...args);
|
|
741
1535
|
};
|
|
1536
|
+
/**
|
|
1537
|
+
* {@link SessionEventEmitter} instance to subscribe to events by the default session.
|
|
1538
|
+
*
|
|
1539
|
+
* @since 1.14.0
|
|
1540
|
+
*/
|
|
742
1541
|
const events = () => {
|
|
743
1542
|
return getDefaultSession().events;
|
|
744
1543
|
};
|