@crossauth/sveltekit 1.0.1 → 1.1.1

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.
Files changed (47) hide show
  1. package/README.md +1 -1
  2. package/dist/index.d.ts +1 -1
  3. package/dist/index.js +16 -6181
  4. package/dist/sveltekitadminclientendpoints.d.ts +13 -12
  5. package/dist/sveltekitadminclientendpoints.js +187 -0
  6. package/dist/sveltekitadminendpoints.d.ts +5 -4
  7. package/dist/sveltekitadminendpoints.js +766 -0
  8. package/dist/sveltekitapikey.d.ts +4 -3
  9. package/dist/sveltekitapikey.js +81 -0
  10. package/dist/sveltekitoauthclient.d.ts +6 -4
  11. package/dist/sveltekitoauthclient.js +2309 -0
  12. package/dist/sveltekitoauthserver.d.ts +4 -4
  13. package/dist/sveltekitoauthserver.js +1350 -0
  14. package/dist/sveltekitresserver.d.ts +6 -4
  15. package/dist/sveltekitresserver.js +286 -0
  16. package/dist/sveltekitserver.d.ts +11 -9
  17. package/dist/sveltekitserver.js +393 -0
  18. package/dist/sveltekitsession.d.ts +6 -5
  19. package/dist/sveltekitsession.js +1112 -0
  20. package/dist/sveltekitsessionadapter.d.ts +2 -3
  21. package/dist/sveltekitsessionadapter.js +2 -0
  22. package/dist/sveltekitsharedclientendpoints.d.ts +7 -6
  23. package/dist/sveltekitsharedclientendpoints.js +630 -0
  24. package/dist/sveltekituserclientendpoints.d.ts +13 -12
  25. package/dist/sveltekituserclientendpoints.js +270 -0
  26. package/dist/sveltekituserendpoints.d.ts +6 -5
  27. package/dist/sveltekituserendpoints.js +1813 -0
  28. package/dist/tests/sveltekitadminclientendpoints.test.js +330 -0
  29. package/dist/tests/sveltekitadminendpoints.test.js +242 -0
  30. package/dist/tests/sveltekitapikeyserver.test.js +44 -0
  31. package/dist/tests/sveltekitoauthclient.test.d.ts +5 -5
  32. package/dist/tests/sveltekitoauthclient.test.js +1016 -0
  33. package/dist/tests/sveltekitoauthresserver.test.d.ts +4 -4
  34. package/dist/tests/sveltekitoauthresserver.test.js +185 -0
  35. package/dist/tests/sveltekitoauthserver.test.js +673 -0
  36. package/dist/tests/sveltekituserclientendpoints.test.js +244 -0
  37. package/dist/tests/sveltekituserendpoints.test.js +152 -0
  38. package/dist/tests/sveltemock.test.js +36 -0
  39. package/dist/tests/sveltemocks.d.ts +22 -8
  40. package/dist/tests/sveltemocks.js +114 -0
  41. package/dist/tests/sveltesessionhooks.test.js +224 -0
  42. package/dist/tests/testshared.d.ts +8 -8
  43. package/dist/tests/testshared.js +344 -0
  44. package/dist/utils.d.ts +1 -2
  45. package/dist/utils.js +123 -0
  46. package/package.json +23 -15
  47. package/dist/index.cjs +0 -1
@@ -0,0 +1,673 @@
1
+ // Copyright (c) 2026 Matthew Baker. All rights reserved. Licenced under the Apache Licence 2.0. See LICENSE file
2
+ import { MockRequestEvent } from './sveltemocks';
3
+ import { test, expect } from 'vitest';
4
+ import { makeServer, getCsrfToken, login } from './testshared';
5
+ export var passwordResetData;
6
+ test('SvelteKitOAuthServer.authorizeRedirectsToLogin', async () => {
7
+ const { server } = await makeServer(true, false, true);
8
+ const redirect = encodeURIComponent("http://example.com/redirect");
9
+ // authorize get endpoint
10
+ let getRequest = new Request(`http://server.com/authorize?response_type=code&client_id=ABC&redirect_uri=${redirect}&scope=read+write&state=ABC123`, {
11
+ method: "GET",
12
+ });
13
+ let event = new MockRequestEvent("1", getRequest, {});
14
+ let authServer = server.oAuthAuthServer;
15
+ let redirectTo = undefined;
16
+ try {
17
+ await authServer?.authorizeEndpoint.load(event);
18
+ }
19
+ catch (e) {
20
+ redirectTo = "location" in Object(e) ? Object(e).location : undefined;
21
+ }
22
+ expect(redirectTo).toContain("/login");
23
+ });
24
+ test('SvelteKitOAuthServer.getAccessTokenWhileLoggedIn', async () => {
25
+ const { server, resolver, handle } = await makeServer(true, false, true);
26
+ // log in
27
+ let resp = await login(server, resolver, handle);
28
+ const user = resp.event.locals.user;
29
+ let loginEvent = resp.event;
30
+ loginEvent = resp.event;
31
+ let sessionCookieValue = loginEvent.cookies.get("SESSIONID");
32
+ // authorize get endpoint
33
+ const redirect = encodeURIComponent("http://example.com/redirect");
34
+ let getRequest = new Request(`http://server.com/authorize?response_type=code&client_id=ABC&redirect_uri=${redirect}&scope=read+write&state=ABC123`, {
35
+ method: "GET",
36
+ headers: [
37
+ ["cookie", "SESSIONID=" + sessionCookieValue],
38
+ ]
39
+ });
40
+ let event = new MockRequestEvent("1", getRequest, {});
41
+ event.locals.user = user;
42
+ let authServer = server.oAuthAuthServer;
43
+ if (!authServer)
44
+ throw new Error("No auth server");
45
+ let redirectTo = undefined;
46
+ try {
47
+ const resp = await authServer?.authorizeEndpoint.load(event);
48
+ expect(resp?.ok).toBe(true);
49
+ expect(resp?.authorizationNeeded).toBeDefined();
50
+ expect(resp?.authorizationNeeded?.user?.username).toBe("bob");
51
+ }
52
+ catch (e) {
53
+ redirectTo = "location" in Object(e) ? Object(e).location : undefined;
54
+ }
55
+ expect(redirectTo).toBeUndefined();
56
+ const { csrfToken, csrfCookieValue } = await getCsrfToken(server, resolver, handle);
57
+ let sessionId = server.sessionServer?.sessionManager.getSessionId(sessionCookieValue ?? "");
58
+ // authorize
59
+ let postRequest = new Request("http://ex.com/authorize", {
60
+ method: "POST",
61
+ body: "csrfToken=" + csrfToken + "&" +
62
+ "response_type=code&" +
63
+ "client_id=ABC&" +
64
+ "redirect_uri=" + redirect + "&" +
65
+ "scope=read+write&" +
66
+ "state=ABC123&" +
67
+ "authorized=true",
68
+ headers: [
69
+ ["cookie", "CSRFTOKEN=" + csrfCookieValue],
70
+ ["cookie", "SESSIONID=" + sessionCookieValue],
71
+ ["content-type", "application/x-www-form-urlencoded"],
72
+ ]
73
+ });
74
+ event = new MockRequestEvent("1", postRequest, {});
75
+ event.locals.user = user;
76
+ event.locals.csrfToken = csrfToken;
77
+ event.locals.sessionId = sessionId;
78
+ try {
79
+ await authServer?.authorizeEndpoint.actions.default(event);
80
+ }
81
+ catch (e) {
82
+ redirectTo = "location" in Object(e) ? Object(e).location : undefined;
83
+ }
84
+ expect(redirectTo).toContain("http://example.com/redirect");
85
+ const redirectUrl = new URL(redirectTo ?? "");
86
+ let code = redirectUrl.searchParams.get("code");
87
+ // token
88
+ postRequest = new Request("http://ex.com/token", {
89
+ method: "POST",
90
+ body: JSON.stringify({
91
+ grant_type: "authorization_code",
92
+ client_id: "ABC",
93
+ client_secret: "DEF",
94
+ scope: "read write",
95
+ code: code,
96
+ }),
97
+ headers: [
98
+ ["content-type", "application/json"],
99
+ ]
100
+ });
101
+ event = new MockRequestEvent("1", postRequest, {});
102
+ let access_token = undefined;
103
+ try {
104
+ const resp = await authServer.tokenEndpoint.post(event);
105
+ access_token = (await resp.json()).access_token;
106
+ }
107
+ catch (e) {
108
+ redirectTo = "location" in Object(e) ? Object(e).location : undefined;
109
+ }
110
+ expect(access_token).toBeDefined();
111
+ });
112
+ test('SvelteKitOAuthServer.alreadyAuthorized', async () => {
113
+ const { server, resolver, handle } = await makeServer(true, false, true);
114
+ // log in
115
+ let resp = await login(server, resolver, handle);
116
+ const user = resp.event.locals.user;
117
+ let loginEvent = resp.event;
118
+ loginEvent = resp.event;
119
+ let sessionCookieValue = loginEvent.cookies.get("SESSIONID");
120
+ // authorize get endpoint
121
+ const redirect = encodeURIComponent("http://example.com/redirect");
122
+ let getRequest = new Request(`http://server.com/authorize?response_type=code&client_id=ABC&redirect_uri=${redirect}&scope=read+write&state=ABC123`, {
123
+ method: "GET",
124
+ headers: [
125
+ ["cookie", "SESSIONID=" + sessionCookieValue],
126
+ ]
127
+ });
128
+ let event = new MockRequestEvent("1", getRequest, {});
129
+ event.locals.user = user;
130
+ let authServer = server.oAuthAuthServer;
131
+ if (!authServer)
132
+ throw new Error("No auth server");
133
+ let redirectTo = undefined;
134
+ try {
135
+ const resp = await authServer?.authorizeEndpoint.load(event);
136
+ expect(resp?.ok).toBe(true);
137
+ expect(resp?.authorizationNeeded).toBeDefined();
138
+ expect(resp?.authorizationNeeded?.user?.username).toBe("bob");
139
+ }
140
+ catch (e) {
141
+ redirectTo = "location" in Object(e) ? Object(e).location : undefined;
142
+ }
143
+ expect(redirectTo).toBeUndefined();
144
+ const { csrfToken, csrfCookieValue } = await getCsrfToken(server, resolver, handle);
145
+ let sessionId = server.sessionServer?.sessionManager.getSessionId(sessionCookieValue ?? "");
146
+ // authorize
147
+ let postRequest = new Request("http://ex.com/authorize", {
148
+ method: "POST",
149
+ body: "csrfToken=" + csrfToken + "&" +
150
+ "response_type=code&" +
151
+ "client_id=ABC&" +
152
+ "redirect_uri=" + redirect + "&" +
153
+ "scope=read+write&" +
154
+ "state=ABC123&" +
155
+ "authorized=true",
156
+ headers: [
157
+ ["cookie", "CSRFTOKEN=" + csrfCookieValue],
158
+ ["cookie", "SESSIONID=" + sessionCookieValue],
159
+ ["content-type", "application/x-www-form-urlencoded"],
160
+ ]
161
+ });
162
+ event = new MockRequestEvent("1", postRequest, {});
163
+ event.locals.user = user;
164
+ event.locals.csrfToken = csrfToken;
165
+ event.locals.sessionId = sessionId;
166
+ try {
167
+ await authServer?.authorizeEndpoint.actions.default(event);
168
+ }
169
+ catch (e) {
170
+ redirectTo = "location" in Object(e) ? Object(e).location : undefined;
171
+ }
172
+ expect(redirectTo).toContain("http://example.com/redirect");
173
+ const redirectUrl = new URL(redirectTo ?? "");
174
+ let code = redirectUrl.searchParams.get("code");
175
+ expect(code).toBeDefined();
176
+ // second authorize request
177
+ event = new MockRequestEvent("1", getRequest, {});
178
+ event.locals.user = user;
179
+ redirectTo = undefined;
180
+ try {
181
+ await authServer?.authorizeEndpoint.load(event);
182
+ }
183
+ catch (e) {
184
+ redirectTo = "location" in Object(e) ? Object(e).location : undefined;
185
+ }
186
+ expect(redirectTo).toContain("http://example.com/redirect");
187
+ });
188
+ test('SvelteKitOAuthServer.notAuthorized', async () => {
189
+ const { server, resolver, handle } = await makeServer(true, false, true);
190
+ // log in
191
+ let resp = await login(server, resolver, handle);
192
+ const user = resp.event.locals.user;
193
+ let loginEvent = resp.event;
194
+ loginEvent = resp.event;
195
+ let sessionCookieValue = loginEvent.cookies.get("SESSIONID");
196
+ // authorize get endpoint
197
+ const redirect = encodeURIComponent("http://example.com/redirect");
198
+ let getRequest = new Request(`http://server.com/authorize?response_type=code&client_id=ABC&redirect_uri=${redirect}&scope=read+write&state=ABC123`, {
199
+ method: "GET",
200
+ headers: [
201
+ ["cookie", "SESSIONID=" + sessionCookieValue],
202
+ ]
203
+ });
204
+ let event = new MockRequestEvent("1", getRequest, {});
205
+ event.locals.user = user;
206
+ let authServer = server.oAuthAuthServer;
207
+ if (!authServer)
208
+ throw new Error("No auth server");
209
+ let redirectTo = undefined;
210
+ try {
211
+ const resp = await authServer?.authorizeEndpoint.load(event);
212
+ expect(resp?.ok).toBe(true);
213
+ expect(resp?.authorizationNeeded).toBeDefined();
214
+ expect(resp?.authorizationNeeded?.user?.username).toBe("bob");
215
+ }
216
+ catch (e) {
217
+ redirectTo = "location" in Object(e) ? Object(e).location : undefined;
218
+ }
219
+ expect(redirectTo).toBeUndefined();
220
+ const { csrfToken, csrfCookieValue } = await getCsrfToken(server, resolver, handle);
221
+ let sessionId = server.sessionServer?.sessionManager.getSessionId(sessionCookieValue ?? "");
222
+ // authorize
223
+ let postRequest = new Request("http://ex.com/authorize", {
224
+ method: "POST",
225
+ body: "csrfToken=" + csrfToken + "&" +
226
+ "response_type=code&" +
227
+ "client_id=ABC&" +
228
+ "redirect_uri=" + redirect + "&" +
229
+ "scope=read+write&" +
230
+ "state=ABC123&" +
231
+ "authorized=false",
232
+ headers: [
233
+ ["cookie", "CSRFTOKEN=" + csrfCookieValue],
234
+ ["cookie", "SESSIONID=" + sessionCookieValue],
235
+ ["content-type", "application/x-www-form-urlencoded"],
236
+ ]
237
+ });
238
+ event = new MockRequestEvent("1", postRequest, {});
239
+ event.locals.user = user;
240
+ event.locals.csrfToken = csrfToken;
241
+ event.locals.sessionId = sessionId;
242
+ try {
243
+ await authServer?.authorizeEndpoint.actions.default(event);
244
+ }
245
+ catch (e) {
246
+ redirectTo = "location" in Object(e) ? Object(e).location : undefined;
247
+ }
248
+ expect(redirectTo).toContain("http://example.com/redirect");
249
+ const url = new URL(redirectTo ?? "");
250
+ expect(url.searchParams.get("error")).toBe("access_denied");
251
+ });
252
+ test('SvelteKitOAuthServer.oidcConfiguration', async () => {
253
+ const { server } = await makeServer(true, false, true);
254
+ let getRequest = new Request(`http://server.com/oidc-configuration`, {
255
+ method: "GET",
256
+ });
257
+ let event = new MockRequestEvent("1", getRequest, {});
258
+ let authServer = server.oAuthAuthServer;
259
+ const resp = await authServer?.oidcConfigurationEndpoint.get(event);
260
+ expect(resp?.status).toBe(200);
261
+ const body = await resp?.json();
262
+ expect(body?.authorization_endpoint).toBe("http://localhost:3000/oauth/authorize");
263
+ expect(body?.token_endpoint).toBe("http://localhost:3000/oauth/token");
264
+ expect(body?.jwks_uri).toBe("http://localhost:3000/oauth/jwks");
265
+ });
266
+ test('SvelteKitOAuthServer.jwks', async () => {
267
+ const { server } = await makeServer(true, false, true);
268
+ let getRequest = new Request(`http://server.com/jwks`, {
269
+ method: "GET",
270
+ });
271
+ let event = new MockRequestEvent("1", getRequest, {});
272
+ let authServer = server.oAuthAuthServer;
273
+ const resp = await authServer?.jwksGetEndpoint.get(event);
274
+ expect(resp?.status).toBe(200);
275
+ const body = await resp?.json();
276
+ expect(body.keys).toBeDefined();
277
+ expect(body.keys.length).toBe(1);
278
+ });
279
+ test('SvelteKitOAuthServer.getCsrfTokenJson', async () => {
280
+ const { server } = await makeServer(true, false, true);
281
+ let getRequest = new Request(`http://server.com/getcsrftoken`, {
282
+ method: "GET",
283
+ });
284
+ let event = new MockRequestEvent("1", getRequest, {});
285
+ let authServer = server.oAuthAuthServer;
286
+ const resp = await authServer?.getCsrfTokenEndpoint.get(event);
287
+ expect(resp?.status).toBe(200);
288
+ const body = await resp?.json();
289
+ expect(body.ok).toBe(false);
290
+ });
291
+ test('SvelteKitOAuthServer.getCsrfTokenCookie', async () => {
292
+ const { server } = await makeServer(true, false, true, false, { refreshTokenType: "cookie" });
293
+ let getRequest = new Request(`http://server.com/getcsrftoken`, {
294
+ method: "GET",
295
+ });
296
+ let event = new MockRequestEvent("1", getRequest, {});
297
+ let authServer = server.oAuthAuthServer;
298
+ const resp = await authServer?.getCsrfTokenEndpoint.get(event);
299
+ expect(resp?.status).toBe(200);
300
+ const body = await resp?.json();
301
+ expect(body.ok).toBe(true);
302
+ expect(body.csrfToken).toBeDefined();
303
+ });
304
+ test('SvelteKitOAuthServer.mfa', async () => {
305
+ const { server, resolver, handle } = await makeServer(true, false, true);
306
+ // log in
307
+ await login(server, resolver, handle);
308
+ // get OAuth auth server
309
+ let authServer = server.oAuthAuthServer;
310
+ if (!authServer)
311
+ throw new Error("No auth server");
312
+ // token
313
+ let postRequest = new Request("http://ex.com/token", {
314
+ method: "POST",
315
+ body: JSON.stringify({
316
+ grant_type: "password",
317
+ client_id: "ABC",
318
+ client_secret: "DEF",
319
+ scope: "read write",
320
+ username: "alice",
321
+ password: "alicePass123",
322
+ state: "ABCDEF",
323
+ }),
324
+ headers: [
325
+ ["content-type", "application/json"],
326
+ ]
327
+ });
328
+ let event = new MockRequestEvent("1", postRequest, {});
329
+ const resp2 = await authServer.tokenEndpoint.post(event);
330
+ const body2 = await resp2.json();
331
+ expect(body2.error).toBe("mfa_required");
332
+ const mfa_token = body2.mfa_token ?? "";
333
+ // authenticators
334
+ let getRequest = new Request(`http://server.com/authenticators`, {
335
+ method: "GET",
336
+ headers: {
337
+ 'authorization': "Bearer " + mfa_token,
338
+ },
339
+ });
340
+ event = new MockRequestEvent("1", getRequest, {});
341
+ const resp3 = await authServer.mfaAuthenticatorsEndpoint.get(event);
342
+ const body3 = await resp3.json();
343
+ expect(Array.isArray(body3)).toBe(true);
344
+ expect(body3.length).toBe(1);
345
+ // challenge
346
+ postRequest = new Request("http://ex.com/mfachallenge", {
347
+ method: "POST",
348
+ body: JSON.stringify({
349
+ mfa_token: mfa_token,
350
+ client_id: "ABC",
351
+ client_secret: "DEF",
352
+ authenticator_id: "dummyFactor2",
353
+ challenge_type: "oob",
354
+ }),
355
+ headers: [
356
+ ["content-type", "application/json"],
357
+ ]
358
+ });
359
+ event = new MockRequestEvent("1", postRequest, {});
360
+ const resp4 = await authServer.mfaChallengeEndpoint.post(event);
361
+ const body4 = await resp4.json();
362
+ expect(body4.challenge_type).toBe("oob");
363
+ expect(body4.oob_code).toBeDefined();
364
+ const oob_code = body4.oob_code;
365
+ const oob = "0000";
366
+ // OOB
367
+ postRequest = new Request("http://ex.com/token", {
368
+ method: "POST",
369
+ body: JSON.stringify({
370
+ grant_type: "http://auth0.com/oauth/grant-type/mfa-oob",
371
+ client_id: "ABC",
372
+ scope: "read write",
373
+ client_secret: "DEF",
374
+ mfa_token: mfa_token,
375
+ oob_code: oob_code,
376
+ binding_code: oob
377
+ }),
378
+ headers: [
379
+ ["content-type", "application/json"],
380
+ ]
381
+ });
382
+ event = new MockRequestEvent("1", postRequest, {});
383
+ const resp5 = await authServer.tokenEndpoint.post(event);
384
+ const body5 = await resp5.json();
385
+ expect(body5.access_token).toBeDefined();
386
+ });
387
+ test('SvelteKitOAuthServer.deviceCodeManual', async () => {
388
+ const { server, resolver, handle, userStorage } = await makeServer(true, false, true);
389
+ // log in
390
+ await login(server, resolver, handle);
391
+ // get OAuth auth server
392
+ let authServer = server.oAuthAuthServer;
393
+ if (!authServer)
394
+ throw new Error("No auth server");
395
+ // call device authorization endpoint to create user and device code
396
+ let postRequest = new Request("http://ex.com/dummy", {
397
+ method: "POST",
398
+ body: JSON.stringify({
399
+ grant_type: "urn:ietf:params:oauth:grant-type:device_code",
400
+ client_id: "ABC",
401
+ client_secret: "DEF",
402
+ scope: "read write",
403
+ }),
404
+ headers: [
405
+ ["content-type", "application/json"],
406
+ ]
407
+ });
408
+ let event = new MockRequestEvent("1", postRequest, {});
409
+ const devAuthResp = await authServer.deviceAuthorizationEndpoint.post(event);
410
+ let devAuthBody = await devAuthResp.json();
411
+ expect(devAuthBody.device_code).toBeDefined();
412
+ expect(devAuthBody.verification_uri).toBe('http://localhost:5174/device');
413
+ expect(devAuthBody.verification_uri_complete).toContain('http://localhost:5174/device?user_code=');
414
+ expect(devAuthBody.user_code?.length).toBe(9);
415
+ // call device endpoint - should result in a redirect to login page
416
+ let getRequest = new Request("http://ex.com/dummy", {
417
+ method: "GET",
418
+ });
419
+ event = new MockRequestEvent("1", getRequest, {});
420
+ let location = undefined;
421
+ let status = undefined;
422
+ try {
423
+ await authServer.deviceEndpoint.load(event);
424
+ }
425
+ catch (e) {
426
+ location = e.location;
427
+ status = e.status;
428
+ }
429
+ expect(status).toBe(302);
430
+ expect(location).toBe("/login?next=http%3A%2F%2Fex.com%2Fdummy");
431
+ // simulate login
432
+ const user = (await userStorage.getUserByUsername("bob")).user;
433
+ event.locals.user = user;
434
+ // call device endpoint again, without user code. Should return data for showing prompt page
435
+ let devResp = await authServer.deviceEndpoint.load(event);
436
+ expect(devResp.ok).toBe(true);
437
+ expect(devResp.completed).toBe(false);
438
+ expect(devResp.retryAllowed).toBe(true);
439
+ expect(devResp.user?.username).toBe("bob");
440
+ expect(devResp.authorizationNeeded).toBeUndefined();
441
+ // call token endpoint - should return authorization_pending
442
+ let tokenRequest = new Request("http://ex.com/token", {
443
+ method: "POST",
444
+ body: JSON.stringify({
445
+ grant_type: "urn:ietf:params:oauth:grant-type:device_code",
446
+ client_id: "ABC",
447
+ client_secret: "DEF",
448
+ scope: "read write",
449
+ device_code: devAuthBody.device_code,
450
+ }),
451
+ headers: [
452
+ ["content-type", "application/json"],
453
+ ]
454
+ });
455
+ let tokenEvent = new MockRequestEvent("1", tokenRequest, {});
456
+ let tokenResp = await authServer.tokenEndpoint.post(tokenEvent);
457
+ let tokenBody = await tokenResp.json();
458
+ expect(tokenResp.status).toBe(200);
459
+ expect(tokenBody.error).toBe("authorization_pending");
460
+ // submit wrong user code
461
+ postRequest = new Request("http://ex.com/dummy", {
462
+ method: "POST",
463
+ body: JSON.stringify({
464
+ user_code: "XXX",
465
+ }),
466
+ headers: [
467
+ ["content-type", "application/json"],
468
+ ]
469
+ });
470
+ event = new MockRequestEvent("1", postRequest, {});
471
+ event.locals.user = user;
472
+ let devPostResp = await authServer.deviceEndpoint.actions.userCode(event);
473
+ expect(devPostResp.ok).toBe(false);
474
+ expect(devPostResp.error).toBe("access_denied");
475
+ // token should still return authorization_pending
476
+ tokenResp = await authServer.tokenEndpoint.post(tokenEvent);
477
+ tokenBody = await tokenResp.json();
478
+ expect(tokenResp.status).toBe(200);
479
+ expect(tokenBody.error).toBe("authorization_pending");
480
+ // submit right user code
481
+ postRequest = new Request("http://ex.com/dummy", {
482
+ method: "POST",
483
+ body: JSON.stringify({
484
+ user_code: devAuthBody.user_code,
485
+ }),
486
+ headers: [
487
+ ["content-type", "application/json"],
488
+ ]
489
+ });
490
+ event = new MockRequestEvent("1", postRequest, {});
491
+ event.locals.user = user;
492
+ devPostResp = await authServer.deviceEndpoint.actions.userCode(event);
493
+ expect(devPostResp.ok).toBe(true);
494
+ expect(devPostResp.completed).toBe(false);
495
+ expect(devPostResp.authorizationNeeded?.user?.username).toBe("bob");
496
+ expect(devPostResp.authorizationNeeded?.client_id).toBe("ABC");
497
+ expect(devPostResp.authorizationNeeded?.scope).toBe("read write");
498
+ expect(devPostResp.user_code).toBe(devAuthBody.user_code);
499
+ // authorize scopes
500
+ authServer.authServer.validateAndPersistScope("ABC", "read write", user);
501
+ // submit user code again
502
+ devPostResp = await authServer.deviceEndpoint.actions.userCode(event);
503
+ expect(devPostResp.ok).toBe(true);
504
+ expect(devPostResp.completed).toBe(true);
505
+ // token should return access_token
506
+ tokenResp = await authServer.tokenEndpoint.post(tokenEvent);
507
+ tokenBody = await tokenResp.json();
508
+ expect(tokenResp.status).toBe(200);
509
+ expect(tokenBody.error).toBeUndefined();
510
+ expect(tokenBody.access_token).toBeDefined();
511
+ });
512
+ test('SvelteKitOAuthServer.deviceCodeAuto', async () => {
513
+ const { server, resolver, handle, userStorage } = await makeServer(true, false, true);
514
+ // log in
515
+ await login(server, resolver, handle);
516
+ // get OAuth auth server
517
+ let authServer = server.oAuthAuthServer;
518
+ if (!authServer)
519
+ throw new Error("No auth server");
520
+ // call device authorization endpoint to create user and device code
521
+ let postRequest = new Request("http://ex.com/dummy", {
522
+ method: "POST",
523
+ body: JSON.stringify({
524
+ grant_type: "urn:ietf:params:oauth:grant-type:device_code",
525
+ client_id: "ABC",
526
+ client_secret: "DEF",
527
+ scope: "read write",
528
+ }),
529
+ headers: [
530
+ ["content-type", "application/json"],
531
+ ]
532
+ });
533
+ let event = new MockRequestEvent("1", postRequest, {});
534
+ const devAuthResp = await authServer.deviceAuthorizationEndpoint.post(event);
535
+ let devAuthBody = await devAuthResp.json();
536
+ expect(devAuthBody.device_code).toBeDefined();
537
+ expect(devAuthBody.verification_uri).toBe('http://localhost:5174/device');
538
+ expect(devAuthBody.verification_uri_complete).toContain('http://localhost:5174/device?user_code=');
539
+ expect(devAuthBody.user_code?.length).toBe(9);
540
+ // call device endpoint - should result in a redirect to login page
541
+ let getRequest = new Request("http://ex.com/dummy?user_code=" + devAuthBody.user_code, {
542
+ method: "GET",
543
+ });
544
+ event = new MockRequestEvent("1", getRequest, {});
545
+ let location = undefined;
546
+ let status = undefined;
547
+ try {
548
+ await authServer.deviceEndpoint.load(event);
549
+ }
550
+ catch (e) {
551
+ location = e.location;
552
+ status = e.status;
553
+ }
554
+ expect(status).toBe(302);
555
+ expect(location).toContain("/login?next=http%3A%2F%2Fex.com%2Fdummy%3Fuser_code%3D");
556
+ // simulate login
557
+ const user = (await userStorage.getUserByUsername("bob")).user;
558
+ event.locals.user = user;
559
+ // call device endpoint again, without user code. Should return data for showing prompt page
560
+ let devResp = await authServer.deviceEndpoint.load(event);
561
+ expect(devResp.ok).toBe(true);
562
+ expect(devResp.completed).toBe(false);
563
+ expect(devResp.retryAllowed).toBe(true);
564
+ expect(devResp.user?.username).toBe("bob");
565
+ expect(devResp.authorizationNeeded).toBeDefined();
566
+ expect(devResp.user_code?.length).toBe(9);
567
+ // authorize scopes
568
+ authServer.authServer.validateAndPersistScope("ABC", "read write", user);
569
+ // call token endpoint - should return authorization_pending
570
+ let tokenRequest = new Request("http://ex.com/token", {
571
+ method: "POST",
572
+ body: JSON.stringify({
573
+ grant_type: "urn:ietf:params:oauth:grant-type:device_code",
574
+ client_id: "ABC",
575
+ client_secret: "DEF",
576
+ scope: "read write",
577
+ device_code: devAuthBody.device_code,
578
+ }),
579
+ headers: [
580
+ ["content-type", "application/json"],
581
+ ]
582
+ });
583
+ let tokenEvent = new MockRequestEvent("1", tokenRequest, {});
584
+ let tokenResp = await authServer.tokenEndpoint.post(tokenEvent);
585
+ let tokenBody = await tokenResp.json();
586
+ expect(tokenResp.status).toBe(200);
587
+ expect(tokenBody.error).toBe("authorization_pending");
588
+ // submit post with user code
589
+ postRequest = new Request("http://ex.com/dummy", {
590
+ method: "POST",
591
+ body: JSON.stringify({
592
+ user_code: devResp.user_code,
593
+ }),
594
+ headers: [
595
+ ["content-type", "application/json"],
596
+ ]
597
+ });
598
+ event = new MockRequestEvent("1", postRequest, {});
599
+ event.locals.user = user;
600
+ let devPostResp = await authServer.deviceEndpoint.actions.userCode(event);
601
+ expect(devPostResp.ok).toBe(true);
602
+ expect(devPostResp.completed).toBe(true);
603
+ expect(devPostResp.authorizationNeeded).toBeUndefined();
604
+ // token should return access_token
605
+ tokenResp = await authServer.tokenEndpoint.post(tokenEvent);
606
+ tokenBody = await tokenResp.json();
607
+ expect(tokenResp.status).toBe(200);
608
+ expect(tokenBody.error).toBeUndefined();
609
+ expect(tokenBody.access_token).toBeDefined();
610
+ });
611
+ test('SvelteKitOAuthServer.deviceCodeAutoPreAuthorizePreLogin', async () => {
612
+ const { server, resolver, handle, userStorage } = await makeServer(true, false, true);
613
+ // log in
614
+ await login(server, resolver, handle);
615
+ // get OAuth auth server
616
+ let authServer = server.oAuthAuthServer;
617
+ if (!authServer)
618
+ throw new Error("No auth server");
619
+ // call device authorization endpoint to create user and device code
620
+ let postRequest = new Request("http://ex.com/dummy", {
621
+ method: "POST",
622
+ body: JSON.stringify({
623
+ grant_type: "urn:ietf:params:oauth:grant-type:device_code",
624
+ client_id: "ABC",
625
+ client_secret: "DEF",
626
+ scope: "read write",
627
+ }),
628
+ headers: [
629
+ ["content-type", "application/json"],
630
+ ]
631
+ });
632
+ let event = new MockRequestEvent("1", postRequest, {});
633
+ const devAuthResp = await authServer.deviceAuthorizationEndpoint.post(event);
634
+ let devAuthBody = await devAuthResp.json();
635
+ expect(devAuthBody.device_code).toBeDefined();
636
+ expect(devAuthBody.verification_uri).toBe('http://localhost:5174/device');
637
+ expect(devAuthBody.verification_uri_complete).toContain('http://localhost:5174/device?user_code=');
638
+ expect(devAuthBody.user_code?.length).toBe(9);
639
+ const user = (await userStorage.getUserByUsername("bob")).user;
640
+ // authorize scopes
641
+ authServer.authServer.validateAndPersistScope("ABC", "read write", user);
642
+ // call device endpoint - should result in a redirect to login page
643
+ let getRequest = new Request("http://ex.com/dummy?user_code=" + devAuthBody.user_code, {
644
+ method: "GET",
645
+ });
646
+ event = new MockRequestEvent("1", getRequest, {});
647
+ event.locals.user = user;
648
+ let devResp = await authServer.deviceEndpoint.load(event);
649
+ expect(devResp.ok).toBe(true);
650
+ expect(devResp.completed).toBe(true);
651
+ expect(devResp.user?.username).toBe("bob");
652
+ expect(devResp.authorizationNeeded).toBeUndefined();
653
+ // call token endpoint - should return access token
654
+ let tokenRequest = new Request("http://ex.com/token", {
655
+ method: "POST",
656
+ body: JSON.stringify({
657
+ grant_type: "urn:ietf:params:oauth:grant-type:device_code",
658
+ client_id: "ABC",
659
+ client_secret: "DEF",
660
+ scope: "read write",
661
+ device_code: devAuthBody.device_code,
662
+ }),
663
+ headers: [
664
+ ["content-type", "application/json"],
665
+ ]
666
+ });
667
+ let tokenEvent = new MockRequestEvent("1", tokenRequest, {});
668
+ let tokenResp = await authServer.tokenEndpoint.post(tokenEvent);
669
+ let tokenBody = await tokenResp.json();
670
+ expect(tokenResp.status).toBe(200);
671
+ expect(tokenBody.error).toBeUndefined();
672
+ expect(tokenBody.access_token).toBeDefined();
673
+ });