@inrupt/solid-client-authn-browser 2.2.7 → 2.4.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/LICENSE +2 -2
- package/dist/index.js +274 -238
- package/dist/index.js.map +1 -1
- package/dist/index.mjs +274 -238
- package/dist/index.mjs.map +1 -1
- package/dist/solid-client-authn.bundle.js +7 -7
- package/dist/solid-client-authn.bundle.js.map +1 -1
- package/package.json +6 -6
- package/src/ClientAuthentication.spec.ts +29 -0
- package/src/ClientAuthentication.ts +5 -6
- package/src/Session.spec.ts +1 -1
- package/src/login/oidc/ClientRegistrar.spec.ts +154 -78
- package/src/login/oidc/ClientRegistrar.ts +33 -10
- package/src/login/oidc/Redirector.spec.ts +2 -2
- package/src/login/oidc/__mocks__/ClientRegistrar.ts +2 -0
- package/src/login/oidc/incomingRedirectHandler/AuthCodeRedirectHandler.ts +1 -1
- package/src/login/oidc/oidcHandlers/AuthorizationCodeWithPkceOidcHandler.spec.ts +1 -0
- package/tsconfig.json +1 -1
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@inrupt/solid-client-authn-browser",
|
|
3
|
-
"version": "2.
|
|
3
|
+
"version": "2.4.0",
|
|
4
4
|
"license": "MIT",
|
|
5
5
|
"types": "dist/index.d.ts",
|
|
6
6
|
"browser": "dist/index.js",
|
|
@@ -26,18 +26,18 @@
|
|
|
26
26
|
"build-docs-preview-site": "npm run build-api-docs; cd docs/api; make html"
|
|
27
27
|
},
|
|
28
28
|
"devDependencies": {
|
|
29
|
-
"@types/node": "^22.
|
|
29
|
+
"@types/node": "^22.14.1",
|
|
30
30
|
"@types/uuid": "^10.0.0"
|
|
31
31
|
},
|
|
32
32
|
"dependencies": {
|
|
33
|
-
"@inrupt/oidc-client-ext": "^2.
|
|
34
|
-
"@inrupt/solid-client-authn-core": "^2.
|
|
33
|
+
"@inrupt/oidc-client-ext": "^2.4.0",
|
|
34
|
+
"@inrupt/solid-client-authn-core": "^2.4.0",
|
|
35
35
|
"events": "^3.3.0",
|
|
36
36
|
"jose": "^5.1.3",
|
|
37
|
-
"uuid": "^
|
|
37
|
+
"uuid": "^11.1.0"
|
|
38
38
|
},
|
|
39
39
|
"publishConfig": {
|
|
40
40
|
"access": "public"
|
|
41
41
|
},
|
|
42
|
-
"gitHead": "
|
|
42
|
+
"gitHead": "1bfff9da8474e38a1d87216aa6aff1d1c7065d1b"
|
|
43
43
|
}
|
|
@@ -230,6 +230,35 @@ describe("ClientAuthentication", () => {
|
|
|
230
230
|
).resolves.toBeUndefined();
|
|
231
231
|
});
|
|
232
232
|
|
|
233
|
+
it("should not clear the local storage when logging in with prompt set to none", async () => {
|
|
234
|
+
const nonEmptyStorage = mockStorageUtility({
|
|
235
|
+
someUser: { someKey: "someValue" },
|
|
236
|
+
});
|
|
237
|
+
await nonEmptyStorage.setForUser(
|
|
238
|
+
"someUser",
|
|
239
|
+
{ someKey: "someValue" },
|
|
240
|
+
{ secure: false },
|
|
241
|
+
);
|
|
242
|
+
const clientAuthn = getClientAuthentication({
|
|
243
|
+
sessionInfoManager: mockSessionInfoManager(nonEmptyStorage),
|
|
244
|
+
});
|
|
245
|
+
await clientAuthn.login(
|
|
246
|
+
{
|
|
247
|
+
sessionId: "someUser",
|
|
248
|
+
tokenType: "DPoP",
|
|
249
|
+
clientId: "coolApp",
|
|
250
|
+
clientName: "coolApp Name",
|
|
251
|
+
redirectUrl: "https://coolapp.com/redirect",
|
|
252
|
+
oidcIssuer: "https://idp.com",
|
|
253
|
+
prompt: "none",
|
|
254
|
+
},
|
|
255
|
+
mockEmitter,
|
|
256
|
+
);
|
|
257
|
+
await expect(
|
|
258
|
+
nonEmptyStorage.getForUser("someUser", "someKey", { secure: false }),
|
|
259
|
+
).resolves.toBe("someValue");
|
|
260
|
+
});
|
|
261
|
+
|
|
233
262
|
it("throws if the redirect IRI is a malformed URL", async () => {
|
|
234
263
|
const clientAuthn = getClientAuthentication();
|
|
235
264
|
await expect(() =>
|
|
@@ -49,12 +49,11 @@ export default class ClientAuthentication extends ClientAuthenticationBase {
|
|
|
49
49
|
eventEmitter: EventEmitter,
|
|
50
50
|
): Promise<void> => {
|
|
51
51
|
// In order to get a clean start, make sure that the session is logged out
|
|
52
|
-
// on login
|
|
53
|
-
//
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
await this.sessionInfoManager.clear(options.sessionId);
|
|
52
|
+
// on login, except when doing a silent login so that Dynamic Client information
|
|
53
|
+
// is preserved.
|
|
54
|
+
if (options.prompt !== "none") {
|
|
55
|
+
await this.sessionInfoManager.clear(options.sessionId);
|
|
56
|
+
}
|
|
58
57
|
|
|
59
58
|
// In the case of the user hitting the 'back' button in their browser, they
|
|
60
59
|
// could return to a previous redirect URL that contains OIDC params that
|
package/src/Session.spec.ts
CHANGED
|
@@ -63,7 +63,7 @@ const mockLocation = (mockedLocation: string) => {
|
|
|
63
63
|
delete (window as any).history.replaceState;
|
|
64
64
|
|
|
65
65
|
// Set our window's location to our test value.
|
|
66
|
-
window.location = {
|
|
66
|
+
(window as any).location = {
|
|
67
67
|
href: mockedLocation,
|
|
68
68
|
} as Location;
|
|
69
69
|
window.history.replaceState = jest.fn();
|
|
@@ -25,9 +25,17 @@ import {
|
|
|
25
25
|
} from "@inrupt/solid-client-authn-core/mocks";
|
|
26
26
|
import { jest, it, describe, expect } from "@jest/globals";
|
|
27
27
|
import { randomUUID } from "crypto";
|
|
28
|
+
import type * as OidcClientExt from "@inrupt/oidc-client-ext";
|
|
29
|
+
import type { IOpenIdDynamicClient } from "core";
|
|
28
30
|
import ClientRegistrar from "./ClientRegistrar";
|
|
29
31
|
import { IssuerConfigFetcherFetchConfigResponse } from "./__mocks__/IssuerConfigFetcher";
|
|
30
32
|
|
|
33
|
+
jest.mock("@inrupt/oidc-client-ext", () => {
|
|
34
|
+
return {
|
|
35
|
+
registerClient: jest.fn(),
|
|
36
|
+
};
|
|
37
|
+
});
|
|
38
|
+
|
|
31
39
|
/**
|
|
32
40
|
* Test for ClientRegistrar
|
|
33
41
|
*/
|
|
@@ -42,24 +50,19 @@ describe("ClientRegistrar", () => {
|
|
|
42
50
|
}
|
|
43
51
|
|
|
44
52
|
describe("getClient", () => {
|
|
45
|
-
it("
|
|
46
|
-
|
|
47
|
-
const
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
}),
|
|
59
|
-
),
|
|
60
|
-
/* eslint-enable camelcase */
|
|
61
|
-
);
|
|
62
|
-
global.fetch = mockFetch;
|
|
53
|
+
it("performs dynamic registration if no client is in storage", async () => {
|
|
54
|
+
// Setup registration mock.
|
|
55
|
+
const client: IOpenIdDynamicClient = {
|
|
56
|
+
clientId: randomUUID(),
|
|
57
|
+
clientSecret: randomUUID(),
|
|
58
|
+
expiresAt: Date.now() + 1000,
|
|
59
|
+
clientType: "dynamic",
|
|
60
|
+
};
|
|
61
|
+
const { registerClient: mockedRegisterClient } = jest.requireMock(
|
|
62
|
+
"@inrupt/oidc-client-ext",
|
|
63
|
+
) as jest.Mocked<typeof OidcClientExt>;
|
|
64
|
+
mockedRegisterClient.mockResolvedValueOnce(client);
|
|
65
|
+
|
|
63
66
|
const clientRegistrar = getClientRegistrar({
|
|
64
67
|
storage: mockStorageUtility({}),
|
|
65
68
|
});
|
|
@@ -75,40 +78,20 @@ describe("ClientRegistrar", () => {
|
|
|
75
78
|
registrationEndpoint: registrationUrl,
|
|
76
79
|
},
|
|
77
80
|
),
|
|
78
|
-
).toMatchObject(
|
|
79
|
-
clientId,
|
|
80
|
-
clientSecret,
|
|
81
|
-
});
|
|
82
|
-
expect(mockFetch).toHaveBeenCalledWith(registrationUrl.toString(), {
|
|
83
|
-
method: "POST",
|
|
84
|
-
headers: {
|
|
85
|
-
"Content-Type": "application/json",
|
|
86
|
-
},
|
|
87
|
-
body: JSON.stringify({
|
|
88
|
-
/* eslint-disable camelcase */
|
|
89
|
-
application_type: "web",
|
|
90
|
-
redirect_uris: ["https://example.com"],
|
|
91
|
-
subject_type: "public",
|
|
92
|
-
token_endpoint_auth_method: "client_secret_basic",
|
|
93
|
-
id_token_signed_response_alg: "RS256",
|
|
94
|
-
grant_types: ["authorization_code", "refresh_token"],
|
|
95
|
-
/* eslint-enable camelcase */
|
|
96
|
-
}),
|
|
97
|
-
});
|
|
81
|
+
).toMatchObject(client);
|
|
98
82
|
});
|
|
99
83
|
|
|
100
84
|
it("can register a public client without secret", async () => {
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
global.fetch = mockFetch;
|
|
85
|
+
// Setup registration mock.
|
|
86
|
+
const client: IOpenIdDynamicClient = {
|
|
87
|
+
clientId: randomUUID(),
|
|
88
|
+
clientType: "dynamic",
|
|
89
|
+
};
|
|
90
|
+
const { registerClient: mockedRegisterClient } = jest.requireMock(
|
|
91
|
+
"@inrupt/oidc-client-ext",
|
|
92
|
+
) as jest.Mocked<typeof OidcClientExt>;
|
|
93
|
+
mockedRegisterClient.mockResolvedValueOnce(client);
|
|
94
|
+
|
|
112
95
|
const clientRegistrar = getClientRegistrar({
|
|
113
96
|
storage: mockStorageUtility({}),
|
|
114
97
|
});
|
|
@@ -138,20 +121,18 @@ describe("ClientRegistrar", () => {
|
|
|
138
121
|
},
|
|
139
122
|
IssuerConfigFetcherFetchConfigResponse,
|
|
140
123
|
),
|
|
141
|
-
).rejects.toThrow(
|
|
142
|
-
"Dynamic Registration could not be completed because the issuer has no registration endpoint.",
|
|
143
|
-
);
|
|
124
|
+
).rejects.toThrow();
|
|
144
125
|
});
|
|
145
126
|
|
|
146
127
|
it("handles a failure to dynamically register elegantly", async () => {
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
|
|
128
|
+
// Setup registration mock.
|
|
129
|
+
const { registerClient: mockedRegisterClient } = jest.requireMock(
|
|
130
|
+
"@inrupt/oidc-client-ext",
|
|
131
|
+
) as jest.Mocked<typeof OidcClientExt>;
|
|
132
|
+
mockedRegisterClient.mockRejectedValueOnce(
|
|
133
|
+
new Error("Dynamic client registration failed"),
|
|
153
134
|
);
|
|
154
|
-
|
|
135
|
+
|
|
155
136
|
const clientRegistrar = getClientRegistrar({
|
|
156
137
|
storage: mockStorageUtility({}),
|
|
157
138
|
});
|
|
@@ -167,20 +148,20 @@ describe("ClientRegistrar", () => {
|
|
|
167
148
|
registrationEndpoint: registrationUrl,
|
|
168
149
|
},
|
|
169
150
|
),
|
|
170
|
-
).rejects.toThrow(
|
|
171
|
-
"Client registration failed: [Error: Dynamic client registration failed: bad stuff that's an error - ]",
|
|
172
|
-
);
|
|
151
|
+
).rejects.toThrow("Client registration failed");
|
|
173
152
|
});
|
|
174
153
|
|
|
175
154
|
it("retrieves client id and secret from storage if they are present", async () => {
|
|
176
155
|
const clientId = randomUUID();
|
|
177
156
|
const clientSecret = randomUUID();
|
|
157
|
+
const expiresAt = Math.ceil((Date.now() + 100_000) / 1000);
|
|
178
158
|
const clientRegistrar = getClientRegistrar({
|
|
179
159
|
storage: mockStorageUtility(
|
|
180
160
|
{
|
|
181
161
|
"solidClientAuthenticationUser:mySession": {
|
|
182
162
|
clientId,
|
|
183
163
|
clientSecret,
|
|
164
|
+
expiresAt: String(expiresAt),
|
|
184
165
|
clientType: "dynamic",
|
|
185
166
|
},
|
|
186
167
|
},
|
|
@@ -198,23 +179,115 @@ describe("ClientRegistrar", () => {
|
|
|
198
179
|
);
|
|
199
180
|
expect(client.clientId).toBe(clientId);
|
|
200
181
|
expect(client.clientSecret).toBe(clientSecret);
|
|
182
|
+
expect(client.clientType).toBe("dynamic");
|
|
183
|
+
expect((client as IOpenIdDynamicClient).expiresAt).toBe(expiresAt);
|
|
201
184
|
});
|
|
202
185
|
|
|
203
|
-
it("
|
|
204
|
-
|
|
205
|
-
const
|
|
206
|
-
|
|
207
|
-
|
|
208
|
-
|
|
209
|
-
|
|
210
|
-
|
|
211
|
-
|
|
212
|
-
|
|
213
|
-
|
|
186
|
+
it("performs dynamic registration if an expired client is present in storage", async () => {
|
|
187
|
+
// Setup the registration mock.
|
|
188
|
+
const renewedClient: IOpenIdDynamicClient = {
|
|
189
|
+
clientId: randomUUID(),
|
|
190
|
+
clientSecret: randomUUID(),
|
|
191
|
+
// Date.now is in ms, and the expiration date is in seconds.
|
|
192
|
+
expiresAt: Math.ceil((Date.now() + 100_000) / 1000),
|
|
193
|
+
clientType: "dynamic",
|
|
194
|
+
};
|
|
195
|
+
const { registerClient: mockedRegisterClient } = jest.requireMock(
|
|
196
|
+
"@inrupt/oidc-client-ext",
|
|
197
|
+
) as jest.Mocked<typeof OidcClientExt>;
|
|
198
|
+
mockedRegisterClient.mockResolvedValueOnce(renewedClient);
|
|
199
|
+
const expiredClient: IOpenIdDynamicClient = {
|
|
200
|
+
clientId: randomUUID(),
|
|
201
|
+
clientSecret: randomUUID(),
|
|
202
|
+
// Date.now is in ms, and the expiration date is in seconds.
|
|
203
|
+
expiresAt: Math.ceil((Date.now() - 100_000) / 1000),
|
|
204
|
+
clientType: "dynamic",
|
|
205
|
+
};
|
|
206
|
+
|
|
207
|
+
// Setup the expired client in storage mock.
|
|
208
|
+
const clientRegistrar = getClientRegistrar({
|
|
209
|
+
storage: mockStorageUtility(
|
|
210
|
+
{
|
|
211
|
+
"solidClientAuthenticationUser:mySession": {
|
|
212
|
+
clientId: expiredClient.clientId,
|
|
213
|
+
clientSecret: expiredClient.clientSecret,
|
|
214
|
+
expiresAt: String(expiredClient.expiresAt),
|
|
215
|
+
clientType: expiredClient.clientType,
|
|
216
|
+
},
|
|
217
|
+
},
|
|
218
|
+
false,
|
|
214
219
|
),
|
|
215
|
-
|
|
216
|
-
|
|
217
|
-
|
|
220
|
+
});
|
|
221
|
+
const registrationUrl = "https://idp.com/register";
|
|
222
|
+
expect(
|
|
223
|
+
await clientRegistrar.getClient(
|
|
224
|
+
{
|
|
225
|
+
sessionId: "mySession",
|
|
226
|
+
redirectUrl: "https://example.com",
|
|
227
|
+
},
|
|
228
|
+
{
|
|
229
|
+
...IssuerConfigFetcherFetchConfigResponse,
|
|
230
|
+
registrationEndpoint: registrationUrl,
|
|
231
|
+
},
|
|
232
|
+
),
|
|
233
|
+
).toMatchObject(renewedClient);
|
|
234
|
+
});
|
|
235
|
+
|
|
236
|
+
it("performs dynamic registration if a confidential client is missing an expiration date (legacy case)", async () => {
|
|
237
|
+
// Setup the registration mock.
|
|
238
|
+
const renewedClient: IOpenIdDynamicClient = {
|
|
239
|
+
clientId: randomUUID(),
|
|
240
|
+
clientSecret: randomUUID(),
|
|
241
|
+
expiresAt: Date.now() + 1000,
|
|
242
|
+
clientType: "dynamic",
|
|
243
|
+
};
|
|
244
|
+
const { registerClient: mockedRegisterClient } = jest.requireMock(
|
|
245
|
+
"@inrupt/oidc-client-ext",
|
|
246
|
+
) as jest.Mocked<typeof OidcClientExt>;
|
|
247
|
+
mockedRegisterClient.mockResolvedValueOnce(renewedClient);
|
|
248
|
+
|
|
249
|
+
// Setup the legacy client in storage mock.
|
|
250
|
+
const clientRegistrar = getClientRegistrar({
|
|
251
|
+
storage: mockStorageUtility(
|
|
252
|
+
{
|
|
253
|
+
"solidClientAuthenticationUser:mySession": {
|
|
254
|
+
clientId: randomUUID(),
|
|
255
|
+
clientSecret: randomUUID(),
|
|
256
|
+
clientType: "dynamic",
|
|
257
|
+
// The expiration date is missing.
|
|
258
|
+
},
|
|
259
|
+
},
|
|
260
|
+
false,
|
|
261
|
+
),
|
|
262
|
+
});
|
|
263
|
+
const registrationUrl = "https://idp.com/register";
|
|
264
|
+
expect(
|
|
265
|
+
await clientRegistrar.getClient(
|
|
266
|
+
{
|
|
267
|
+
sessionId: "mySession",
|
|
268
|
+
redirectUrl: "https://example.com",
|
|
269
|
+
},
|
|
270
|
+
{
|
|
271
|
+
...IssuerConfigFetcherFetchConfigResponse,
|
|
272
|
+
registrationEndpoint: registrationUrl,
|
|
273
|
+
},
|
|
274
|
+
),
|
|
275
|
+
).toMatchObject(renewedClient);
|
|
276
|
+
});
|
|
277
|
+
|
|
278
|
+
it("saves dynamic registration information", async () => {
|
|
279
|
+
// Setup registration mock.
|
|
280
|
+
const client: IOpenIdDynamicClient = {
|
|
281
|
+
clientId: randomUUID(),
|
|
282
|
+
clientSecret: randomUUID(),
|
|
283
|
+
expiresAt: Math.ceil((Date.now() + 10_000) / 1000),
|
|
284
|
+
clientType: "dynamic",
|
|
285
|
+
};
|
|
286
|
+
const { registerClient: mockedRegisterClient } = jest.requireMock(
|
|
287
|
+
"@inrupt/oidc-client-ext",
|
|
288
|
+
) as jest.Mocked<typeof OidcClientExt>;
|
|
289
|
+
mockedRegisterClient.mockResolvedValueOnce(client);
|
|
290
|
+
|
|
218
291
|
const myStorage = mockStorageUtility({});
|
|
219
292
|
const clientRegistrar = getClientRegistrar({
|
|
220
293
|
storage: myStorage,
|
|
@@ -234,10 +307,13 @@ describe("ClientRegistrar", () => {
|
|
|
234
307
|
|
|
235
308
|
await expect(
|
|
236
309
|
myStorage.getForUser("mySession", "clientId", { secure: false }),
|
|
237
|
-
).resolves.toBe(clientId);
|
|
310
|
+
).resolves.toBe(client.clientId);
|
|
238
311
|
await expect(
|
|
239
312
|
myStorage.getForUser("mySession", "clientSecret", { secure: false }),
|
|
240
|
-
).resolves.toBe(clientSecret);
|
|
313
|
+
).resolves.toBe(client.clientSecret);
|
|
314
|
+
await expect(
|
|
315
|
+
myStorage.getForUser("mySession", "expiresAt", { secure: false }),
|
|
316
|
+
).resolves.toBe(String(client.expiresAt));
|
|
241
317
|
});
|
|
242
318
|
});
|
|
243
319
|
});
|
|
@@ -50,6 +50,7 @@ export default class ClientRegistrar implements IClientRegistrar {
|
|
|
50
50
|
const [
|
|
51
51
|
storedClientId,
|
|
52
52
|
storedClientSecret,
|
|
53
|
+
expiresAt,
|
|
53
54
|
storedClientName,
|
|
54
55
|
storedClientType,
|
|
55
56
|
] = await Promise.all([
|
|
@@ -59,6 +60,9 @@ export default class ClientRegistrar implements IClientRegistrar {
|
|
|
59
60
|
this.storageUtility.getForUser(options.sessionId, "clientSecret", {
|
|
60
61
|
secure: false,
|
|
61
62
|
}),
|
|
63
|
+
this.storageUtility.getForUser(options.sessionId, "expiresAt", {
|
|
64
|
+
secure: false,
|
|
65
|
+
}),
|
|
62
66
|
this.storageUtility.getForUser(options.sessionId, "clientName", {
|
|
63
67
|
secure: false,
|
|
64
68
|
}),
|
|
@@ -66,14 +70,32 @@ export default class ClientRegistrar implements IClientRegistrar {
|
|
|
66
70
|
secure: false,
|
|
67
71
|
}),
|
|
68
72
|
]);
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
73
|
+
// -1 is used as a default to identify legacy cases when a value should
|
|
74
|
+
// have been stored, but wasn't. It will be treated as an expired client.
|
|
75
|
+
const expirationDate =
|
|
76
|
+
expiresAt !== undefined ? Number.parseInt(expiresAt, 10) : -1;
|
|
77
|
+
// Expiration is only applicable to confidential clients.
|
|
78
|
+
// Note that Date.now() is in milliseconds, and expirationDate in seconds.
|
|
79
|
+
const expired =
|
|
80
|
+
storedClientSecret !== undefined &&
|
|
81
|
+
Math.floor(Date.now() / 1000) > expirationDate;
|
|
82
|
+
if (storedClientId && isKnownClientType(storedClientType) && !expired) {
|
|
83
|
+
return storedClientSecret !== undefined
|
|
84
|
+
? {
|
|
85
|
+
clientId: storedClientId,
|
|
86
|
+
clientSecret: storedClientSecret,
|
|
87
|
+
clientName: storedClientName,
|
|
88
|
+
// Note: static clients are not applicable in a browser context.
|
|
89
|
+
clientType: "dynamic",
|
|
90
|
+
expiresAt: expirationDate,
|
|
91
|
+
}
|
|
92
|
+
: ({
|
|
93
|
+
clientId: storedClientId,
|
|
94
|
+
clientName: storedClientName,
|
|
95
|
+
// Note: static clients are not applicable in a browser context.
|
|
96
|
+
clientType: storedClientType,
|
|
97
|
+
// The type assertion is required even though the type should match the declaration.
|
|
98
|
+
} as IClient);
|
|
77
99
|
}
|
|
78
100
|
|
|
79
101
|
try {
|
|
@@ -83,8 +105,9 @@ export default class ClientRegistrar implements IClientRegistrar {
|
|
|
83
105
|
clientId: registeredClient.clientId,
|
|
84
106
|
clientType: "dynamic",
|
|
85
107
|
};
|
|
86
|
-
if (registeredClient.clientSecret) {
|
|
108
|
+
if (registeredClient.clientSecret !== undefined) {
|
|
87
109
|
infoToSave.clientSecret = registeredClient.clientSecret;
|
|
110
|
+
infoToSave.expiresAt = String(registeredClient.expiresAt);
|
|
88
111
|
}
|
|
89
112
|
if (registeredClient.idTokenSignedResponseAlg) {
|
|
90
113
|
infoToSave.idTokenSignedResponseAlg =
|
|
@@ -98,7 +121,7 @@ export default class ClientRegistrar implements IClientRegistrar {
|
|
|
98
121
|
});
|
|
99
122
|
return registeredClient;
|
|
100
123
|
} catch (error) {
|
|
101
|
-
throw new Error(`Client registration failed:
|
|
124
|
+
throw new Error(`Client registration failed.`, { cause: error });
|
|
102
125
|
}
|
|
103
126
|
}
|
|
104
127
|
}
|
|
@@ -49,14 +49,14 @@ describe("Redirector", () => {
|
|
|
49
49
|
// eslint-disable-next-line @typescript-eslint/ban-ts-comment
|
|
50
50
|
// @ts-ignore
|
|
51
51
|
delete window.history.replaceState;
|
|
52
|
-
window.location = {
|
|
52
|
+
(window as any).location = {
|
|
53
53
|
href: "https://coolSite.com",
|
|
54
54
|
} as Location;
|
|
55
55
|
window.history.replaceState = jest.fn();
|
|
56
56
|
});
|
|
57
57
|
|
|
58
58
|
afterEach(() => {
|
|
59
|
-
window.location = location;
|
|
59
|
+
(window as any).location = location;
|
|
60
60
|
window.history.replaceState = replaceState;
|
|
61
61
|
});
|
|
62
62
|
|
|
@@ -160,7 +160,7 @@ export class AuthCodeRedirectHandler implements IIncomingRedirectHandler {
|
|
|
160
160
|
};
|
|
161
161
|
}
|
|
162
162
|
|
|
163
|
-
const authFetch =
|
|
163
|
+
const authFetch = buildAuthenticatedFetch(tokens.accessToken, {
|
|
164
164
|
dpopKey: tokens.dpopKey,
|
|
165
165
|
refreshOptions,
|
|
166
166
|
eventEmitter,
|
|
@@ -264,6 +264,7 @@ describe("AuthorizationCodeWithPkceOidcHandler", () => {
|
|
|
264
264
|
...standardOidcOptions.client,
|
|
265
265
|
clientType: "dynamic",
|
|
266
266
|
clientSecret: "I can't cook because I only drink Soylent",
|
|
267
|
+
expiresAt: 95618140501000,
|
|
267
268
|
},
|
|
268
269
|
issuerConfiguration: {
|
|
269
270
|
...standardOidcOptions.issuerConfiguration,
|