1 |
|
2 |
|
3 |
|
4 |
|
5 |
|
6 |
|
7 |
|
8 |
|
9 |
|
10 |
|
11 |
|
12 |
|
13 |
|
14 |
|
15 |
|
16 |
|
17 |
|
18 |
|
19 |
|
20 |
|
21 |
|
22 | import { jest, it, describe, expect } from "@jest/globals";
|
23 | import {
|
24 | IIssuerConfig,
|
25 | mockStorageUtility,
|
26 | StorageUtility,
|
27 | USER_SESSION_PREFIX,
|
28 | mockStorage,
|
29 | } from "@inrupt/solid-client-authn-core";
|
30 |
|
31 | import { JWK, SignJWT, parseJwk } from "@inrupt/jose-legacy-modules";
|
32 | import { LoginHandlerMock } from "../src/login/__mocks__/LoginHandler";
|
33 | import {
|
34 | RedirectHandlerMock,
|
35 | RedirectHandlerResponse,
|
36 | } from "../src/login/oidc/redirectHandler/__mocks__/RedirectHandler";
|
37 | import { LogoutHandlerMock } from "../src/logout/__mocks__/LogoutHandler";
|
38 | import { mockSessionInfoManager } from "../src/sessionInfo/__mocks__/SessionInfoManager";
|
39 | import ClientAuthentication from "../src/ClientAuthentication";
|
40 | import { KEY_CURRENT_SESSION } from "../src/constant";
|
41 | import {
|
42 | mockDefaultIssuerConfigFetcher,
|
43 | mockIssuerConfigFetcher,
|
44 | } from "../src/login/oidc/__mocks__/IssuerConfigFetcher";
|
45 | import { LocalStorageMock } from "../src/storage/__mocks__/LocalStorage";
|
46 |
|
47 | jest.mock("@inrupt/solid-client-authn-core", () => {
|
48 | const actualCoreModule = jest.requireActual(
|
49 | "@inrupt/solid-client-authn-core"
|
50 |
|
51 | ) as any;
|
52 | return {
|
53 |
|
54 |
|
55 | ...actualCoreModule,
|
56 | };
|
57 | });
|
58 |
|
59 | const mockJwk = (): JWK => {
|
60 | return {
|
61 | kty: "EC",
|
62 | kid: "oOArcXxcwvsaG21jAx_D5CHr4BgVCzCEtlfmNFQtU0s",
|
63 | alg: "ES256",
|
64 | crv: "P-256",
|
65 | x: "0dGe_s-urLhD3mpqYqmSXrqUZApVV5ZNxMJXg7Vp-2A",
|
66 | y: "-oMe9gGkpfIrnJ0aiSUHMdjqYVm5ZrGCeQmRKoIIfj8",
|
67 | d: "yR1bCsR7m4hjFCvWo8Jw3OfNR4aiYDAFbBD9nkudJKM",
|
68 | };
|
69 | };
|
70 |
|
71 | const mockAnotherJwk = (): JWK => {
|
72 | return {
|
73 | kty: "EC",
|
74 | kid: "oOArcXxcwvsaG21jAx_D5CHr4BgVCzCEtlfmNFQtU0s",
|
75 | alg: "ES256",
|
76 | crv: "P-256",
|
77 | x: "0dGe_s-urLhD3mpqYqmSXriUZApVV5ZNxMJXg7Vp-2A",
|
78 | y: "-oMe9gGkpfIr1J0aiSUHMdjqYVm5ZrGCeQmRKoIIfj8",
|
79 | d: "yR1bCsR8m4hjFCvWo8Jw3OfNR4aiYDAFbBD9nkudJKM",
|
80 | };
|
81 | };
|
82 |
|
83 | const mockIdTokenPayload = (
|
84 | subject: string,
|
85 | issuer: string,
|
86 | audience: string
|
87 | ): Record<string, string | number> => {
|
88 | return {
|
89 | sub: subject,
|
90 | iss: issuer,
|
91 | aud: audience,
|
92 | exp: 1662266216,
|
93 | iat: 1462266216,
|
94 | };
|
95 | };
|
96 |
|
97 | type SessionStorageOptions = {
|
98 | clientId: string;
|
99 | issuer: string;
|
100 | };
|
101 |
|
102 | const mockSessionStorage = async (
|
103 | sessionId: string,
|
104 | idTokenPayload: Record<string, string | number> = {},
|
105 | options: SessionStorageOptions = {
|
106 | clientId: "https://some.app/registration",
|
107 | issuer: "https://some.issuer",
|
108 | }
|
109 | ): Promise<StorageUtility> => {
|
110 | return new StorageUtility(
|
111 | mockStorage({
|
112 | [`${USER_SESSION_PREFIX}:${sessionId}`]: {
|
113 | isLoggedIn: "true",
|
114 | webId: "https://my.pod/profile#me",
|
115 | },
|
116 | }),
|
117 | mockStorage({
|
118 | [`${USER_SESSION_PREFIX}:${sessionId}`]: {
|
119 | idToken: await new SignJWT(idTokenPayload)
|
120 | .setProtectedHeader({
|
121 | alg: "ES256",
|
122 | })
|
123 | .setIssuedAt()
|
124 | .sign(await parseJwk(mockJwk()), {}),
|
125 | clientId: options.clientId,
|
126 | issuer: options.issuer,
|
127 | },
|
128 | })
|
129 | );
|
130 | };
|
131 |
|
132 | const mockLocalStorage = (stored: Record<string, string>) => {
|
133 |
|
134 |
|
135 |
|
136 |
|
137 | Object.defineProperty(window, "localStorage", {
|
138 | value: new LocalStorageMock(stored),
|
139 | writable: true,
|
140 | });
|
141 | };
|
142 |
|
143 | describe("ClientAuthentication", () => {
|
144 | const defaultMocks = {
|
145 | loginHandler: LoginHandlerMock,
|
146 | redirectHandler: RedirectHandlerMock,
|
147 | logoutHandler: LogoutHandlerMock,
|
148 | sessionInfoManager: mockSessionInfoManager(mockStorageUtility({})),
|
149 | issuerConfigFetcher: mockDefaultIssuerConfigFetcher(),
|
150 | };
|
151 |
|
152 | function getClientAuthentication(
|
153 | mocks: Partial<typeof defaultMocks> = defaultMocks
|
154 | ): ClientAuthentication {
|
155 | return new ClientAuthentication(
|
156 | mocks.loginHandler ?? defaultMocks.loginHandler,
|
157 | mocks.redirectHandler ?? defaultMocks.redirectHandler,
|
158 | mocks.logoutHandler ?? defaultMocks.logoutHandler,
|
159 | mocks.sessionInfoManager ?? defaultMocks.sessionInfoManager,
|
160 | mocks.issuerConfigFetcher ?? defaultMocks.issuerConfigFetcher
|
161 | );
|
162 | }
|
163 |
|
164 | describe("login", () => {
|
165 | it("calls login, and defaults to a DPoP token", async () => {
|
166 | const clientAuthn = getClientAuthentication();
|
167 | await clientAuthn.login({
|
168 | sessionId: "mySession",
|
169 | tokenType: "DPoP",
|
170 | clientId: "coolApp",
|
171 | redirectUrl: "https://coolapp.com/redirect",
|
172 | oidcIssuer: "https://idp.com",
|
173 | });
|
174 | expect(defaultMocks.loginHandler.handle).toHaveBeenCalledWith({
|
175 | sessionId: "mySession",
|
176 | clientId: "coolApp",
|
177 | redirectUrl: "https://coolapp.com/redirect",
|
178 | oidcIssuer: "https://idp.com",
|
179 | clientName: "coolApp",
|
180 | clientSecret: undefined,
|
181 | handleRedirect: undefined,
|
182 | tokenType: "DPoP",
|
183 | });
|
184 | });
|
185 |
|
186 | it("request a bearer token if specified", async () => {
|
187 | const clientAuthn = getClientAuthentication();
|
188 | await clientAuthn.login({
|
189 | sessionId: "mySession",
|
190 | clientId: "coolApp",
|
191 | redirectUrl: "https://coolapp.com/redirect",
|
192 | oidcIssuer: "https://idp.com",
|
193 | tokenType: "Bearer",
|
194 | });
|
195 | expect(defaultMocks.loginHandler.handle).toHaveBeenCalledWith({
|
196 | sessionId: "mySession",
|
197 | clientId: "coolApp",
|
198 | redirectUrl: "https://coolapp.com/redirect",
|
199 | oidcIssuer: "https://idp.com",
|
200 | clientName: "coolApp",
|
201 | clientSecret: undefined,
|
202 | handleRedirect: undefined,
|
203 | tokenType: "Bearer",
|
204 | });
|
205 | });
|
206 |
|
207 | it("should clear the local storage when logging in", async () => {
|
208 | const nonEmptyStorage = mockStorageUtility({
|
209 | someUser: { someKey: "someValue" },
|
210 | });
|
211 | await nonEmptyStorage.setForUser(
|
212 | "someUser",
|
213 | { someKey: "someValue" },
|
214 | { secure: true }
|
215 | );
|
216 | const clientAuthn = getClientAuthentication({
|
217 | sessionInfoManager: mockSessionInfoManager(nonEmptyStorage),
|
218 | });
|
219 | await clientAuthn.login({
|
220 | sessionId: "someUser",
|
221 | tokenType: "DPoP",
|
222 | clientId: "coolApp",
|
223 | clientName: "coolApp Name",
|
224 | redirectUrl: "https://coolapp.com/redirect",
|
225 | oidcIssuer: "https://idp.com",
|
226 | });
|
227 | await expect(
|
228 | nonEmptyStorage.getForUser("someUser", "someKey", { secure: true })
|
229 | ).resolves.toBeUndefined();
|
230 | await expect(
|
231 | nonEmptyStorage.getForUser("someUser", "someKey", { secure: false })
|
232 | ).resolves.toBeUndefined();
|
233 |
|
234 | await expect(
|
235 | nonEmptyStorage.get("clientKey", { secure: false })
|
236 | ).resolves.toBeUndefined();
|
237 | });
|
238 | });
|
239 |
|
240 | describe("fetch", () => {
|
241 | it("calls fetch", async () => {
|
242 | window.fetch = jest.fn();
|
243 | const clientAuthn = getClientAuthentication();
|
244 | await clientAuthn.fetch("https://html5zombo.com");
|
245 | expect(window.fetch).toHaveBeenCalledWith(
|
246 | "https://html5zombo.com",
|
247 | undefined
|
248 | );
|
249 | });
|
250 | });
|
251 |
|
252 | describe("logout", () => {
|
253 | it("reverts back to un-authenticated fetch on logout", async () => {
|
254 | window.fetch = jest.fn();
|
255 |
|
256 | history.replaceState = jest.fn();
|
257 | const clientAuthn = getClientAuthentication();
|
258 |
|
259 | const unauthFetch = clientAuthn.fetch;
|
260 |
|
261 | const url =
|
262 | "https://coolapp.com/redirect?state=userId&id_token=idToken&access_token=accessToken";
|
263 | await clientAuthn.handleIncomingRedirect(url);
|
264 |
|
265 |
|
266 | expect(clientAuthn.fetch).not.toBe(unauthFetch);
|
267 |
|
268 | await clientAuthn.logout("mySession");
|
269 | const spyFetch = jest.spyOn(window, "fetch");
|
270 | await clientAuthn.fetch("https://example.com", {
|
271 | credentials: "omit",
|
272 | });
|
273 |
|
274 | expect(clientAuthn.fetch).toBe(unauthFetch);
|
275 | expect(spyFetch).toHaveBeenCalledWith("https://example.com", {
|
276 | credentials: "omit",
|
277 | });
|
278 | });
|
279 | });
|
280 |
|
281 | describe("getAllSessionInfo", () => {
|
282 | it("creates a session for the global user", async () => {
|
283 | const clientAuthn = getClientAuthentication();
|
284 | await expect(() => clientAuthn.getAllSessionInfo()).rejects.toThrow(
|
285 | "Not implemented"
|
286 | );
|
287 | });
|
288 | });
|
289 |
|
290 | describe("getSessionInfo", () => {
|
291 | it("creates a session for the global user", async () => {
|
292 | const sessionInfo = {
|
293 | isLoggedIn: "true",
|
294 | sessionId: "mySession",
|
295 | webId: "https://pod.com/profile/card#me",
|
296 | };
|
297 | const clientAuthn = getClientAuthentication({
|
298 | sessionInfoManager: mockSessionInfoManager(
|
299 | mockStorageUtility(
|
300 | {
|
301 | "solidClientAuthenticationUser:mySession": { ...sessionInfo },
|
302 | },
|
303 | true
|
304 | )
|
305 | ),
|
306 | });
|
307 | const session = await clientAuthn.getSessionInfo("mySession");
|
308 |
|
309 | expect(session).toEqual({
|
310 | ...sessionInfo,
|
311 | isLoggedIn: true,
|
312 | tokenType: "DPoP",
|
313 | });
|
314 | });
|
315 | });
|
316 |
|
317 | describe("handleIncomingRedirect", () => {
|
318 | it("calls handle redirect", async () => {
|
319 |
|
320 | history.replaceState = jest.fn();
|
321 | const clientAuthn = getClientAuthentication();
|
322 | const unauthFetch = clientAuthn.fetch;
|
323 | const url =
|
324 | "https://coolapp.com/redirect?state=userId&id_token=idToken&access_token=accessToken";
|
325 | const redirectInfo = await clientAuthn.handleIncomingRedirect(url);
|
326 |
|
327 |
|
328 |
|
329 |
|
330 |
|
331 |
|
332 | expect(redirectInfo?.sessionId).toEqual(
|
333 | RedirectHandlerResponse.sessionId
|
334 | );
|
335 | expect(redirectInfo?.webId).toEqual(RedirectHandlerResponse.webId);
|
336 | expect(redirectInfo?.isLoggedIn).toEqual(
|
337 | RedirectHandlerResponse.isLoggedIn
|
338 | );
|
339 | expect(redirectInfo?.expirationDate).toEqual(
|
340 | RedirectHandlerResponse.expirationDate
|
341 | );
|
342 | expect(defaultMocks.redirectHandler.handle).toHaveBeenCalledWith(url);
|
343 |
|
344 |
|
345 | expect(clientAuthn.fetch).not.toBe(unauthFetch);
|
346 | });
|
347 |
|
348 | it("clears the current IRI from OAuth query parameters in the auth code flow", async () => {
|
349 |
|
350 | history.replaceState = jest.fn();
|
351 | const clientAuthn = getClientAuthentication();
|
352 | const url =
|
353 | "https://coolapp.com/redirect?state=someState&code=someAuthCode";
|
354 | await clientAuthn.handleIncomingRedirect(url);
|
355 |
|
356 | expect(history.replaceState).toHaveBeenCalledWith(
|
357 | null,
|
358 | "",
|
359 | "https://coolapp.com/redirect"
|
360 | );
|
361 | });
|
362 |
|
363 | it("clears the current IRI from OAuth query parameters in the implicit flow", async () => {
|
364 |
|
365 | history.replaceState = jest.fn();
|
366 | const clientAuthn = getClientAuthentication();
|
367 | const url =
|
368 | "https://coolapp.com/redirect?state=someState&id_token=idToken&access_token=accessToken";
|
369 | await clientAuthn.handleIncomingRedirect(url);
|
370 |
|
371 | expect(history.replaceState).toHaveBeenCalledWith(
|
372 | null,
|
373 | "",
|
374 | "https://coolapp.com/redirect"
|
375 | );
|
376 | });
|
377 |
|
378 | it("preserves non-OAuth query strings", async () => {
|
379 |
|
380 | history.replaceState = jest.fn();
|
381 | const clientAuthn = getClientAuthentication();
|
382 | const url =
|
383 | "https://coolapp.com/redirect?state=someState&code=someAuthCode&someQuery=someValue";
|
384 | await clientAuthn.handleIncomingRedirect(url);
|
385 |
|
386 | expect(history.replaceState).toHaveBeenCalledWith(
|
387 | null,
|
388 | "",
|
389 | "https://coolapp.com/redirect?someQuery=someValue"
|
390 | );
|
391 | });
|
392 | });
|
393 |
|
394 | describe("getCurrentIssuer", () => {
|
395 |
|
396 |
|
397 |
|
398 |
|
399 | it("returns null no current session is in storage", async () => {
|
400 | const clientAuthn = getClientAuthentication({});
|
401 |
|
402 | await expect(clientAuthn.validateCurrentSession()).resolves.toBeNull();
|
403 | });
|
404 |
|
405 | it("returns null if the current session has no stored issuer", async () => {
|
406 | const sessionId = "mySession";
|
407 | mockLocalStorage({
|
408 | [KEY_CURRENT_SESSION]: sessionId,
|
409 | });
|
410 |
|
411 | const mockedStorage = new StorageUtility(
|
412 | mockStorage({
|
413 | [`${USER_SESSION_PREFIX}:${sessionId}`]: {
|
414 | isLoggedIn: "true",
|
415 | },
|
416 | }),
|
417 | mockStorage({
|
418 | [`${USER_SESSION_PREFIX}:${sessionId}`]: {
|
419 | clientId: "https://some.app/registration",
|
420 | idToken: "some.id.token",
|
421 | },
|
422 | })
|
423 | );
|
424 | const clientAuthn = getClientAuthentication({
|
425 | sessionInfoManager: mockSessionInfoManager(mockedStorage),
|
426 | });
|
427 |
|
428 | await expect(clientAuthn.validateCurrentSession()).resolves.toBeNull();
|
429 | });
|
430 |
|
431 | it("returns null if the current session has no stored ID token", async () => {
|
432 | const sessionId = "mySession";
|
433 | mockLocalStorage({
|
434 | [KEY_CURRENT_SESSION]: sessionId,
|
435 | });
|
436 |
|
437 | const mockedStorage = new StorageUtility(
|
438 | mockStorage({
|
439 | [`${USER_SESSION_PREFIX}:${sessionId}`]: {
|
440 | isLoggedIn: "true",
|
441 | issuer: "https://some.issuer",
|
442 | },
|
443 | }),
|
444 | mockStorage({
|
445 | [`${USER_SESSION_PREFIX}:${sessionId}`]: {
|
446 | clientId: "https://some.app/registration",
|
447 | },
|
448 | })
|
449 | );
|
450 | const clientAuthn = getClientAuthentication({
|
451 | sessionInfoManager: mockSessionInfoManager(mockedStorage),
|
452 | });
|
453 |
|
454 | await expect(clientAuthn.validateCurrentSession()).resolves.toBeNull();
|
455 | });
|
456 |
|
457 | it("returns null if the current session has no stored client ID", async () => {
|
458 | const sessionId = "mySession";
|
459 | mockLocalStorage({
|
460 | [KEY_CURRENT_SESSION]: sessionId,
|
461 | });
|
462 | const mockedStorage = new StorageUtility(
|
463 | mockStorage({
|
464 | [`${USER_SESSION_PREFIX}:${sessionId}`]: {
|
465 | isLoggedIn: "true",
|
466 | issuer: "https://some.issuer",
|
467 | },
|
468 | }),
|
469 | mockStorage({
|
470 | [`${USER_SESSION_PREFIX}:${sessionId}`]: {
|
471 | idToken: "some.id.token",
|
472 | },
|
473 | })
|
474 | );
|
475 | const clientAuthn = getClientAuthentication({
|
476 | sessionInfoManager: mockSessionInfoManager(mockedStorage),
|
477 | });
|
478 |
|
479 | await expect(clientAuthn.validateCurrentSession()).resolves.toBeNull();
|
480 | });
|
481 |
|
482 | it("returns null if the issuer does not have a JWKS", async () => {
|
483 | const sessionId = "mySession";
|
484 | mockLocalStorage({
|
485 | [KEY_CURRENT_SESSION]: sessionId,
|
486 | });
|
487 | const mockedIssuerConfig = mockIssuerConfigFetcher({} as IIssuerConfig);
|
488 |
|
489 | const mockedStorage = await mockSessionStorage(sessionId);
|
490 |
|
491 | const clientAuthn = getClientAuthentication({
|
492 | issuerConfigFetcher: mockedIssuerConfig,
|
493 | sessionInfoManager: mockSessionInfoManager(mockedStorage),
|
494 | });
|
495 |
|
496 | await expect(clientAuthn.validateCurrentSession()).resolves.toBeNull();
|
497 | });
|
498 |
|
499 | it("returns null if the issuer's JWKS isn't available", async () => {
|
500 | const sessionId = "mySession";
|
501 | mockLocalStorage({
|
502 | [KEY_CURRENT_SESSION]: sessionId,
|
503 | });
|
504 | const mockedIssuerConfig = mockIssuerConfigFetcher({
|
505 | jwksUri: "https://some.issuer/jwks",
|
506 | } as IIssuerConfig);
|
507 | const mockedStorage = await mockSessionStorage(sessionId);
|
508 | const coreModule = jest.requireMock(
|
509 | "@inrupt/solid-client-authn-core"
|
510 | ) as jest.Mocked<any>;
|
511 | coreModule.fetchJwks = jest.fn().mockRejectedValue("Not a valid JWK");
|
512 |
|
513 | const clientAuthn = getClientAuthentication({
|
514 | issuerConfigFetcher: mockedIssuerConfig,
|
515 | sessionInfoManager: mockSessionInfoManager(mockedStorage),
|
516 | });
|
517 |
|
518 | await expect(clientAuthn.validateCurrentSession()).resolves.toBeNull();
|
519 | });
|
520 |
|
521 | it("returns null if the current issuer doesn't match the ID token's", async () => {
|
522 | const sessionId = "mySession";
|
523 | mockLocalStorage({
|
524 | [KEY_CURRENT_SESSION]: sessionId,
|
525 | });
|
526 | const mockedIssuerConfig = mockIssuerConfigFetcher({
|
527 | jwksUri: "https://some.issuer/jwks",
|
528 | issuer: "https://some.issuer",
|
529 | } as IIssuerConfig);
|
530 | const mockedStorage = await mockSessionStorage(
|
531 | sessionId,
|
532 | mockIdTokenPayload(
|
533 | "https://my.pod/profile#me",
|
534 |
|
535 | "https://some-other.issuer",
|
536 | "https://some.app/registration"
|
537 | ),
|
538 | {
|
539 |
|
540 | issuer: "https://some.issuer",
|
541 | clientId: "https://some.app/registration",
|
542 | }
|
543 | );
|
544 | const coreModule = jest.requireMock(
|
545 | "@inrupt/solid-client-authn-core"
|
546 | ) as jest.Mocked<any>;
|
547 | coreModule.fetchJwks = jest.fn(() => Promise.resolve(mockJwk()));
|
548 |
|
549 | const clientAuthn = getClientAuthentication({
|
550 | issuerConfigFetcher: mockedIssuerConfig,
|
551 | sessionInfoManager: mockSessionInfoManager(mockedStorage),
|
552 | });
|
553 |
|
554 | await expect(clientAuthn.validateCurrentSession()).resolves.toBeNull();
|
555 | });
|
556 |
|
557 | it("returns null if the current client ID doesn't match the ID token audience", async () => {
|
558 | const sessionId = "mySession";
|
559 | mockLocalStorage({
|
560 | [KEY_CURRENT_SESSION]: sessionId,
|
561 | });
|
562 | const mockedIssuerConfig = mockIssuerConfigFetcher({
|
563 | jwksUri: "https://some.issuer/jwks",
|
564 | } as IIssuerConfig);
|
565 | const mockedStorage = await mockSessionStorage(
|
566 | sessionId,
|
567 | mockIdTokenPayload(
|
568 | "https://my.pod/profile#me",
|
569 | "https://some.issuer",
|
570 |
|
571 | "https://some-other.app/registration"
|
572 | ),
|
573 | {
|
574 | issuer: "https://some.issuer",
|
575 |
|
576 | clientId: "https://some.app/registration",
|
577 | }
|
578 | );
|
579 | const coreModule = jest.requireMock(
|
580 | "@inrupt/solid-client-authn-core"
|
581 | ) as jest.Mocked<any>;
|
582 | coreModule.fetchJwks = jest.fn(() => Promise.resolve(mockJwk()));
|
583 |
|
584 | const clientAuthn = getClientAuthentication({
|
585 | issuerConfigFetcher: mockedIssuerConfig,
|
586 | sessionInfoManager: mockSessionInfoManager(mockedStorage),
|
587 | });
|
588 |
|
589 | await expect(clientAuthn.validateCurrentSession()).resolves.toBeNull();
|
590 | });
|
591 |
|
592 | it("returns null if the ID token isn't signed with the keys of the issuer", async () => {
|
593 | const sessionId = "mySession";
|
594 | mockLocalStorage({
|
595 | [KEY_CURRENT_SESSION]: sessionId,
|
596 | });
|
597 | const mockedIssuerConfig = mockIssuerConfigFetcher({
|
598 | jwksUri: "https://some.issuer/jwks",
|
599 | } as IIssuerConfig);
|
600 | const mockedStorage = await mockSessionStorage(
|
601 | sessionId,
|
602 | mockIdTokenPayload(
|
603 | "https://my.pod/profile#me",
|
604 | "https://some.issuer",
|
605 | "https://some.app/registration"
|
606 | )
|
607 | );
|
608 |
|
609 | const coreModule = jest.requireMock(
|
610 | "@inrupt/solid-client-authn-core"
|
611 | ) as jest.Mocked<any>;
|
612 | coreModule.fetchJwks = jest.fn(() => Promise.resolve(mockAnotherJwk()));
|
613 |
|
614 | const clientAuthn = getClientAuthentication({
|
615 | issuerConfigFetcher: mockedIssuerConfig,
|
616 | sessionInfoManager: mockSessionInfoManager(mockedStorage),
|
617 | });
|
618 |
|
619 | await expect(clientAuthn.validateCurrentSession()).resolves.toBeNull();
|
620 | });
|
621 | });
|
622 |
|
623 | it("returns the issuer if the ID token is verified", async () => {
|
624 | const sessionId = "mySession";
|
625 | mockLocalStorage({
|
626 | [KEY_CURRENT_SESSION]: sessionId,
|
627 | });
|
628 | const mockedIssuerConfig = mockIssuerConfigFetcher({
|
629 | jwksUri: "https://some.issuer/jwks",
|
630 | } as IIssuerConfig);
|
631 | const mockedStorage = await mockSessionStorage(
|
632 | sessionId,
|
633 | mockIdTokenPayload(
|
634 | "https://my.pod/profile#me",
|
635 | "https://some.issuer",
|
636 | "https://some.app/registration"
|
637 | )
|
638 | );
|
639 | const coreModule = jest.requireMock(
|
640 | "@inrupt/solid-client-authn-core"
|
641 | ) as jest.Mocked<any>;
|
642 | coreModule.fetchJwks = jest.fn(() => Promise.resolve(mockJwk()));
|
643 |
|
644 | const clientAuthn = getClientAuthentication({
|
645 | issuerConfigFetcher: mockedIssuerConfig,
|
646 | sessionInfoManager: mockSessionInfoManager(mockedStorage),
|
647 | });
|
648 |
|
649 | await expect(clientAuthn.validateCurrentSession()).resolves.toStrictEqual(
|
650 | expect.objectContaining({
|
651 | issuer: "https://some.issuer",
|
652 | clientAppId: "https://some.app/registration",
|
653 | sessionId,
|
654 | idToken: expect.anything(),
|
655 | webId: "https://my.pod/profile#me",
|
656 | })
|
657 | );
|
658 | });
|
659 | });
|