@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/src/Session.spec.ts
CHANGED
|
@@ -40,7 +40,7 @@ import type ClientAuthentication from "./ClientAuthentication";
|
|
|
40
40
|
|
|
41
41
|
jest.mock("@inrupt/universal-fetch", () => {
|
|
42
42
|
const fetchModule = jest.requireActual(
|
|
43
|
-
"@inrupt/universal-fetch"
|
|
43
|
+
"@inrupt/universal-fetch",
|
|
44
44
|
) as typeof UniversalFetch;
|
|
45
45
|
return {
|
|
46
46
|
...fetchModule,
|
|
@@ -164,7 +164,7 @@ describe("Session", () => {
|
|
|
164
164
|
expect.objectContaining({
|
|
165
165
|
tokenType: "Bearer",
|
|
166
166
|
}),
|
|
167
|
-
mySession
|
|
167
|
+
mySession,
|
|
168
168
|
);
|
|
169
169
|
});
|
|
170
170
|
|
|
@@ -246,26 +246,24 @@ 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();
|
|
256
255
|
expect(incomingRedirectHandler).toHaveBeenCalledWith(
|
|
257
256
|
"https://some.url",
|
|
258
|
-
mySession
|
|
257
|
+
mySession,
|
|
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();
|
|
@@ -299,12 +297,12 @@ describe("Session", () => {
|
|
|
299
297
|
sessionId: "a session ID",
|
|
300
298
|
webId: "https://some.webid#them",
|
|
301
299
|
};
|
|
302
|
-
}
|
|
300
|
+
},
|
|
303
301
|
);
|
|
304
302
|
const mySession = new Session({ clientAuthentication });
|
|
305
303
|
await mySession.handleIncomingRedirect("https://some.url");
|
|
306
304
|
expect(window.localStorage.getItem(KEY_CURRENT_SESSION)).toBe(
|
|
307
|
-
mySession.info.sessionId
|
|
305
|
+
mySession.info.sessionId,
|
|
308
306
|
);
|
|
309
307
|
});
|
|
310
308
|
|
|
@@ -317,7 +315,7 @@ describe("Session", () => {
|
|
|
317
315
|
sessionId: "a session ID",
|
|
318
316
|
webId: "https://some.webid#them",
|
|
319
317
|
};
|
|
320
|
-
}
|
|
318
|
+
},
|
|
321
319
|
);
|
|
322
320
|
const mySession = new Session({ clientAuthentication });
|
|
323
321
|
await mySession.handleIncomingRedirect("https://some.url");
|
|
@@ -325,7 +323,7 @@ describe("Session", () => {
|
|
|
325
323
|
await mySession.handleIncomingRedirect("https://some.url");
|
|
326
324
|
// The second request should not hit the wrapped function
|
|
327
325
|
expect(clientAuthentication.handleIncomingRedirect).toHaveBeenCalledTimes(
|
|
328
|
-
1
|
|
326
|
+
1,
|
|
329
327
|
);
|
|
330
328
|
});
|
|
331
329
|
|
|
@@ -336,7 +334,7 @@ describe("Session", () => {
|
|
|
336
334
|
const clientAuthentication = mockClientAuthentication();
|
|
337
335
|
const incomingRedirectHandler = jest.spyOn(
|
|
338
336
|
clientAuthentication,
|
|
339
|
-
"handleIncomingRedirect"
|
|
337
|
+
"handleIncomingRedirect",
|
|
340
338
|
);
|
|
341
339
|
incomingRedirectHandler.mockResolvedValueOnce({
|
|
342
340
|
isLoggedIn: true,
|
|
@@ -348,14 +346,14 @@ describe("Session", () => {
|
|
|
348
346
|
expect(mySession.info.expirationDate).toBe(MOCK_TIMESTAMP + 1337);
|
|
349
347
|
mySession.emit(EVENTS.SESSION_EXTENDED, 1337 * 2);
|
|
350
348
|
expect(mySession.info.expirationDate).toBe(
|
|
351
|
-
MOCK_TIMESTAMP + 1337 * 2 * 1000
|
|
349
|
+
MOCK_TIMESTAMP + 1337 * 2 * 1000,
|
|
352
350
|
);
|
|
353
351
|
});
|
|
354
352
|
|
|
355
353
|
it("leaves the session's info unchanged if no session is obtained after redirect", async () => {
|
|
356
354
|
const clientAuthentication = mockClientAuthentication();
|
|
357
355
|
clientAuthentication.handleIncomingRedirect = jest.fn(
|
|
358
|
-
async (_url: string) => undefined
|
|
356
|
+
async (_url: string) => undefined,
|
|
359
357
|
);
|
|
360
358
|
const mySession = new Session({ clientAuthentication }, "mySession");
|
|
361
359
|
await mySession.handleIncomingRedirect("https://some.url");
|
|
@@ -382,10 +380,10 @@ describe("Session", () => {
|
|
|
382
380
|
clientAuthentication.handleIncomingRedirect = jest.fn(blockingRequest);
|
|
383
381
|
const mySession = new Session({ clientAuthentication });
|
|
384
382
|
const firstTokenRequest = mySession.handleIncomingRedirect(
|
|
385
|
-
"https://my.app/?code=someCode&state=arizona"
|
|
383
|
+
"https://my.app/?code=someCode&state=arizona",
|
|
386
384
|
);
|
|
387
385
|
const secondTokenRequest = mySession.handleIncomingRedirect(
|
|
388
|
-
"https://my.app/?code=someCode&state=arizona"
|
|
386
|
+
"https://my.app/?code=someCode&state=arizona",
|
|
389
387
|
);
|
|
390
388
|
// We know that it has been set by the call to `blockingRequest`.
|
|
391
389
|
// eslint-disable-next-line @typescript-eslint/no-non-null-assertion
|
|
@@ -401,16 +399,15 @@ 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,
|
|
411
408
|
};
|
|
412
409
|
await objectWithHandleIncomingRedirect.handleIncomingRedirect(
|
|
413
|
-
"https://some.url"
|
|
410
|
+
"https://some.url",
|
|
414
411
|
);
|
|
415
412
|
expect(incomingRedirectHandler).toHaveBeenCalled();
|
|
416
413
|
});
|
|
@@ -422,7 +419,7 @@ describe("Session", () => {
|
|
|
422
419
|
mockLocation("https://mock.current/location");
|
|
423
420
|
const mockedStorage = new StorageUtility(
|
|
424
421
|
mockStorage({}),
|
|
425
|
-
mockStorage({})
|
|
422
|
+
mockStorage({}),
|
|
426
423
|
);
|
|
427
424
|
const clientAuthentication = mockClientAuthentication({
|
|
428
425
|
sessionInfoManager: mockSessionInfoManager(mockedStorage),
|
|
@@ -431,7 +428,7 @@ describe("Session", () => {
|
|
|
431
428
|
clientAuthentication.handleIncomingRedirect = jest
|
|
432
429
|
.fn()
|
|
433
430
|
.mockReturnValue(
|
|
434
|
-
incomingRedirectPromise
|
|
431
|
+
incomingRedirectPromise,
|
|
435
432
|
) as typeof clientAuthentication.handleIncomingRedirect;
|
|
436
433
|
const mySession = new Session({ clientAuthentication });
|
|
437
434
|
// eslint-disable-next-line no-void
|
|
@@ -456,7 +453,7 @@ describe("Session", () => {
|
|
|
456
453
|
}),
|
|
457
454
|
// No session information need to be mocked, because `validateCurrentSession`
|
|
458
455
|
// itself is mocked.
|
|
459
|
-
mockStorage({})
|
|
456
|
+
mockStorage({}),
|
|
460
457
|
);
|
|
461
458
|
const clientAuthentication = mockClientAuthentication({
|
|
462
459
|
sessionInfoManager: mockSessionInfoManager(mockedStorage),
|
|
@@ -465,15 +462,15 @@ describe("Session", () => {
|
|
|
465
462
|
clientAuthentication.handleIncomingRedirect = jest
|
|
466
463
|
.fn()
|
|
467
464
|
.mockReturnValueOnce(
|
|
468
|
-
incomingRedirectPromise
|
|
465
|
+
incomingRedirectPromise,
|
|
469
466
|
) as typeof clientAuthentication.handleIncomingRedirect;
|
|
470
467
|
const validateCurrentSessionPromise = Promise.resolve(
|
|
471
|
-
"https://some.issuer/"
|
|
468
|
+
"https://some.issuer/",
|
|
472
469
|
);
|
|
473
470
|
clientAuthentication.validateCurrentSession = jest
|
|
474
471
|
.fn()
|
|
475
472
|
.mockReturnValue(
|
|
476
|
-
validateCurrentSessionPromise
|
|
473
|
+
validateCurrentSessionPromise,
|
|
477
474
|
) as typeof clientAuthentication.validateCurrentSession;
|
|
478
475
|
|
|
479
476
|
const mySession = new Session({ clientAuthentication });
|
|
@@ -485,7 +482,7 @@ describe("Session", () => {
|
|
|
485
482
|
await incomingRedirectPromise;
|
|
486
483
|
await validateCurrentSessionPromise;
|
|
487
484
|
expect(window.localStorage.getItem(KEY_CURRENT_URL)).toBe(
|
|
488
|
-
"https://mock.current/location"
|
|
485
|
+
"https://mock.current/location",
|
|
489
486
|
);
|
|
490
487
|
});
|
|
491
488
|
|
|
@@ -505,7 +502,7 @@ describe("Session", () => {
|
|
|
505
502
|
[`${USER_SESSION_PREFIX}:${sessionId}`]: {
|
|
506
503
|
clientId: "value doesn't matter",
|
|
507
504
|
},
|
|
508
|
-
})
|
|
505
|
+
}),
|
|
509
506
|
);
|
|
510
507
|
const clientAuthentication = mockClientAuthentication({
|
|
511
508
|
sessionInfoManager: mockSessionInfoManager(mockedStorage),
|
|
@@ -513,7 +510,7 @@ describe("Session", () => {
|
|
|
513
510
|
clientAuthentication.handleIncomingRedirect = (
|
|
514
511
|
jest.fn() as any
|
|
515
512
|
).mockResolvedValue(
|
|
516
|
-
undefined
|
|
513
|
+
undefined,
|
|
517
514
|
) as typeof clientAuthentication.handleIncomingRedirect;
|
|
518
515
|
const mySession = new Session({ clientAuthentication });
|
|
519
516
|
// eslint-disable-next-line no-void
|
|
@@ -536,7 +533,7 @@ describe("Session", () => {
|
|
|
536
533
|
isLoggedIn: "true",
|
|
537
534
|
},
|
|
538
535
|
}),
|
|
539
|
-
mockStorage({})
|
|
536
|
+
mockStorage({}),
|
|
540
537
|
);
|
|
541
538
|
const clientAuthentication = mockClientAuthentication({
|
|
542
539
|
sessionInfoManager: mockSessionInfoManager(mockedStorage),
|
|
@@ -551,13 +548,13 @@ describe("Session", () => {
|
|
|
551
548
|
clientAuthentication.validateCurrentSession = jest
|
|
552
549
|
.fn()
|
|
553
550
|
.mockReturnValue(
|
|
554
|
-
validateCurrentSessionPromise
|
|
551
|
+
validateCurrentSessionPromise,
|
|
555
552
|
) as typeof clientAuthentication.validateCurrentSession;
|
|
556
553
|
const incomingRedirectPromise = Promise.resolve();
|
|
557
554
|
clientAuthentication.handleIncomingRedirect = jest
|
|
558
555
|
.fn()
|
|
559
556
|
.mockReturnValueOnce(
|
|
560
|
-
incomingRedirectPromise
|
|
557
|
+
incomingRedirectPromise,
|
|
561
558
|
) as typeof clientAuthentication.handleIncomingRedirect;
|
|
562
559
|
clientAuthentication.login = jest.fn<typeof clientAuthentication.login>();
|
|
563
560
|
|
|
@@ -579,11 +576,11 @@ describe("Session", () => {
|
|
|
579
576
|
clientSecret: "some client secret",
|
|
580
577
|
redirectUrl: "https://some.redirect/url",
|
|
581
578
|
},
|
|
582
|
-
expect.anything()
|
|
579
|
+
expect.anything(),
|
|
583
580
|
);
|
|
584
581
|
// Check that second parameter is of type session
|
|
585
582
|
expect(
|
|
586
|
-
(clientAuthentication.login as any).mock.calls[0][1]
|
|
583
|
+
(clientAuthentication.login as any).mock.calls[0][1],
|
|
587
584
|
).toBeInstanceOf(Session);
|
|
588
585
|
});
|
|
589
586
|
|
|
@@ -599,7 +596,7 @@ describe("Session", () => {
|
|
|
599
596
|
isLoggedIn: "true",
|
|
600
597
|
},
|
|
601
598
|
}),
|
|
602
|
-
mockStorage({})
|
|
599
|
+
mockStorage({}),
|
|
603
600
|
);
|
|
604
601
|
const clientAuthentication = mockClientAuthentication({
|
|
605
602
|
sessionInfoManager: mockSessionInfoManager(mockedStorage),
|
|
@@ -608,13 +605,13 @@ describe("Session", () => {
|
|
|
608
605
|
clientAuthentication.validateCurrentSession = jest
|
|
609
606
|
.fn()
|
|
610
607
|
.mockReturnValue(
|
|
611
|
-
validateCurrentSessionPromise
|
|
608
|
+
validateCurrentSessionPromise,
|
|
612
609
|
) as typeof clientAuthentication.validateCurrentSession;
|
|
613
610
|
const incomingRedirectPromise = Promise.resolve();
|
|
614
611
|
clientAuthentication.handleIncomingRedirect = jest
|
|
615
612
|
.fn()
|
|
616
613
|
.mockReturnValueOnce(
|
|
617
|
-
incomingRedirectPromise
|
|
614
|
+
incomingRedirectPromise,
|
|
618
615
|
) as typeof clientAuthentication.handleIncomingRedirect;
|
|
619
616
|
clientAuthentication.login = jest.fn<typeof clientAuthentication.login>();
|
|
620
617
|
|
|
@@ -640,7 +637,7 @@ describe("Session", () => {
|
|
|
640
637
|
isLoggedIn: "true",
|
|
641
638
|
},
|
|
642
639
|
}),
|
|
643
|
-
mockStorage({})
|
|
640
|
+
mockStorage({}),
|
|
644
641
|
);
|
|
645
642
|
const clientAuthentication = mockClientAuthentication({
|
|
646
643
|
sessionInfoManager: mockSessionInfoManager(mockedStorage),
|
|
@@ -656,7 +653,7 @@ describe("Session", () => {
|
|
|
656
653
|
clientAuthentication.handleIncomingRedirect = (
|
|
657
654
|
jest.fn() as any
|
|
658
655
|
).mockResolvedValue(
|
|
659
|
-
undefined
|
|
656
|
+
undefined,
|
|
660
657
|
) as typeof clientAuthentication.handleIncomingRedirect;
|
|
661
658
|
clientAuthentication.login = jest.fn<typeof clientAuthentication.login>();
|
|
662
659
|
|
|
@@ -679,7 +676,7 @@ describe("Session", () => {
|
|
|
679
676
|
isLoggedIn: "true",
|
|
680
677
|
},
|
|
681
678
|
}),
|
|
682
|
-
mockStorage({})
|
|
679
|
+
mockStorage({}),
|
|
683
680
|
);
|
|
684
681
|
const clientAuthentication = mockClientAuthentication({
|
|
685
682
|
sessionInfoManager: mockSessionInfoManager(mockedStorage),
|
|
@@ -720,7 +717,7 @@ describe("Session", () => {
|
|
|
720
717
|
isLoggedIn: "true",
|
|
721
718
|
},
|
|
722
719
|
}),
|
|
723
|
-
mockStorage({})
|
|
720
|
+
mockStorage({}),
|
|
724
721
|
);
|
|
725
722
|
const clientAuthentication = mockClientAuthentication({
|
|
726
723
|
sessionInfoManager: mockSessionInfoManager(mockedStorage),
|
|
@@ -759,7 +756,7 @@ describe("Session", () => {
|
|
|
759
756
|
isLoggedIn: "true",
|
|
760
757
|
},
|
|
761
758
|
}),
|
|
762
|
-
mockStorage({})
|
|
759
|
+
mockStorage({}),
|
|
763
760
|
);
|
|
764
761
|
const clientAuthentication = mockClientAuthentication({
|
|
765
762
|
sessionInfoManager: mockSessionInfoManager(mockedStorage),
|
|
@@ -781,7 +778,7 @@ describe("Session", () => {
|
|
|
781
778
|
mySession.emit(EVENTS.ERROR, "error", "error_description");
|
|
782
779
|
expect(onErrorCallback).toHaveBeenCalledWith(
|
|
783
780
|
"error",
|
|
784
|
-
"error_description"
|
|
781
|
+
"error_description",
|
|
785
782
|
);
|
|
786
783
|
});
|
|
787
784
|
});
|
|
@@ -807,7 +804,7 @@ describe("Session", () => {
|
|
|
807
804
|
it("does not call the registered callback if login isn't successful", async () => {
|
|
808
805
|
const failCallback = (): void => {
|
|
809
806
|
throw new Error(
|
|
810
|
-
"Should *NOT* call callback - this means test has failed!"
|
|
807
|
+
"Should *NOT* call callback - this means test has failed!",
|
|
811
808
|
);
|
|
812
809
|
};
|
|
813
810
|
const clientAuthentication = mockClientAuthentication();
|
|
@@ -818,12 +815,12 @@ describe("Session", () => {
|
|
|
818
815
|
sessionId: "a session ID",
|
|
819
816
|
webId: "https://some.webid#them",
|
|
820
817
|
};
|
|
821
|
-
}
|
|
818
|
+
},
|
|
822
819
|
);
|
|
823
820
|
const mySession = new Session({ clientAuthentication });
|
|
824
821
|
mySession.onLogin(failCallback);
|
|
825
822
|
await expect(
|
|
826
|
-
mySession.handleIncomingRedirect("https://some.url")
|
|
823
|
+
mySession.handleIncomingRedirect("https://some.url"),
|
|
827
824
|
).resolves.not.toThrow();
|
|
828
825
|
});
|
|
829
826
|
|
|
@@ -836,7 +833,7 @@ describe("Session", () => {
|
|
|
836
833
|
sessionId: "a session ID",
|
|
837
834
|
webId: "https://some.webid#them",
|
|
838
835
|
};
|
|
839
|
-
}
|
|
836
|
+
},
|
|
840
837
|
);
|
|
841
838
|
const mySession = new Session({ clientAuthentication });
|
|
842
839
|
const myCallback = jest.fn((): void => {
|
|
@@ -1035,7 +1032,7 @@ describe("Session", () => {
|
|
|
1035
1032
|
const mySession = new Session();
|
|
1036
1033
|
// @ts-expect-error onLogin is a function on Session, and not SessionEventEmitter
|
|
1037
1034
|
expect(() => mySession.events.onLogin(jest.fn())).toThrow(
|
|
1038
|
-
"[onLogin] is not supported"
|
|
1035
|
+
"[onLogin] is not supported",
|
|
1039
1036
|
);
|
|
1040
1037
|
});
|
|
1041
1038
|
});
|
package/src/Session.ts
CHANGED
|
@@ -101,7 +101,7 @@ export interface IHandleIncomingRedirectOptions {
|
|
|
101
101
|
export async function silentlyAuthenticate(
|
|
102
102
|
sessionId: string,
|
|
103
103
|
clientAuthn: ClientAuthentication,
|
|
104
|
-
session: Session
|
|
104
|
+
session: Session,
|
|
105
105
|
): Promise<boolean> {
|
|
106
106
|
const storedSessionInfo = await clientAuthn.validateCurrentSession(sessionId);
|
|
107
107
|
if (storedSessionInfo !== null) {
|
|
@@ -120,7 +120,7 @@ export async function silentlyAuthenticate(
|
|
|
120
120
|
clientSecret: storedSessionInfo.clientAppSecret,
|
|
121
121
|
tokenType: storedSessionInfo.tokenType ?? "DPoP",
|
|
122
122
|
},
|
|
123
|
-
session.events
|
|
123
|
+
session.events,
|
|
124
124
|
);
|
|
125
125
|
return true;
|
|
126
126
|
}
|
|
@@ -128,7 +128,7 @@ export async function silentlyAuthenticate(
|
|
|
128
128
|
}
|
|
129
129
|
|
|
130
130
|
function isLoggedIn(
|
|
131
|
-
sessionInfo?: ISessionInfo
|
|
131
|
+
sessionInfo?: ISessionInfo,
|
|
132
132
|
): sessionInfo is ISessionInfo & { isLoggedIn: true } {
|
|
133
133
|
return !!sessionInfo?.isLoggedIn;
|
|
134
134
|
}
|
|
@@ -170,7 +170,7 @@ export class Session extends EventEmitter implements IHasSessionEventListener {
|
|
|
170
170
|
*/
|
|
171
171
|
constructor(
|
|
172
172
|
sessionOptions: Partial<ISessionOptions> = {},
|
|
173
|
-
sessionId: string | undefined = undefined
|
|
173
|
+
sessionId: string | undefined = undefined,
|
|
174
174
|
) {
|
|
175
175
|
super();
|
|
176
176
|
// Until Session no longer implements EventEmitter, this.events is just a proxy
|
|
@@ -181,8 +181,8 @@ export class Session extends EventEmitter implements IHasSessionEventListener {
|
|
|
181
181
|
this,
|
|
182
182
|
buildProxyHandler(
|
|
183
183
|
Session.prototype,
|
|
184
|
-
"events only implements ISessionEventListener"
|
|
185
|
-
)
|
|
184
|
+
"events only implements ISessionEventListener",
|
|
185
|
+
),
|
|
186
186
|
);
|
|
187
187
|
if (sessionOptions.clientAuthentication) {
|
|
188
188
|
this.clientAuthentication = sessionOptions.clientAuthentication;
|
|
@@ -213,7 +213,7 @@ export class Session extends EventEmitter implements IHasSessionEventListener {
|
|
|
213
213
|
// (as opposed to using our storage abstraction layer) because it is only
|
|
214
214
|
// used in a browser-specific mechanism.
|
|
215
215
|
this.events.on(EVENTS.LOGIN, () =>
|
|
216
|
-
window.localStorage.setItem(KEY_CURRENT_SESSION, this.info.sessionId)
|
|
216
|
+
window.localStorage.setItem(KEY_CURRENT_SESSION, this.info.sessionId),
|
|
217
217
|
);
|
|
218
218
|
|
|
219
219
|
this.events.on(EVENTS.SESSION_EXPIRED, () => this.internalLogout(false));
|
|
@@ -237,7 +237,7 @@ export class Session extends EventEmitter implements IHasSessionEventListener {
|
|
|
237
237
|
// Defaults the token type to DPoP
|
|
238
238
|
tokenType: options.tokenType ?? "DPoP",
|
|
239
239
|
},
|
|
240
|
-
this.events
|
|
240
|
+
this.events,
|
|
241
241
|
);
|
|
242
242
|
// `login` redirects the user away from the app,
|
|
243
243
|
// so unless it throws an error, there is no code that should run afterwards
|
|
@@ -264,7 +264,7 @@ export class Session extends EventEmitter implements IHasSessionEventListener {
|
|
|
264
264
|
*/
|
|
265
265
|
private internalLogout = async (
|
|
266
266
|
emitSignal: boolean,
|
|
267
|
-
options?: ILogoutOptions
|
|
267
|
+
options?: ILogoutOptions,
|
|
268
268
|
): Promise<void> => {
|
|
269
269
|
// Clearing this value means that silent refresh will no longer be attempted.
|
|
270
270
|
// In particular, in the case of a silent authentication error it prevents
|
|
@@ -293,14 +293,20 @@ export class Session extends EventEmitter implements IHasSessionEventListener {
|
|
|
293
293
|
* ```
|
|
294
294
|
*
|
|
295
295
|
* IDP logout will log the user out of their Solid identity provider,
|
|
296
|
-
* and will redirect the user away from the application to do so.
|
|
296
|
+
* and will redirect the user away from the application to do so. In order
|
|
297
|
+
* for users to be redirected back to `postLogoutUrl` you MUST include the
|
|
298
|
+
* `postLogoutUrl` value in the `post_logout_redirect_uris` field in the
|
|
299
|
+
* [Client ID Document](https://docs.inrupt.com/ess/latest/security/authentication/#client-identifier-client-id).
|
|
297
300
|
* IDP logout can be performed as follows:
|
|
298
301
|
* ```typescript
|
|
299
302
|
* await session.logout({
|
|
300
303
|
* logoutType: 'idp',
|
|
301
304
|
* // An optional URL to redirect to after logout has completed;
|
|
302
|
-
* // this MUST match a logout URL listed in the
|
|
303
|
-
* // the application that is logged in.
|
|
305
|
+
* // this MUST match a logout URL listed in the Client ID Document
|
|
306
|
+
* // of the application that is logged in.
|
|
307
|
+
* // If the application is logged in with a Client ID that is not
|
|
308
|
+
* // a URI dereferencing to a Client ID Document then users will
|
|
309
|
+
* // not be redirected back to the `postLogoutUrl` after logout.
|
|
304
310
|
* postLogoutUrl: 'https://example.com/logout',
|
|
305
311
|
* // An optional value to be included in the query parameters
|
|
306
312
|
* // when the IDP provider redirects the user to the postLogoutRedirectUrl.
|
|
@@ -318,7 +324,7 @@ export class Session extends EventEmitter implements IHasSessionEventListener {
|
|
|
318
324
|
* @param options See {@see IHandleIncomingRedirectOptions}.
|
|
319
325
|
*/
|
|
320
326
|
handleIncomingRedirect = async (
|
|
321
|
-
inputOptions: string | IHandleIncomingRedirectOptions = {}
|
|
327
|
+
inputOptions: string | IHandleIncomingRedirectOptions = {},
|
|
322
328
|
): Promise<ISessionInfo | undefined> => {
|
|
323
329
|
if (this.info.isLoggedIn) {
|
|
324
330
|
return this.info;
|
|
@@ -334,7 +340,7 @@ export class Session extends EventEmitter implements IHasSessionEventListener {
|
|
|
334
340
|
this.tokenRequestInProgress = true;
|
|
335
341
|
const sessionInfo = await this.clientAuthentication.handleIncomingRedirect(
|
|
336
342
|
url,
|
|
337
|
-
this.events
|
|
343
|
+
this.events,
|
|
338
344
|
);
|
|
339
345
|
if (isLoggedIn(sessionInfo)) {
|
|
340
346
|
this.setSessionInfo(sessionInfo);
|
|
@@ -363,7 +369,7 @@ export class Session extends EventEmitter implements IHasSessionEventListener {
|
|
|
363
369
|
const attemptedSilentAuthentication = await silentlyAuthenticate(
|
|
364
370
|
storedSessionId,
|
|
365
371
|
this.clientAuthentication,
|
|
366
|
-
this
|
|
372
|
+
this,
|
|
367
373
|
);
|
|
368
374
|
// At this point, we know that the main window will imminently be redirected.
|
|
369
375
|
// However, this redirect is asynchronous and there is no way to halt execution
|
|
@@ -410,8 +416,8 @@ export class Session extends EventEmitter implements IHasSessionEventListener {
|
|
|
410
416
|
onError(
|
|
411
417
|
callback: (
|
|
412
418
|
error: string | null,
|
|
413
|
-
errorDescription?: string | null
|
|
414
|
-
) => unknown
|
|
419
|
+
errorDescription?: string | null,
|
|
420
|
+
) => unknown,
|
|
415
421
|
): void {
|
|
416
422
|
this.events.on(EVENTS.ERROR, callback);
|
|
417
423
|
}
|
|
@@ -441,7 +447,7 @@ export class Session extends EventEmitter implements IHasSessionEventListener {
|
|
|
441
447
|
}
|
|
442
448
|
|
|
443
449
|
private setSessionInfo(
|
|
444
|
-
sessionInfo: ISessionInfo & { isLoggedIn: true }
|
|
450
|
+
sessionInfo: ISessionInfo & { isLoggedIn: true },
|
|
445
451
|
): void {
|
|
446
452
|
this.info.isLoggedIn = sessionInfo.isLoggedIn;
|
|
447
453
|
this.info.webId = sessionInfo.webId;
|
|
@@ -48,7 +48,7 @@ type ClientAuthnDependencies = {
|
|
|
48
48
|
};
|
|
49
49
|
|
|
50
50
|
export const mockClientAuthentication = (
|
|
51
|
-
mocks?: Partial<ClientAuthnDependencies
|
|
51
|
+
mocks?: Partial<ClientAuthnDependencies>,
|
|
52
52
|
): ClientAuthentication => {
|
|
53
53
|
const storage = mocks?.storage ?? mockStorageUtility({});
|
|
54
54
|
return new ClientAuthentication(
|
|
@@ -56,6 +56,6 @@ export const mockClientAuthentication = (
|
|
|
56
56
|
mocks?.redirectHandler ?? mockIncomingRedirectHandler(),
|
|
57
57
|
mocks?.logoutHandler ?? mockLogoutHandler(storage),
|
|
58
58
|
mocks?.sessionInfoManager ?? mockSessionInfoManager(storage),
|
|
59
|
-
mocks?.issuerConfigFetcher ?? IssuerConfigFetcherMock
|
|
59
|
+
mocks?.issuerConfigFetcher ?? IssuerConfigFetcherMock,
|
|
60
60
|
);
|
|
61
61
|
};
|
package/src/defaultSession.ts
CHANGED
|
@@ -70,7 +70,8 @@ export const login: Session["login"] = (...args) => {
|
|
|
70
70
|
* Logs the user out of the application.
|
|
71
71
|
*
|
|
72
72
|
* By default this does not log the user out of their Solid identity provider.
|
|
73
|
-
* In order to do so, you must set the logoutType to `idp`.
|
|
73
|
+
* In order to do so, you must set the logoutType to `idp`. For usage details
|
|
74
|
+
* see {@link Session.logout}.
|
|
74
75
|
*
|
|
75
76
|
* @since 1.3.0
|
|
76
77
|
*/
|
|
@@ -113,7 +114,7 @@ export const onLogin: Session["onLogin"] = (...args) => {
|
|
|
113
114
|
* @param callback The function called when a user completes logout.
|
|
114
115
|
* @since 1.3.0
|
|
115
116
|
* @deprecated Prefer events.on(EVENTS.LOGOUT, callback)
|
|
116
|
-
|
|
117
|
+
*
|
|
117
118
|
*/
|
|
118
119
|
export const onLogout: Session["onLogout"] = (...args) => {
|
|
119
120
|
const session = getDefaultSession();
|
|
@@ -135,7 +136,7 @@ export const onSessionRestore: Session["onSessionRestore"] = (...args) => {
|
|
|
135
136
|
/**
|
|
136
137
|
* {@link SessionEventEmitter} instance to subscribe to events by the default session.
|
|
137
138
|
*
|
|
138
|
-
* @since
|
|
139
|
+
* @since 1.14.0
|
|
139
140
|
*/
|
|
140
141
|
export const events = (): Session["events"] => {
|
|
141
142
|
return getDefaultSession().events;
|
package/src/dependencies.ts
CHANGED
|
@@ -62,7 +62,7 @@ export function getClientAuthenticationWithDependencies(dependencies: {
|
|
|
62
62
|
|
|
63
63
|
const storageUtility = new StorageUtilityBrowser(
|
|
64
64
|
secureStorage,
|
|
65
|
-
insecureStorage
|
|
65
|
+
insecureStorage,
|
|
66
66
|
);
|
|
67
67
|
|
|
68
68
|
const issuerConfigFetcher = new IssuerConfigFetcher(storageUtility);
|
|
@@ -73,7 +73,7 @@ export function getClientAuthenticationWithDependencies(dependencies: {
|
|
|
73
73
|
const tokenRefresher = new TokenRefresher(
|
|
74
74
|
storageUtility,
|
|
75
75
|
issuerConfigFetcher,
|
|
76
|
-
clientRegistrar
|
|
76
|
+
clientRegistrar,
|
|
77
77
|
);
|
|
78
78
|
|
|
79
79
|
const redirector = new Redirector();
|
|
@@ -83,7 +83,7 @@ export function getClientAuthenticationWithDependencies(dependencies: {
|
|
|
83
83
|
storageUtility,
|
|
84
84
|
new AuthorizationCodeWithPkceOidcHandler(storageUtility, redirector),
|
|
85
85
|
issuerConfigFetcher,
|
|
86
|
-
clientRegistrar
|
|
86
|
+
clientRegistrar,
|
|
87
87
|
);
|
|
88
88
|
|
|
89
89
|
const redirectHandler = new AggregateRedirectHandler([
|
|
@@ -93,7 +93,7 @@ export function getClientAuthenticationWithDependencies(dependencies: {
|
|
|
93
93
|
sessionInfoManager,
|
|
94
94
|
issuerConfigFetcher,
|
|
95
95
|
clientRegistrar,
|
|
96
|
-
tokenRefresher
|
|
96
|
+
tokenRefresher,
|
|
97
97
|
),
|
|
98
98
|
// This catch-all class will always be able to handle the
|
|
99
99
|
// redirect IRI, so it must be registered last.
|
|
@@ -105,6 +105,6 @@ export function getClientAuthenticationWithDependencies(dependencies: {
|
|
|
105
105
|
redirectHandler,
|
|
106
106
|
new IWaterfallLogoutHandler(sessionInfoManager, redirector),
|
|
107
107
|
sessionInfoManager,
|
|
108
|
-
issuerConfigFetcher
|
|
108
|
+
issuerConfigFetcher,
|
|
109
109
|
);
|
|
110
110
|
}
|