@inrupt/solid-client-authn-browser 1.17.2 → 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/defaultSession.d.ts +70 -0
- package/dist/dependencies.d.ts +12 -0
- package/dist/index.js +783 -6
- package/dist/index.js.map +1 -1
- package/dist/index.mjs +783 -6
- 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 +5 -5
- 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 +6 -6
- package/src/ClientAuthentication.spec.ts +40 -19
- package/src/ClientAuthentication.ts +14 -3
- package/src/Session.spec.ts +10 -13
|
@@ -1,4 +1,11 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @hidden
|
|
3
|
+
* @packageDocumentation
|
|
4
|
+
*/
|
|
1
5
|
import type { IStorage } from "@inrupt/solid-client-authn-core";
|
|
6
|
+
/**
|
|
7
|
+
* @hidden
|
|
8
|
+
*/
|
|
2
9
|
export default class BrowserStorage implements IStorage {
|
|
3
10
|
get storage(): typeof window.localStorage;
|
|
4
11
|
get(key: string): Promise<string | undefined>;
|
|
@@ -1,5 +1,19 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @hidden
|
|
3
|
+
* @packageDocumentation
|
|
4
|
+
*/
|
|
5
|
+
/**
|
|
6
|
+
* A helper class that will validate items taken from local storage
|
|
7
|
+
*/
|
|
1
8
|
import type { IStorage } from "@inrupt/solid-client-authn-core";
|
|
2
9
|
import { StorageUtility } from "@inrupt/solid-client-authn-core";
|
|
10
|
+
/**
|
|
11
|
+
* This class in a no-value-added extension of StorageUtility from the core module.
|
|
12
|
+
* The reason it has to be declared is for TSyringe to find the decorators in the
|
|
13
|
+
* same modules as where the dependency container is declared (in this case,
|
|
14
|
+
* the browser module, with the dependancy container in dependencies.ts).
|
|
15
|
+
* @hidden
|
|
16
|
+
*/
|
|
3
17
|
export default class StorageUtilityBrowser extends StorageUtility {
|
|
4
18
|
constructor(secureStorage: IStorage, insecureStorage: IStorage);
|
|
5
19
|
}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@inrupt/solid-client-authn-browser",
|
|
3
|
-
"version": "1.17.
|
|
3
|
+
"version": "1.17.3",
|
|
4
4
|
"license": "MIT",
|
|
5
5
|
"types": "dist/index.d.ts",
|
|
6
6
|
"browser": "dist/index.js",
|
|
@@ -26,18 +26,18 @@
|
|
|
26
26
|
},
|
|
27
27
|
"devDependencies": {
|
|
28
28
|
"@types/node": "^20.6.0",
|
|
29
|
-
"@types/uuid": "^9.0.
|
|
29
|
+
"@types/uuid": "^9.0.4"
|
|
30
30
|
},
|
|
31
31
|
"dependencies": {
|
|
32
|
-
"@inrupt/oidc-client-ext": "^1.17.
|
|
33
|
-
"@inrupt/solid-client-authn-core": "^1.17.
|
|
32
|
+
"@inrupt/oidc-client-ext": "^1.17.3",
|
|
33
|
+
"@inrupt/solid-client-authn-core": "^1.17.3",
|
|
34
34
|
"@inrupt/universal-fetch": "^1.0.2",
|
|
35
35
|
"events": "^3.3.0",
|
|
36
36
|
"jose": "^4.14.6",
|
|
37
|
-
"uuid": "^9.0.
|
|
37
|
+
"uuid": "^9.0.1"
|
|
38
38
|
},
|
|
39
39
|
"publishConfig": {
|
|
40
40
|
"access": "public"
|
|
41
41
|
},
|
|
42
|
-
"gitHead": "
|
|
42
|
+
"gitHead": "ef10e8366ee8511f726b6cf5918f9e9c6653c6cc"
|
|
43
43
|
}
|
|
@@ -336,8 +336,12 @@ describe("ClientAuthentication", () => {
|
|
|
336
336
|
// TODO: add tests for events & errors
|
|
337
337
|
|
|
338
338
|
it("reverts back to un-authenticated fetch on logout", async () => {
|
|
339
|
-
|
|
340
|
-
|
|
339
|
+
window.history.replaceState = jest
|
|
340
|
+
.fn<typeof window.history.replaceState>()
|
|
341
|
+
.mockImplementationOnce((_data, _unused, url) => {
|
|
342
|
+
// Pretend the current location is updated
|
|
343
|
+
window.location.href = url as string;
|
|
344
|
+
});
|
|
341
345
|
const clientAuthn = getClientAuthentication();
|
|
342
346
|
|
|
343
347
|
const unauthFetch = clientAuthn.fetch;
|
|
@@ -402,13 +406,17 @@ describe("ClientAuthentication", () => {
|
|
|
402
406
|
mockEmitter.emit = jest.fn<typeof mockEmitter.emit>();
|
|
403
407
|
|
|
404
408
|
it("calls handle redirect", async () => {
|
|
405
|
-
|
|
406
|
-
|
|
409
|
+
window.history.replaceState = jest
|
|
410
|
+
.fn<typeof window.history.replaceState>()
|
|
411
|
+
.mockImplementationOnce((_data, _unused, url) => {
|
|
412
|
+
// Pretend the current location is updated
|
|
413
|
+
window.location.href = url as string;
|
|
414
|
+
});
|
|
407
415
|
const expectedResult = SessionCreatorCreateResponse;
|
|
408
416
|
const clientAuthn = getClientAuthentication();
|
|
409
417
|
const unauthFetch = clientAuthn.fetch;
|
|
410
418
|
const url =
|
|
411
|
-
"https://
|
|
419
|
+
"https://example.org/redirect?state=userId&id_token=idToken&access_token=accessToken";
|
|
412
420
|
const redirectInfo = await clientAuthn.handleIncomingRedirect(
|
|
413
421
|
url,
|
|
414
422
|
mockEmitter,
|
|
@@ -436,8 +444,12 @@ describe("ClientAuthentication", () => {
|
|
|
436
444
|
});
|
|
437
445
|
|
|
438
446
|
it("clears the current IRI from OAuth query parameters in the auth code flow", async () => {
|
|
439
|
-
|
|
440
|
-
|
|
447
|
+
window.history.replaceState = jest
|
|
448
|
+
.fn<typeof window.history.replaceState>()
|
|
449
|
+
.mockImplementationOnce((_data, _unused, url) => {
|
|
450
|
+
// Pretend the current location is updated
|
|
451
|
+
window.location.href = url as string;
|
|
452
|
+
});
|
|
441
453
|
const clientAuthn = getClientAuthentication();
|
|
442
454
|
const url =
|
|
443
455
|
"https://coolapp.com/redirect?state=someState&code=someAuthCode&iss=someIssuer";
|
|
@@ -452,8 +464,12 @@ describe("ClientAuthentication", () => {
|
|
|
452
464
|
});
|
|
453
465
|
|
|
454
466
|
it("clears the current IRI from OAuth query parameters even if auth flow fails", async () => {
|
|
455
|
-
|
|
456
|
-
|
|
467
|
+
window.history.replaceState = jest
|
|
468
|
+
.fn<typeof window.history.replaceState>()
|
|
469
|
+
.mockImplementationOnce((_data, _unused, url) => {
|
|
470
|
+
// Pretend the current location is updated
|
|
471
|
+
window.location.href = url as string;
|
|
472
|
+
});
|
|
457
473
|
|
|
458
474
|
mockHandleIncomingRedirect.mockImplementationOnce(() =>
|
|
459
475
|
Promise.reject(new Error("Something went wrong")),
|
|
@@ -464,8 +480,7 @@ describe("ClientAuthentication", () => {
|
|
|
464
480
|
const url =
|
|
465
481
|
"https://coolapp.com/redirect?state=someState&code=someAuthCode";
|
|
466
482
|
await clientAuthn.handleIncomingRedirect(url, mockEmitter);
|
|
467
|
-
|
|
468
|
-
expect(history.replaceState).toHaveBeenCalledWith(
|
|
483
|
+
expect(window.history.replaceState).toHaveBeenCalledWith(
|
|
469
484
|
null,
|
|
470
485
|
"",
|
|
471
486
|
"https://coolapp.com/redirect",
|
|
@@ -479,14 +494,17 @@ describe("ClientAuthentication", () => {
|
|
|
479
494
|
});
|
|
480
495
|
|
|
481
496
|
it("clears the current IRI from OAuth query parameters in the implicit flow", async () => {
|
|
482
|
-
|
|
483
|
-
|
|
497
|
+
window.history.replaceState = jest
|
|
498
|
+
.fn<typeof window.history.replaceState>()
|
|
499
|
+
.mockImplementationOnce((_data, _unused, url) => {
|
|
500
|
+
// Pretend the current location is updated
|
|
501
|
+
window.location.href = url as string;
|
|
502
|
+
});
|
|
484
503
|
const clientAuthn = getClientAuthentication();
|
|
485
504
|
const url =
|
|
486
505
|
"https://coolapp.com/redirect?state=someState&id_token=idToken&access_token=accessToken";
|
|
487
506
|
await clientAuthn.handleIncomingRedirect(url, mockEmitter);
|
|
488
|
-
|
|
489
|
-
expect(history.replaceState).toHaveBeenCalledWith(
|
|
507
|
+
expect(window.history.replaceState).toHaveBeenCalledWith(
|
|
490
508
|
null,
|
|
491
509
|
"",
|
|
492
510
|
"https://coolapp.com/redirect",
|
|
@@ -495,14 +513,17 @@ describe("ClientAuthentication", () => {
|
|
|
495
513
|
});
|
|
496
514
|
|
|
497
515
|
it("preserves non-OAuth query strings", async () => {
|
|
498
|
-
|
|
499
|
-
|
|
516
|
+
window.history.replaceState = jest
|
|
517
|
+
.fn<typeof window.history.replaceState>()
|
|
518
|
+
.mockImplementationOnce((_data, _unused, url) => {
|
|
519
|
+
// Pretend the current location is updated
|
|
520
|
+
window.location.href = url as string;
|
|
521
|
+
});
|
|
500
522
|
const clientAuthn = getClientAuthentication();
|
|
501
523
|
const url =
|
|
502
524
|
"https://coolapp.com/redirect?state=someState&code=someAuthCode&someQuery=someValue";
|
|
503
525
|
await clientAuthn.handleIncomingRedirect(url, mockEmitter);
|
|
504
|
-
|
|
505
|
-
expect(history.replaceState).toHaveBeenCalledWith(
|
|
526
|
+
expect(window.history.replaceState).toHaveBeenCalledWith(
|
|
506
527
|
null,
|
|
507
528
|
"",
|
|
508
529
|
"https://coolapp.com/redirect?someQuery=someValue",
|
|
@@ -109,7 +109,7 @@ export default class ClientAuthentication extends ClientAuthenticationBase {
|
|
|
109
109
|
this.boundLogout = redirectInfo.getLogoutUrl;
|
|
110
110
|
|
|
111
111
|
// Strip the oauth params:
|
|
112
|
-
this.cleanUrlAfterRedirect(url);
|
|
112
|
+
await this.cleanUrlAfterRedirect(url);
|
|
113
113
|
|
|
114
114
|
return {
|
|
115
115
|
isLoggedIn: redirectInfo.isLoggedIn,
|
|
@@ -119,7 +119,7 @@ export default class ClientAuthentication extends ClientAuthenticationBase {
|
|
|
119
119
|
};
|
|
120
120
|
} catch (err) {
|
|
121
121
|
// Strip the oauth params:
|
|
122
|
-
this.cleanUrlAfterRedirect(url);
|
|
122
|
+
await this.cleanUrlAfterRedirect(url);
|
|
123
123
|
|
|
124
124
|
// FIXME: EVENTS.ERROR should be errorCode, errorDescription
|
|
125
125
|
//
|
|
@@ -132,7 +132,7 @@ export default class ClientAuthentication extends ClientAuthenticationBase {
|
|
|
132
132
|
}
|
|
133
133
|
};
|
|
134
134
|
|
|
135
|
-
private cleanUrlAfterRedirect(url: string): void {
|
|
135
|
+
private async cleanUrlAfterRedirect(url: string): Promise<void> {
|
|
136
136
|
const cleanedUpUrl = new URL(url);
|
|
137
137
|
cleanedUpUrl.searchParams.delete("state");
|
|
138
138
|
// For auth code flow
|
|
@@ -151,5 +151,16 @@ export default class ClientAuthentication extends ClientAuthenticationBase {
|
|
|
151
151
|
// authentication library will be called again with what are now invalid
|
|
152
152
|
// query parameters!).
|
|
153
153
|
window.history.replaceState(null, "", cleanedUpUrl.toString());
|
|
154
|
+
while (window.location.href !== cleanedUpUrl.href) {
|
|
155
|
+
// Poll the current URL every ms. Active polling is required because
|
|
156
|
+
// window.history.replaceState is asynchronous, but the associated
|
|
157
|
+
// 'popstate' event which should be listened to is only sent on active
|
|
158
|
+
// navigation, which we will not have here.
|
|
159
|
+
// See https://developer.mozilla.org/en-US/docs/Web/API/Window/popstate_event#when_popstate_is_sent
|
|
160
|
+
// eslint-disable-next-line no-await-in-loop
|
|
161
|
+
await new Promise<void>((resolve) => {
|
|
162
|
+
setTimeout(() => resolve(), 1);
|
|
163
|
+
});
|
|
164
|
+
}
|
|
154
165
|
}
|
|
155
166
|
}
|
package/src/Session.spec.ts
CHANGED
|
@@ -246,10 +246,9 @@ describe("Session", () => {
|
|
|
246
246
|
it("uses current window location as default redirect URL", async () => {
|
|
247
247
|
mockLocation("https://some.url");
|
|
248
248
|
const clientAuthentication = mockClientAuthentication();
|
|
249
|
-
const incomingRedirectHandler = jest
|
|
250
|
-
clientAuthentication,
|
|
251
|
-
|
|
252
|
-
);
|
|
249
|
+
const incomingRedirectHandler = jest
|
|
250
|
+
.spyOn(clientAuthentication, "handleIncomingRedirect")
|
|
251
|
+
.mockResolvedValue(undefined);
|
|
253
252
|
|
|
254
253
|
const mySession = new Session({ clientAuthentication });
|
|
255
254
|
await mySession.handleIncomingRedirect();
|
|
@@ -259,13 +258,12 @@ describe("Session", () => {
|
|
|
259
258
|
);
|
|
260
259
|
});
|
|
261
260
|
|
|
262
|
-
it("wraps
|
|
261
|
+
it("wraps ClientAuthentication handleIncomingRedirect", async () => {
|
|
263
262
|
mockLocation("https://some.url");
|
|
264
263
|
const clientAuthentication = mockClientAuthentication();
|
|
265
|
-
const incomingRedirectHandler = jest
|
|
266
|
-
clientAuthentication,
|
|
267
|
-
|
|
268
|
-
);
|
|
264
|
+
const incomingRedirectHandler = jest
|
|
265
|
+
.spyOn(clientAuthentication, "handleIncomingRedirect")
|
|
266
|
+
.mockResolvedValue(undefined);
|
|
269
267
|
const mySession = new Session({ clientAuthentication });
|
|
270
268
|
await mySession.handleIncomingRedirect("https://some.url");
|
|
271
269
|
expect(incomingRedirectHandler).toHaveBeenCalled();
|
|
@@ -401,10 +399,9 @@ describe("Session", () => {
|
|
|
401
399
|
|
|
402
400
|
it("preserves a binding to its Session instance", async () => {
|
|
403
401
|
const clientAuthentication = mockClientAuthentication();
|
|
404
|
-
const incomingRedirectHandler = jest
|
|
405
|
-
clientAuthentication,
|
|
406
|
-
|
|
407
|
-
);
|
|
402
|
+
const incomingRedirectHandler = jest
|
|
403
|
+
.spyOn(clientAuthentication, "handleIncomingRedirect")
|
|
404
|
+
.mockResolvedValue(undefined);
|
|
408
405
|
const mySession = new Session({ clientAuthentication });
|
|
409
406
|
const objectWithHandleIncomingRedirect = {
|
|
410
407
|
handleIncomingRedirect: mySession.handleIncomingRedirect,
|