@inrupt/solid-client-authn-browser 2.0.0 → 2.2.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/Session.d.ts +1 -1
- package/dist/index.js +26 -31
- package/dist/index.js.map +1 -1
- package/dist/index.mjs +28 -33
- package/dist/index.mjs.map +1 -1
- package/dist/solid-client-authn.bundle.js +6 -6
- package/dist/solid-client-authn.bundle.js.map +1 -1
- package/package.json +6 -6
- package/src/ClientAuthentication.spec.ts +1 -1
- package/src/ClientAuthentication.ts +5 -1
- package/src/Session.ts +1 -1
- package/src/login/oidc/ClientRegistrar.spec.ts +1 -0
- package/src/login/oidc/ClientRegistrar.ts +15 -8
- package/src/login/oidc/OidcLoginHandler.spec.ts +6 -3
- package/src/login/oidc/incomingRedirectHandler/AuthCodeRedirectHandler.spec.ts +19 -13
- package/src/login/oidc/incomingRedirectHandler/AuthCodeRedirectHandler.ts +12 -15
- package/src/login/oidc/oidcHandlers/AuthorizationCodeWithPkceOidcHandler.spec.ts +1 -0
- package/src/login/oidc/oidcHandlers/AuthorizationCodeWithPkceOidcHandler.ts +1 -2
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@inrupt/solid-client-authn-browser",
|
|
3
|
-
"version": "2.
|
|
3
|
+
"version": "2.2.0",
|
|
4
4
|
"license": "MIT",
|
|
5
5
|
"types": "dist/index.d.ts",
|
|
6
6
|
"browser": "dist/index.js",
|
|
@@ -25,12 +25,12 @@
|
|
|
25
25
|
"build-docs-preview-site": "npm run build-api-docs; cd docs/api; make html"
|
|
26
26
|
},
|
|
27
27
|
"devDependencies": {
|
|
28
|
-
"@types/node": "^20.
|
|
29
|
-
"@types/uuid": "^9.0.
|
|
28
|
+
"@types/node": "^20.12.7",
|
|
29
|
+
"@types/uuid": "^9.0.8"
|
|
30
30
|
},
|
|
31
31
|
"dependencies": {
|
|
32
|
-
"@inrupt/oidc-client-ext": "^2.
|
|
33
|
-
"@inrupt/solid-client-authn-core": "^2.
|
|
32
|
+
"@inrupt/oidc-client-ext": "^2.2.0",
|
|
33
|
+
"@inrupt/solid-client-authn-core": "^2.2.0",
|
|
34
34
|
"events": "^3.3.0",
|
|
35
35
|
"jose": "^5.1.3",
|
|
36
36
|
"uuid": "^9.0.1"
|
|
@@ -38,5 +38,5 @@
|
|
|
38
38
|
"publishConfig": {
|
|
39
39
|
"access": "public"
|
|
40
40
|
},
|
|
41
|
-
"gitHead": "
|
|
41
|
+
"gitHead": "8c3bd6871831c4fa14b593021bbb830cf68e332f"
|
|
42
42
|
}
|
|
@@ -53,7 +53,6 @@ jest.mock("@inrupt/solid-client-authn-core", () => {
|
|
|
53
53
|
) as typeof SolidClientAuthnCore;
|
|
54
54
|
return {
|
|
55
55
|
...actualCoreModule,
|
|
56
|
-
fetchJwks: jest.fn(),
|
|
57
56
|
};
|
|
58
57
|
});
|
|
59
58
|
|
|
@@ -425,6 +424,7 @@ describe("ClientAuthentication", () => {
|
|
|
425
424
|
expect(defaultMocks.redirectHandler.handle).toHaveBeenCalledWith(
|
|
426
425
|
url,
|
|
427
426
|
mockEmitter,
|
|
427
|
+
undefined,
|
|
428
428
|
);
|
|
429
429
|
|
|
430
430
|
// Calling the redirect handler should have updated the fetch.
|
|
@@ -101,7 +101,11 @@ export default class ClientAuthentication extends ClientAuthenticationBase {
|
|
|
101
101
|
eventEmitter: EventEmitter,
|
|
102
102
|
): Promise<ISessionInfo | undefined> => {
|
|
103
103
|
try {
|
|
104
|
-
const redirectInfo = await this.redirectHandler.handle(
|
|
104
|
+
const redirectInfo = await this.redirectHandler.handle(
|
|
105
|
+
url,
|
|
106
|
+
eventEmitter,
|
|
107
|
+
undefined,
|
|
108
|
+
);
|
|
105
109
|
// The `FallbackRedirectHandler` directly returns the global `fetch` for
|
|
106
110
|
// his value, so we should ensure it's bound to `window` rather than to
|
|
107
111
|
// ClientAuthentication, to avoid the following error:
|
package/src/Session.ts
CHANGED
|
@@ -115,7 +115,7 @@ function isLoggedIn(
|
|
|
115
115
|
}
|
|
116
116
|
|
|
117
117
|
/**
|
|
118
|
-
* A {@link Session} object represents a user's session on an application. The session holds state, as it stores information enabling
|
|
118
|
+
* A {@link Session} object represents a user's session on an application. The session holds state, as it stores information enabling access to private resources after login for instance.
|
|
119
119
|
*/
|
|
120
120
|
export class Session implements IHasSessionEventListener {
|
|
121
121
|
/**
|
|
@@ -31,6 +31,7 @@ import type {
|
|
|
31
31
|
IClient,
|
|
32
32
|
IClientRegistrarOptions,
|
|
33
33
|
} from "@inrupt/solid-client-authn-core";
|
|
34
|
+
import { isKnownClientType } from "@inrupt/solid-client-authn-core";
|
|
34
35
|
import { registerClient } from "@inrupt/oidc-client-ext";
|
|
35
36
|
|
|
36
37
|
/**
|
|
@@ -49,7 +50,8 @@ export default class ClientRegistrar implements IClientRegistrar {
|
|
|
49
50
|
const [
|
|
50
51
|
storedClientId,
|
|
51
52
|
storedClientSecret,
|
|
52
|
-
|
|
53
|
+
storedClientName,
|
|
54
|
+
storedClientType,
|
|
53
55
|
] = await Promise.all([
|
|
54
56
|
this.storageUtility.getForUser(options.sessionId, "clientId", {
|
|
55
57
|
secure: false,
|
|
@@ -57,17 +59,21 @@ export default class ClientRegistrar implements IClientRegistrar {
|
|
|
57
59
|
this.storageUtility.getForUser(options.sessionId, "clientSecret", {
|
|
58
60
|
secure: false,
|
|
59
61
|
}),
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
62
|
+
this.storageUtility.getForUser(options.sessionId, "clientName", {
|
|
63
|
+
secure: false,
|
|
64
|
+
}),
|
|
65
|
+
this.storageUtility.getForUser(options.sessionId, "clientType", {
|
|
66
|
+
secure: false,
|
|
67
|
+
}),
|
|
64
68
|
]);
|
|
65
|
-
if (storedClientId) {
|
|
69
|
+
if (storedClientId && isKnownClientType(storedClientType)) {
|
|
66
70
|
return {
|
|
67
71
|
clientId: storedClientId,
|
|
68
72
|
clientSecret: storedClientSecret,
|
|
69
|
-
|
|
70
|
-
|
|
73
|
+
clientName: storedClientName,
|
|
74
|
+
// Note: static clients are not applicable in a browser context.
|
|
75
|
+
clientType: storedClientType,
|
|
76
|
+
} as IClient;
|
|
71
77
|
}
|
|
72
78
|
|
|
73
79
|
try {
|
|
@@ -75,6 +81,7 @@ export default class ClientRegistrar implements IClientRegistrar {
|
|
|
75
81
|
// Save info
|
|
76
82
|
const infoToSave: Record<string, string> = {
|
|
77
83
|
clientId: registeredClient.clientId,
|
|
84
|
+
clientType: "dynamic",
|
|
78
85
|
};
|
|
79
86
|
if (registeredClient.clientSecret) {
|
|
80
87
|
infoToSave.clientSecret = registeredClient.clientSecret;
|
|
@@ -77,9 +77,10 @@ describe("OidcLoginHandler", () => {
|
|
|
77
77
|
mockStorage({}),
|
|
78
78
|
mockStorage({
|
|
79
79
|
"solidClientAuthenticationUser:mySession": {
|
|
80
|
-
// The value of the client ID doesn't matter
|
|
80
|
+
// The value of the client ID doesn't matter.
|
|
81
81
|
// This checks it gets passed through from storage to the handler.
|
|
82
|
-
clientId: "some
|
|
82
|
+
clientId: "https://example.org/some-client-id",
|
|
83
|
+
clientType: "solid-oidc",
|
|
83
84
|
},
|
|
84
85
|
}),
|
|
85
86
|
);
|
|
@@ -99,7 +100,9 @@ describe("OidcLoginHandler", () => {
|
|
|
99
100
|
expect(actualHandler.handle.mock.calls).toHaveLength(1);
|
|
100
101
|
|
|
101
102
|
const calledWith = actualHandler.handle.mock.calls[0][0];
|
|
102
|
-
expect(calledWith.client.clientId).toBe(
|
|
103
|
+
expect(calledWith.client.clientId).toBe(
|
|
104
|
+
"https://example.org/some-client-id",
|
|
105
|
+
);
|
|
103
106
|
});
|
|
104
107
|
|
|
105
108
|
it("should lookup client ID if not provided, if not found do DCR", async () => {
|
|
@@ -33,7 +33,6 @@ import {
|
|
|
33
33
|
mockStorageUtility,
|
|
34
34
|
} from "@inrupt/solid-client-authn-core/mocks";
|
|
35
35
|
import { jest, it, describe, expect } from "@jest/globals";
|
|
36
|
-
import { getBearerToken } from "@inrupt/oidc-client-ext";
|
|
37
36
|
import type * as OidcClientExt from "@inrupt/oidc-client-ext";
|
|
38
37
|
import type { JWK } from "jose";
|
|
39
38
|
import { importJWK } from "jose";
|
|
@@ -150,16 +149,22 @@ jest.mock("@inrupt/oidc-client-ext");
|
|
|
150
149
|
|
|
151
150
|
jest.useFakeTimers();
|
|
152
151
|
|
|
153
|
-
function mockOidcClient(
|
|
152
|
+
function mockOidcClient(
|
|
153
|
+
tokenType: "dpop" | "bearer" = "dpop",
|
|
154
|
+
): typeof OidcClientExt {
|
|
154
155
|
const mockedOidcClient = jest.requireMock<typeof OidcClientExt>(
|
|
155
156
|
"@inrupt/oidc-client-ext",
|
|
156
157
|
);
|
|
157
|
-
mockedOidcClient.
|
|
158
|
-
.fn<typeof mockedOidcClient.
|
|
159
|
-
.mockImplementationOnce(
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
|
|
158
|
+
mockedOidcClient.getTokens = jest
|
|
159
|
+
.fn<typeof mockedOidcClient.getTokens>()
|
|
160
|
+
.mockImplementationOnce(
|
|
161
|
+
tokenType === "dpop"
|
|
162
|
+
? mockTokenEndpointDpopResponse
|
|
163
|
+
: () =>
|
|
164
|
+
new Promise<OidcClientExt.CodeExchangeResult>((resolve) => {
|
|
165
|
+
resolve(mockTokenEndpointBearerResponse());
|
|
166
|
+
}),
|
|
167
|
+
);
|
|
163
168
|
mockedOidcClient.refresh = jest
|
|
164
169
|
.fn<typeof mockedOidcClient.refresh>()
|
|
165
170
|
.mockResolvedValueOnce({
|
|
@@ -318,13 +323,14 @@ describe("AuthCodeRedirectHandler", () => {
|
|
|
318
323
|
|
|
319
324
|
await authCodeRedirectHandler.handle(mockRedirectUrl());
|
|
320
325
|
|
|
321
|
-
expect(mockedOidcClient.
|
|
326
|
+
expect(mockedOidcClient.getTokens).toHaveBeenCalledWith(
|
|
322
327
|
expect.anything(),
|
|
323
328
|
expect.anything(),
|
|
324
329
|
expect.objectContaining({
|
|
325
330
|
codeVerifier: DEFAULT_CODE_VERIFIER,
|
|
326
331
|
redirectUrl: DEFAULT_REDIRECT_URL,
|
|
327
332
|
}),
|
|
333
|
+
expect.anything(),
|
|
328
334
|
);
|
|
329
335
|
});
|
|
330
336
|
|
|
@@ -402,7 +408,7 @@ describe("AuthCodeRedirectHandler", () => {
|
|
|
402
408
|
});
|
|
403
409
|
|
|
404
410
|
it("returns an authenticated bearer fetch if requested", async () => {
|
|
405
|
-
mockOidcClient();
|
|
411
|
+
mockOidcClient("bearer");
|
|
406
412
|
mockedFetch.mockResolvedValueOnce(
|
|
407
413
|
new Response("", {
|
|
408
414
|
status: 200,
|
|
@@ -517,10 +523,10 @@ describe("AuthCodeRedirectHandler", () => {
|
|
|
517
523
|
});
|
|
518
524
|
|
|
519
525
|
it("returns null for the expiration time if none was provided", async () => {
|
|
520
|
-
const mockedOidcClient = mockOidcClient();
|
|
526
|
+
const mockedOidcClient = mockOidcClient("bearer");
|
|
521
527
|
mockFetch(new Response());
|
|
522
|
-
mockedOidcClient.
|
|
523
|
-
.fn(
|
|
528
|
+
mockedOidcClient.getTokens = jest
|
|
529
|
+
.fn<(typeof OidcClientExt)["getTokens"]>()
|
|
524
530
|
.mockResolvedValueOnce({
|
|
525
531
|
accessToken: mockAccessTokenBearer(),
|
|
526
532
|
idToken: mockIdToken(),
|
|
@@ -39,8 +39,7 @@ import {
|
|
|
39
39
|
loadOidcContextFromStorage,
|
|
40
40
|
maybeBuildRpInitiatedLogout,
|
|
41
41
|
} from "@inrupt/solid-client-authn-core";
|
|
42
|
-
import
|
|
43
|
-
import { getDpopToken, getBearerToken } from "@inrupt/oidc-client-ext";
|
|
42
|
+
import { getTokens } from "@inrupt/oidc-client-ext";
|
|
44
43
|
import type { EventEmitter } from "events";
|
|
45
44
|
|
|
46
45
|
/**
|
|
@@ -132,26 +131,24 @@ export class AuthCodeRedirectHandler implements IIncomingRedirectHandler {
|
|
|
132
131
|
issuerConfig,
|
|
133
132
|
);
|
|
134
133
|
|
|
135
|
-
let tokens: CodeExchangeResult;
|
|
136
134
|
const tokenCreatedAt = Date.now();
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
135
|
+
const tokens = await getTokens(
|
|
136
|
+
issuerConfig,
|
|
137
|
+
client,
|
|
138
|
+
{
|
|
140
139
|
grantType: "authorization_code",
|
|
141
140
|
// We rely on our 'canHandle' function checking that the OAuth 'code'
|
|
142
141
|
// parameter is present in our query string.
|
|
143
142
|
code: url.searchParams.get("code") as string,
|
|
144
143
|
codeVerifier,
|
|
145
144
|
redirectUrl: storedRedirectIri,
|
|
146
|
-
}
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
}
|
|
153
|
-
tokens = await getBearerToken(url.toString());
|
|
154
|
-
}
|
|
145
|
+
},
|
|
146
|
+
isDpop,
|
|
147
|
+
);
|
|
148
|
+
|
|
149
|
+
// Delete oidc-client-specific session information from storage. oidc-client
|
|
150
|
+
// is no longer used for the token exchange, so it doesn't perform this automatically.
|
|
151
|
+
window.localStorage.removeItem(`oidc.${oauthState}`);
|
|
155
152
|
|
|
156
153
|
let refreshOptions: RefreshOptions | undefined;
|
|
157
154
|
if (tokens.refreshToken !== undefined) {
|
|
@@ -262,6 +262,7 @@ describe("AuthorizationCodeWithPkceOidcHandler", () => {
|
|
|
262
262
|
...standardOidcOptions,
|
|
263
263
|
client: {
|
|
264
264
|
...standardOidcOptions.client,
|
|
265
|
+
clientType: "dynamic",
|
|
265
266
|
clientSecret: "I can't cook because I only drink Soylent",
|
|
266
267
|
},
|
|
267
268
|
issuerConfiguration: {
|
|
@@ -53,8 +53,7 @@ export default class AuthorizationCodeWithPkceOidcHandler
|
|
|
53
53
|
authority: oidcLoginOptions.issuer.toString(),
|
|
54
54
|
client_id: oidcLoginOptions.client.clientId,
|
|
55
55
|
client_secret: oidcLoginOptions.client.clientSecret,
|
|
56
|
-
redirect_uri: oidcLoginOptions.redirectUrl
|
|
57
|
-
post_logout_redirect_uri: oidcLoginOptions.redirectUrl.toString(),
|
|
56
|
+
redirect_uri: oidcLoginOptions.redirectUrl,
|
|
58
57
|
response_type: "code",
|
|
59
58
|
scope: DEFAULT_SCOPES,
|
|
60
59
|
filterProtocolClaims: true,
|