@japikey/cloudflare 0.1.0 → 0.2.2
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 +21 -0
- package/dist/d1.d.ts +22 -0
- package/dist/d1.d.ts.map +1 -0
- package/dist/d1.js +143 -0
- package/{src/index.ts → dist/index.d.ts} +2 -9
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +3 -0
- package/dist/router.d.ts +19 -0
- package/dist/router.d.ts.map +1 -0
- package/dist/router.js +195 -0
- package/package.json +7 -2
- package/eslint.config.js +0 -3
- package/src/d1.ts +0 -185
- package/src/router.ts +0 -280
- package/test/index.test.ts +0 -14
- package/test/router.test.ts +0 -1120
- package/tsconfig.json +0 -9
package/test/router.test.ts
DELETED
|
@@ -1,1120 +0,0 @@
|
|
|
1
|
-
import {
|
|
2
|
-
describe,
|
|
3
|
-
test,
|
|
4
|
-
expect,
|
|
5
|
-
beforeAll,
|
|
6
|
-
afterAll,
|
|
7
|
-
vi,
|
|
8
|
-
Mock,
|
|
9
|
-
beforeEach,
|
|
10
|
-
} from 'vitest';
|
|
11
|
-
import type {
|
|
12
|
-
ExportedHandler,
|
|
13
|
-
ExecutionContext,
|
|
14
|
-
Request as CloudflareRequest,
|
|
15
|
-
IncomingRequestCfProperties,
|
|
16
|
-
} from '@cloudflare/workers-types';
|
|
17
|
-
import { v4 as uuidv4 } from 'uuid';
|
|
18
|
-
import * as jose from 'jose';
|
|
19
|
-
import {
|
|
20
|
-
createJWKSRouter,
|
|
21
|
-
createApiKeyRouter,
|
|
22
|
-
isJWKSPath,
|
|
23
|
-
type CreateApiKeyRouterOptions,
|
|
24
|
-
} from '../src/router.ts';
|
|
25
|
-
|
|
26
|
-
import { createApiKey, DatabaseDriver, errorType } from '@japikey/japikey';
|
|
27
|
-
import {
|
|
28
|
-
baseIssuer,
|
|
29
|
-
userClaims,
|
|
30
|
-
apiKeyOptions,
|
|
31
|
-
} from '../../japikey/test/testHelpers.ts';
|
|
32
|
-
import { appendPathToUrl } from '../../shared/src/util.ts';
|
|
33
|
-
import SqliteDriver from '../../sqlite/src/index.ts';
|
|
34
|
-
import { UnauthorizedError } from '@japikey/shared';
|
|
35
|
-
|
|
36
|
-
function castRequest(
|
|
37
|
-
request: Request
|
|
38
|
-
): CloudflareRequest<unknown, IncomingRequestCfProperties<unknown>> {
|
|
39
|
-
return request as unknown as CloudflareRequest<
|
|
40
|
-
unknown,
|
|
41
|
-
IncomingRequestCfProperties<unknown>
|
|
42
|
-
>;
|
|
43
|
-
}
|
|
44
|
-
|
|
45
|
-
describe('createJWKSRouter', () => {
|
|
46
|
-
let db: DatabaseDriver;
|
|
47
|
-
let app: ExportedHandler;
|
|
48
|
-
const ctx: ExecutionContext = {
|
|
49
|
-
waitUntil: vi.fn(),
|
|
50
|
-
passThroughOnException: vi.fn(),
|
|
51
|
-
props: {},
|
|
52
|
-
};
|
|
53
|
-
beforeAll(async () => {
|
|
54
|
-
db = new SqliteDriver(':memory:');
|
|
55
|
-
await db.ensureTable();
|
|
56
|
-
app = createJWKSRouter(baseIssuer, db);
|
|
57
|
-
});
|
|
58
|
-
afterAll(async () => {
|
|
59
|
-
await db.close();
|
|
60
|
-
});
|
|
61
|
-
|
|
62
|
-
test('returns the JWKS for a valid kid', async () => {
|
|
63
|
-
const { jwk, kid } = await createApiKey(userClaims(), apiKeyOptions());
|
|
64
|
-
await db.insertApiKey({
|
|
65
|
-
user_id: apiKeyOptions().sub,
|
|
66
|
-
revoked: false,
|
|
67
|
-
metadata: {},
|
|
68
|
-
jwk,
|
|
69
|
-
kid,
|
|
70
|
-
});
|
|
71
|
-
const response = await app.fetch!(
|
|
72
|
-
castRequest(
|
|
73
|
-
new Request(appendPathToUrl(baseIssuer, `${kid}/.well-known/jwks.json`))
|
|
74
|
-
),
|
|
75
|
-
{},
|
|
76
|
-
ctx
|
|
77
|
-
);
|
|
78
|
-
expect(response.status).toBe(200);
|
|
79
|
-
await expect(response.json()).resolves.toEqual({ keys: [jwk] });
|
|
80
|
-
});
|
|
81
|
-
|
|
82
|
-
test('returns a 404 for a revoked kid', async () => {
|
|
83
|
-
const { jwk, kid } = await createApiKey(userClaims(), apiKeyOptions());
|
|
84
|
-
await db.insertApiKey({
|
|
85
|
-
user_id: apiKeyOptions().sub,
|
|
86
|
-
revoked: false,
|
|
87
|
-
metadata: {},
|
|
88
|
-
jwk,
|
|
89
|
-
kid,
|
|
90
|
-
});
|
|
91
|
-
await db.revokeApiKey({ user_id: apiKeyOptions().sub, kid });
|
|
92
|
-
const response = await app.fetch!(
|
|
93
|
-
castRequest(
|
|
94
|
-
new Request(appendPathToUrl(baseIssuer, `${kid}/.well-known/jwks.json`))
|
|
95
|
-
),
|
|
96
|
-
{},
|
|
97
|
-
ctx
|
|
98
|
-
);
|
|
99
|
-
expect(response.status).toBe(404);
|
|
100
|
-
await expect(response.json()).resolves.toEqual({
|
|
101
|
-
error: {
|
|
102
|
-
message: 'API key not found',
|
|
103
|
-
type: errorType.NOT_FOUND,
|
|
104
|
-
},
|
|
105
|
-
});
|
|
106
|
-
});
|
|
107
|
-
|
|
108
|
-
test('returns a 404 for a non-existent kid', async () => {
|
|
109
|
-
const missingKid = uuidv4();
|
|
110
|
-
const response = await app.fetch!(
|
|
111
|
-
castRequest(
|
|
112
|
-
new Request(
|
|
113
|
-
appendPathToUrl(baseIssuer, `${missingKid}/.well-known/jwks.json`)
|
|
114
|
-
)
|
|
115
|
-
),
|
|
116
|
-
{},
|
|
117
|
-
ctx
|
|
118
|
-
);
|
|
119
|
-
expect(response.status).toBe(404);
|
|
120
|
-
await expect(response.json()).resolves.toEqual({
|
|
121
|
-
error: {
|
|
122
|
-
message: 'API key not found',
|
|
123
|
-
type: errorType.NOT_FOUND,
|
|
124
|
-
},
|
|
125
|
-
});
|
|
126
|
-
});
|
|
127
|
-
|
|
128
|
-
test('returns a 404 for a malformed kid', async () => {
|
|
129
|
-
const response = await app.fetch!(
|
|
130
|
-
castRequest(
|
|
131
|
-
new Request(
|
|
132
|
-
appendPathToUrl(baseIssuer, `not-a-uuid/.well-known/jwks.json`)
|
|
133
|
-
)
|
|
134
|
-
),
|
|
135
|
-
{},
|
|
136
|
-
ctx
|
|
137
|
-
);
|
|
138
|
-
expect(response.status).toBe(404);
|
|
139
|
-
await expect(response.json()).resolves.toEqual({
|
|
140
|
-
error: {
|
|
141
|
-
message: 'Invalid JWKS request',
|
|
142
|
-
type: errorType.NOT_FOUND,
|
|
143
|
-
},
|
|
144
|
-
});
|
|
145
|
-
});
|
|
146
|
-
|
|
147
|
-
test('returns a 404 for the wrong subpath', async () => {
|
|
148
|
-
const response = await app.fetch!(
|
|
149
|
-
castRequest(
|
|
150
|
-
new Request(
|
|
151
|
-
appendPathToUrl(
|
|
152
|
-
baseIssuer,
|
|
153
|
-
`another-subpath/${uuidv4()}/.well-known/jwks.json`
|
|
154
|
-
)
|
|
155
|
-
)
|
|
156
|
-
),
|
|
157
|
-
{},
|
|
158
|
-
ctx
|
|
159
|
-
);
|
|
160
|
-
expect(response.status).toBe(404);
|
|
161
|
-
await expect(response.json()).resolves.toEqual({
|
|
162
|
-
error: {
|
|
163
|
-
message: 'Invalid JWKS request',
|
|
164
|
-
type: errorType.NOT_FOUND,
|
|
165
|
-
},
|
|
166
|
-
});
|
|
167
|
-
});
|
|
168
|
-
|
|
169
|
-
test('returns a 404 for the wrong issuer', async () => {
|
|
170
|
-
const response = await app.fetch!(
|
|
171
|
-
castRequest(
|
|
172
|
-
new Request(
|
|
173
|
-
appendPathToUrl(
|
|
174
|
-
new URL('https://susu.dev'),
|
|
175
|
-
`${uuidv4()}/.well-known/jwks.json`
|
|
176
|
-
)
|
|
177
|
-
)
|
|
178
|
-
),
|
|
179
|
-
{},
|
|
180
|
-
ctx
|
|
181
|
-
);
|
|
182
|
-
expect(response.status).toBe(404);
|
|
183
|
-
await expect(response.json()).resolves.toEqual({
|
|
184
|
-
error: {
|
|
185
|
-
message: 'Invalid JWKS request',
|
|
186
|
-
type: errorType.NOT_FOUND,
|
|
187
|
-
},
|
|
188
|
-
});
|
|
189
|
-
});
|
|
190
|
-
});
|
|
191
|
-
|
|
192
|
-
describe('createApiKeyRouter', () => {
|
|
193
|
-
let db: DatabaseDriver;
|
|
194
|
-
let getUserId: Mock;
|
|
195
|
-
let parseCreateApiKeyRequest: Mock;
|
|
196
|
-
let userId: string;
|
|
197
|
-
const ctx: ExecutionContext = {
|
|
198
|
-
waitUntil: vi.fn(),
|
|
199
|
-
passThroughOnException: vi.fn(),
|
|
200
|
-
props: {},
|
|
201
|
-
};
|
|
202
|
-
|
|
203
|
-
beforeAll(async () => {
|
|
204
|
-
db = new SqliteDriver(':memory:');
|
|
205
|
-
await db.ensureTable();
|
|
206
|
-
});
|
|
207
|
-
|
|
208
|
-
afterAll(async () => {
|
|
209
|
-
await db.close();
|
|
210
|
-
});
|
|
211
|
-
|
|
212
|
-
beforeEach(() => {
|
|
213
|
-
getUserId = vi.fn();
|
|
214
|
-
parseCreateApiKeyRequest = vi.fn();
|
|
215
|
-
|
|
216
|
-
getUserId.mockReset();
|
|
217
|
-
userId = uuidv4();
|
|
218
|
-
getUserId.mockResolvedValue(userId);
|
|
219
|
-
|
|
220
|
-
parseCreateApiKeyRequest.mockReset();
|
|
221
|
-
parseCreateApiKeyRequest.mockResolvedValue({
|
|
222
|
-
expiresAt: new Date(Date.now() + 1000 * 60 * 60 * 24 * 365),
|
|
223
|
-
claims: {},
|
|
224
|
-
databaseMetadata: {},
|
|
225
|
-
});
|
|
226
|
-
});
|
|
227
|
-
|
|
228
|
-
const routePrefixTestData = [
|
|
229
|
-
{ routePrefix: '', normalizedPath: '' },
|
|
230
|
-
{ routePrefix: '/', normalizedPath: '/' },
|
|
231
|
-
{ routePrefix: 'api-keys', normalizedPath: '/api-keys' },
|
|
232
|
-
{ routePrefix: 'api-keys/', normalizedPath: '/api-keys/' },
|
|
233
|
-
{ routePrefix: '/api-keys', normalizedPath: '/api-keys' },
|
|
234
|
-
{ routePrefix: '/api-keys/', normalizedPath: '/api-keys/' },
|
|
235
|
-
{ routePrefix: '/subpath/api-keys', normalizedPath: '/subpath/api-keys' },
|
|
236
|
-
];
|
|
237
|
-
|
|
238
|
-
test.each(routePrefixTestData)(
|
|
239
|
-
'creates and gets an api key with routePrefix: $routePrefix',
|
|
240
|
-
async ({ routePrefix, normalizedPath }) => {
|
|
241
|
-
const options: CreateApiKeyRouterOptions<unknown> = {
|
|
242
|
-
getUserId,
|
|
243
|
-
parseCreateApiKeyRequest,
|
|
244
|
-
issuer: new URL('https://example.com'),
|
|
245
|
-
aud: 'api-key',
|
|
246
|
-
db,
|
|
247
|
-
routePrefix,
|
|
248
|
-
};
|
|
249
|
-
|
|
250
|
-
const app = createApiKeyRouter(options);
|
|
251
|
-
|
|
252
|
-
const response = await app.fetch!(
|
|
253
|
-
castRequest(
|
|
254
|
-
new Request(`https://example.com${normalizedPath}`, {
|
|
255
|
-
method: 'POST',
|
|
256
|
-
})
|
|
257
|
-
),
|
|
258
|
-
{},
|
|
259
|
-
ctx
|
|
260
|
-
);
|
|
261
|
-
expect(response.status).toBe(200);
|
|
262
|
-
const body = (await response.json()) as { api_key: string; kid: string };
|
|
263
|
-
expect(body).toEqual({
|
|
264
|
-
api_key: expect.any(String),
|
|
265
|
-
kid: expect.any(String),
|
|
266
|
-
});
|
|
267
|
-
|
|
268
|
-
const { kid } = body;
|
|
269
|
-
const getResponse = await app.fetch!(
|
|
270
|
-
castRequest(
|
|
271
|
-
new Request(`https://example.com${normalizedPath}/${kid}`, {
|
|
272
|
-
method: 'GET',
|
|
273
|
-
})
|
|
274
|
-
),
|
|
275
|
-
{},
|
|
276
|
-
ctx
|
|
277
|
-
);
|
|
278
|
-
expect(getResponse.status).toBe(200);
|
|
279
|
-
const getBody = (await getResponse.json()) as any;
|
|
280
|
-
expect(getBody).toEqual(
|
|
281
|
-
expect.objectContaining({
|
|
282
|
-
kid,
|
|
283
|
-
user_id: userId,
|
|
284
|
-
revoked: false,
|
|
285
|
-
})
|
|
286
|
-
);
|
|
287
|
-
}
|
|
288
|
-
);
|
|
289
|
-
|
|
290
|
-
test.each(routePrefixTestData)(
|
|
291
|
-
'creates and gets an api key with claims from request body with routePrefix: $routePrefix',
|
|
292
|
-
async ({ routePrefix, normalizedPath }) => {
|
|
293
|
-
parseCreateApiKeyRequest.mockImplementation(async (request, env) => {
|
|
294
|
-
const body = await request.json();
|
|
295
|
-
return {
|
|
296
|
-
expiresAt: new Date(Date.now() + 1000 * 60 * 60 * 24 * 365),
|
|
297
|
-
claims: {
|
|
298
|
-
scopes: ['read', 'write'],
|
|
299
|
-
os: 'linux',
|
|
300
|
-
},
|
|
301
|
-
databaseMetadata: {
|
|
302
|
-
tags: ['red'],
|
|
303
|
-
},
|
|
304
|
-
};
|
|
305
|
-
});
|
|
306
|
-
|
|
307
|
-
const options: CreateApiKeyRouterOptions<unknown> = {
|
|
308
|
-
getUserId,
|
|
309
|
-
parseCreateApiKeyRequest,
|
|
310
|
-
issuer: new URL('https://example.com'),
|
|
311
|
-
aud: 'api-key',
|
|
312
|
-
db,
|
|
313
|
-
routePrefix,
|
|
314
|
-
};
|
|
315
|
-
|
|
316
|
-
const app = createApiKeyRouter(options);
|
|
317
|
-
|
|
318
|
-
const response = await app.fetch!(
|
|
319
|
-
castRequest(
|
|
320
|
-
new Request(`https://example.com${normalizedPath}`, {
|
|
321
|
-
method: 'POST',
|
|
322
|
-
headers: { 'Content-Type': 'application/json' },
|
|
323
|
-
body: JSON.stringify({
|
|
324
|
-
scopes: ['read', 'write'],
|
|
325
|
-
os: 'linux',
|
|
326
|
-
}),
|
|
327
|
-
})
|
|
328
|
-
),
|
|
329
|
-
{},
|
|
330
|
-
ctx
|
|
331
|
-
);
|
|
332
|
-
expect(response.status).toBe(200);
|
|
333
|
-
const body = (await response.json()) as { api_key: string; kid: string };
|
|
334
|
-
expect(body).toEqual({
|
|
335
|
-
api_key: expect.any(String),
|
|
336
|
-
kid: expect.any(String),
|
|
337
|
-
});
|
|
338
|
-
|
|
339
|
-
const { kid, api_key } = body;
|
|
340
|
-
const decoded = await jose.decodeJwt(api_key);
|
|
341
|
-
expect(decoded.scopes).toEqual(['read', 'write']);
|
|
342
|
-
expect(decoded.os).toBe('linux');
|
|
343
|
-
|
|
344
|
-
const getResponse = await app.fetch!(
|
|
345
|
-
castRequest(
|
|
346
|
-
new Request(`https://example.com${normalizedPath}/${kid}`, {
|
|
347
|
-
method: 'GET',
|
|
348
|
-
})
|
|
349
|
-
),
|
|
350
|
-
{},
|
|
351
|
-
ctx
|
|
352
|
-
);
|
|
353
|
-
expect(getResponse.status).toBe(200);
|
|
354
|
-
const getBody = await getResponse.json();
|
|
355
|
-
expect(getBody).toEqual(
|
|
356
|
-
expect.objectContaining({
|
|
357
|
-
kid,
|
|
358
|
-
user_id: userId,
|
|
359
|
-
revoked: false,
|
|
360
|
-
metadata: {
|
|
361
|
-
tags: ['red'],
|
|
362
|
-
},
|
|
363
|
-
})
|
|
364
|
-
);
|
|
365
|
-
}
|
|
366
|
-
);
|
|
367
|
-
|
|
368
|
-
test.each(routePrefixTestData)(
|
|
369
|
-
'/my with no api keys with routePrefix: $routePrefix',
|
|
370
|
-
async ({ routePrefix, normalizedPath }) => {
|
|
371
|
-
const options: CreateApiKeyRouterOptions<unknown> = {
|
|
372
|
-
getUserId,
|
|
373
|
-
parseCreateApiKeyRequest,
|
|
374
|
-
issuer: new URL('https://example.com'),
|
|
375
|
-
aud: 'api-key',
|
|
376
|
-
db,
|
|
377
|
-
routePrefix,
|
|
378
|
-
};
|
|
379
|
-
|
|
380
|
-
const app = createApiKeyRouter(options);
|
|
381
|
-
|
|
382
|
-
const response = await app.fetch!(
|
|
383
|
-
castRequest(
|
|
384
|
-
new Request(`https://example.com${normalizedPath}/my`, {
|
|
385
|
-
method: 'GET',
|
|
386
|
-
})
|
|
387
|
-
),
|
|
388
|
-
{},
|
|
389
|
-
ctx
|
|
390
|
-
);
|
|
391
|
-
expect(response.status).toBe(200);
|
|
392
|
-
const body = await response.json();
|
|
393
|
-
expect(body).toEqual([]);
|
|
394
|
-
}
|
|
395
|
-
);
|
|
396
|
-
|
|
397
|
-
test.each(routePrefixTestData)(
|
|
398
|
-
'/my with one api key with routePrefix: $routePrefix',
|
|
399
|
-
async ({ routePrefix, normalizedPath }) => {
|
|
400
|
-
const options: CreateApiKeyRouterOptions<unknown> = {
|
|
401
|
-
getUserId,
|
|
402
|
-
parseCreateApiKeyRequest,
|
|
403
|
-
issuer: new URL('https://example.com'),
|
|
404
|
-
aud: 'api-key',
|
|
405
|
-
db,
|
|
406
|
-
routePrefix,
|
|
407
|
-
};
|
|
408
|
-
|
|
409
|
-
const app = createApiKeyRouter(options);
|
|
410
|
-
|
|
411
|
-
const createResponse = await app.fetch!(
|
|
412
|
-
castRequest(
|
|
413
|
-
new Request(`https://example.com${normalizedPath}`, {
|
|
414
|
-
method: 'POST',
|
|
415
|
-
})
|
|
416
|
-
),
|
|
417
|
-
{},
|
|
418
|
-
ctx
|
|
419
|
-
);
|
|
420
|
-
expect(createResponse.status).toBe(200);
|
|
421
|
-
const createBody = (await createResponse.json()) as {
|
|
422
|
-
api_key: string;
|
|
423
|
-
kid: string;
|
|
424
|
-
};
|
|
425
|
-
const { kid } = createBody;
|
|
426
|
-
|
|
427
|
-
const response = await app.fetch!(
|
|
428
|
-
castRequest(
|
|
429
|
-
new Request(`https://example.com${normalizedPath}/my`, {
|
|
430
|
-
method: 'GET',
|
|
431
|
-
})
|
|
432
|
-
),
|
|
433
|
-
{},
|
|
434
|
-
ctx
|
|
435
|
-
);
|
|
436
|
-
expect(response.status).toBe(200);
|
|
437
|
-
const body = (await response.json()) as any[];
|
|
438
|
-
expect(body).toEqual([
|
|
439
|
-
expect.objectContaining({
|
|
440
|
-
kid,
|
|
441
|
-
user_id: userId,
|
|
442
|
-
revoked: false,
|
|
443
|
-
}),
|
|
444
|
-
]);
|
|
445
|
-
}
|
|
446
|
-
);
|
|
447
|
-
|
|
448
|
-
test.each(routePrefixTestData)(
|
|
449
|
-
'/my with two keys, one revoked with routePrefix: $routePrefix',
|
|
450
|
-
async ({ routePrefix, normalizedPath }) => {
|
|
451
|
-
const options: CreateApiKeyRouterOptions<unknown> = {
|
|
452
|
-
getUserId,
|
|
453
|
-
parseCreateApiKeyRequest,
|
|
454
|
-
issuer: new URL('https://example.com'),
|
|
455
|
-
aud: 'api-key',
|
|
456
|
-
db,
|
|
457
|
-
routePrefix,
|
|
458
|
-
};
|
|
459
|
-
|
|
460
|
-
const app = createApiKeyRouter(options);
|
|
461
|
-
|
|
462
|
-
const createResponse = await app.fetch!(
|
|
463
|
-
castRequest(
|
|
464
|
-
new Request(`https://example.com${normalizedPath}`, {
|
|
465
|
-
method: 'POST',
|
|
466
|
-
})
|
|
467
|
-
),
|
|
468
|
-
{},
|
|
469
|
-
ctx
|
|
470
|
-
);
|
|
471
|
-
expect(createResponse.status).toBe(200);
|
|
472
|
-
const createBody = (await createResponse.json()) as {
|
|
473
|
-
api_key: string;
|
|
474
|
-
kid: string;
|
|
475
|
-
};
|
|
476
|
-
const { kid } = createBody;
|
|
477
|
-
|
|
478
|
-
const revokeResponse = await app.fetch!(
|
|
479
|
-
castRequest(
|
|
480
|
-
new Request(`https://example.com${normalizedPath}/${kid}`, {
|
|
481
|
-
method: 'DELETE',
|
|
482
|
-
})
|
|
483
|
-
),
|
|
484
|
-
{},
|
|
485
|
-
ctx
|
|
486
|
-
);
|
|
487
|
-
expect(revokeResponse.status).toBe(200);
|
|
488
|
-
|
|
489
|
-
const createResponse2 = await app.fetch!(
|
|
490
|
-
castRequest(
|
|
491
|
-
new Request(`https://example.com${normalizedPath}`, {
|
|
492
|
-
method: 'POST',
|
|
493
|
-
})
|
|
494
|
-
),
|
|
495
|
-
{},
|
|
496
|
-
ctx
|
|
497
|
-
);
|
|
498
|
-
expect(createResponse2.status).toBe(200);
|
|
499
|
-
const createBody2 = (await createResponse2.json()) as {
|
|
500
|
-
api_key: string;
|
|
501
|
-
kid: string;
|
|
502
|
-
};
|
|
503
|
-
const { kid: kid2 } = createBody2;
|
|
504
|
-
|
|
505
|
-
const response = await app.fetch!(
|
|
506
|
-
castRequest(
|
|
507
|
-
new Request(`https://example.com${normalizedPath}/my`, {
|
|
508
|
-
method: 'GET',
|
|
509
|
-
})
|
|
510
|
-
),
|
|
511
|
-
{},
|
|
512
|
-
ctx
|
|
513
|
-
);
|
|
514
|
-
expect(response.status).toBe(200);
|
|
515
|
-
const body = (await response.json()) as any[];
|
|
516
|
-
expect(body).toEqual(
|
|
517
|
-
expect.arrayContaining([
|
|
518
|
-
expect.objectContaining({
|
|
519
|
-
kid,
|
|
520
|
-
user_id: userId,
|
|
521
|
-
revoked: true,
|
|
522
|
-
}),
|
|
523
|
-
expect.objectContaining({
|
|
524
|
-
kid: kid2,
|
|
525
|
-
user_id: userId,
|
|
526
|
-
revoked: false,
|
|
527
|
-
}),
|
|
528
|
-
])
|
|
529
|
-
);
|
|
530
|
-
}
|
|
531
|
-
);
|
|
532
|
-
|
|
533
|
-
test.each(routePrefixTestData)(
|
|
534
|
-
'/my rejects if auth fails with routePrefix: $routePrefix',
|
|
535
|
-
async ({ routePrefix, normalizedPath }) => {
|
|
536
|
-
getUserId.mockRejectedValue(new UnauthorizedError('auth failed'));
|
|
537
|
-
|
|
538
|
-
const options: CreateApiKeyRouterOptions<unknown> = {
|
|
539
|
-
getUserId,
|
|
540
|
-
parseCreateApiKeyRequest,
|
|
541
|
-
issuer: new URL('https://example.com'),
|
|
542
|
-
aud: 'api-key',
|
|
543
|
-
db,
|
|
544
|
-
routePrefix,
|
|
545
|
-
};
|
|
546
|
-
|
|
547
|
-
const app = createApiKeyRouter(options);
|
|
548
|
-
|
|
549
|
-
const response = await app.fetch!(
|
|
550
|
-
castRequest(
|
|
551
|
-
new Request(`https://example.com${normalizedPath}/my`, {
|
|
552
|
-
method: 'GET',
|
|
553
|
-
})
|
|
554
|
-
),
|
|
555
|
-
{},
|
|
556
|
-
ctx
|
|
557
|
-
);
|
|
558
|
-
expect(response.status).toBe(403);
|
|
559
|
-
}
|
|
560
|
-
);
|
|
561
|
-
|
|
562
|
-
test.each(routePrefixTestData)(
|
|
563
|
-
'get :id fails if auth fails with routePrefix: $routePrefix',
|
|
564
|
-
async ({ routePrefix, normalizedPath }) => {
|
|
565
|
-
const options: CreateApiKeyRouterOptions<unknown> = {
|
|
566
|
-
getUserId,
|
|
567
|
-
parseCreateApiKeyRequest,
|
|
568
|
-
issuer: new URL('https://example.com'),
|
|
569
|
-
aud: 'api-key',
|
|
570
|
-
db,
|
|
571
|
-
routePrefix,
|
|
572
|
-
};
|
|
573
|
-
|
|
574
|
-
const app = createApiKeyRouter(options);
|
|
575
|
-
|
|
576
|
-
const createResponse = await app.fetch!(
|
|
577
|
-
castRequest(
|
|
578
|
-
new Request(`https://example.com${normalizedPath}`, {
|
|
579
|
-
method: 'POST',
|
|
580
|
-
})
|
|
581
|
-
),
|
|
582
|
-
{},
|
|
583
|
-
ctx
|
|
584
|
-
);
|
|
585
|
-
expect(createResponse.status).toBe(200);
|
|
586
|
-
const createBody = (await createResponse.json()) as {
|
|
587
|
-
api_key: string;
|
|
588
|
-
kid: string;
|
|
589
|
-
};
|
|
590
|
-
const { kid } = createBody;
|
|
591
|
-
|
|
592
|
-
getUserId.mockRejectedValue(new UnauthorizedError('auth failed'));
|
|
593
|
-
const response = await app.fetch!(
|
|
594
|
-
castRequest(
|
|
595
|
-
new Request(`https://example.com${normalizedPath}/${kid}`, {
|
|
596
|
-
method: 'GET',
|
|
597
|
-
})
|
|
598
|
-
),
|
|
599
|
-
{},
|
|
600
|
-
ctx
|
|
601
|
-
);
|
|
602
|
-
expect(response.status).toBe(403);
|
|
603
|
-
}
|
|
604
|
-
);
|
|
605
|
-
|
|
606
|
-
test.each(routePrefixTestData)(
|
|
607
|
-
'get :id fails if the key does not belong to the user with routePrefix: $routePrefix',
|
|
608
|
-
async ({ routePrefix, normalizedPath }) => {
|
|
609
|
-
const options: CreateApiKeyRouterOptions<unknown> = {
|
|
610
|
-
getUserId,
|
|
611
|
-
parseCreateApiKeyRequest,
|
|
612
|
-
issuer: new URL('https://example.com'),
|
|
613
|
-
aud: 'api-key',
|
|
614
|
-
db,
|
|
615
|
-
routePrefix,
|
|
616
|
-
};
|
|
617
|
-
|
|
618
|
-
const app = createApiKeyRouter(options);
|
|
619
|
-
|
|
620
|
-
const createResponse = await app.fetch!(
|
|
621
|
-
castRequest(
|
|
622
|
-
new Request(`https://example.com${normalizedPath}`, {
|
|
623
|
-
method: 'POST',
|
|
624
|
-
})
|
|
625
|
-
),
|
|
626
|
-
{},
|
|
627
|
-
ctx
|
|
628
|
-
);
|
|
629
|
-
expect(createResponse.status).toBe(200);
|
|
630
|
-
const createBody = (await createResponse.json()) as {
|
|
631
|
-
api_key: string;
|
|
632
|
-
kid: string;
|
|
633
|
-
};
|
|
634
|
-
const { kid } = createBody;
|
|
635
|
-
|
|
636
|
-
getUserId.mockResolvedValue(uuidv4());
|
|
637
|
-
const response = await app.fetch!(
|
|
638
|
-
castRequest(
|
|
639
|
-
new Request(`https://example.com${normalizedPath}/${kid}`, {
|
|
640
|
-
method: 'GET',
|
|
641
|
-
})
|
|
642
|
-
),
|
|
643
|
-
{},
|
|
644
|
-
ctx
|
|
645
|
-
);
|
|
646
|
-
expect(response.status).toBe(404);
|
|
647
|
-
const body = await response.json();
|
|
648
|
-
expect(body).toEqual({
|
|
649
|
-
error: {
|
|
650
|
-
message: 'API key not found',
|
|
651
|
-
type: errorType.NOT_FOUND,
|
|
652
|
-
},
|
|
653
|
-
});
|
|
654
|
-
}
|
|
655
|
-
);
|
|
656
|
-
|
|
657
|
-
test.each(routePrefixTestData)(
|
|
658
|
-
'get :id fails if the key does not exist with routePrefix: $routePrefix',
|
|
659
|
-
async ({ routePrefix, normalizedPath }) => {
|
|
660
|
-
const options: CreateApiKeyRouterOptions<unknown> = {
|
|
661
|
-
getUserId,
|
|
662
|
-
parseCreateApiKeyRequest,
|
|
663
|
-
issuer: new URL('https://example.com'),
|
|
664
|
-
aud: 'api-key',
|
|
665
|
-
db,
|
|
666
|
-
routePrefix,
|
|
667
|
-
};
|
|
668
|
-
|
|
669
|
-
const app = createApiKeyRouter(options);
|
|
670
|
-
|
|
671
|
-
const response = await app.fetch!(
|
|
672
|
-
castRequest(
|
|
673
|
-
new Request(`https://example.com${normalizedPath}/${uuidv4()}`, {
|
|
674
|
-
method: 'GET',
|
|
675
|
-
})
|
|
676
|
-
),
|
|
677
|
-
{},
|
|
678
|
-
ctx
|
|
679
|
-
);
|
|
680
|
-
expect(response.status).toBe(404);
|
|
681
|
-
const body = await response.json();
|
|
682
|
-
expect(body).toEqual({
|
|
683
|
-
error: {
|
|
684
|
-
message: 'API key not found',
|
|
685
|
-
type: errorType.NOT_FOUND,
|
|
686
|
-
},
|
|
687
|
-
});
|
|
688
|
-
}
|
|
689
|
-
);
|
|
690
|
-
|
|
691
|
-
test.each(routePrefixTestData)(
|
|
692
|
-
'get :id fails if the key is not a uuid with routePrefix: $routePrefix',
|
|
693
|
-
async ({ routePrefix, normalizedPath }) => {
|
|
694
|
-
const options: CreateApiKeyRouterOptions<unknown> = {
|
|
695
|
-
getUserId,
|
|
696
|
-
parseCreateApiKeyRequest,
|
|
697
|
-
issuer: new URL('https://example.com'),
|
|
698
|
-
aud: 'api-key',
|
|
699
|
-
db,
|
|
700
|
-
routePrefix,
|
|
701
|
-
};
|
|
702
|
-
|
|
703
|
-
const app = createApiKeyRouter(options);
|
|
704
|
-
|
|
705
|
-
const response = await app.fetch!(
|
|
706
|
-
castRequest(
|
|
707
|
-
new Request(`https://example.com${normalizedPath}/not-a-uuid`, {
|
|
708
|
-
method: 'GET',
|
|
709
|
-
})
|
|
710
|
-
),
|
|
711
|
-
{},
|
|
712
|
-
ctx
|
|
713
|
-
);
|
|
714
|
-
expect(response.status).toBe(404);
|
|
715
|
-
const body = await response.json();
|
|
716
|
-
expect(body).toEqual({
|
|
717
|
-
error: {
|
|
718
|
-
message: 'Invalid API key ID',
|
|
719
|
-
type: errorType.NOT_FOUND,
|
|
720
|
-
},
|
|
721
|
-
});
|
|
722
|
-
}
|
|
723
|
-
);
|
|
724
|
-
|
|
725
|
-
test.each(routePrefixTestData)(
|
|
726
|
-
'creates and deletes an api key with routePrefix: $routePrefix',
|
|
727
|
-
async ({ routePrefix, normalizedPath }) => {
|
|
728
|
-
const options: CreateApiKeyRouterOptions<unknown> = {
|
|
729
|
-
getUserId,
|
|
730
|
-
parseCreateApiKeyRequest,
|
|
731
|
-
issuer: new URL('https://example.com'),
|
|
732
|
-
aud: 'api-key',
|
|
733
|
-
db,
|
|
734
|
-
routePrefix,
|
|
735
|
-
};
|
|
736
|
-
|
|
737
|
-
const app = createApiKeyRouter(options);
|
|
738
|
-
|
|
739
|
-
const createResponse = await app.fetch!(
|
|
740
|
-
castRequest(
|
|
741
|
-
new Request(`https://example.com${normalizedPath}`, {
|
|
742
|
-
method: 'POST',
|
|
743
|
-
})
|
|
744
|
-
),
|
|
745
|
-
{},
|
|
746
|
-
ctx
|
|
747
|
-
);
|
|
748
|
-
expect(createResponse.status).toBe(200);
|
|
749
|
-
const createBody = (await createResponse.json()) as {
|
|
750
|
-
api_key: string;
|
|
751
|
-
kid: string;
|
|
752
|
-
};
|
|
753
|
-
const { kid } = createBody;
|
|
754
|
-
|
|
755
|
-
const deleteResponse = await app.fetch!(
|
|
756
|
-
castRequest(
|
|
757
|
-
new Request(`https://example.com${normalizedPath}/${kid}`, {
|
|
758
|
-
method: 'DELETE',
|
|
759
|
-
})
|
|
760
|
-
),
|
|
761
|
-
{},
|
|
762
|
-
ctx
|
|
763
|
-
);
|
|
764
|
-
expect(deleteResponse.status).toBe(200);
|
|
765
|
-
|
|
766
|
-
const getResponse = await app.fetch!(
|
|
767
|
-
castRequest(
|
|
768
|
-
new Request(`https://example.com${normalizedPath}/${kid}`, {
|
|
769
|
-
method: 'GET',
|
|
770
|
-
})
|
|
771
|
-
),
|
|
772
|
-
{},
|
|
773
|
-
ctx
|
|
774
|
-
);
|
|
775
|
-
expect(getResponse.status).toBe(200);
|
|
776
|
-
const getBody = (await getResponse.json()) as any;
|
|
777
|
-
expect(getBody.revoked).toBe(true);
|
|
778
|
-
}
|
|
779
|
-
);
|
|
780
|
-
|
|
781
|
-
test.each(routePrefixTestData)(
|
|
782
|
-
'delete :id fails if not authenticated with routePrefix: $routePrefix',
|
|
783
|
-
async ({ routePrefix, normalizedPath }) => {
|
|
784
|
-
const options: CreateApiKeyRouterOptions<unknown> = {
|
|
785
|
-
getUserId,
|
|
786
|
-
parseCreateApiKeyRequest,
|
|
787
|
-
issuer: new URL('https://example.com'),
|
|
788
|
-
aud: 'api-key',
|
|
789
|
-
db,
|
|
790
|
-
routePrefix,
|
|
791
|
-
};
|
|
792
|
-
|
|
793
|
-
const app = createApiKeyRouter(options);
|
|
794
|
-
|
|
795
|
-
const createResponse = await app.fetch!(
|
|
796
|
-
castRequest(
|
|
797
|
-
new Request(`https://example.com${normalizedPath}`, {
|
|
798
|
-
method: 'POST',
|
|
799
|
-
})
|
|
800
|
-
),
|
|
801
|
-
{},
|
|
802
|
-
ctx
|
|
803
|
-
);
|
|
804
|
-
expect(createResponse.status).toBe(200);
|
|
805
|
-
const createBody = (await createResponse.json()) as {
|
|
806
|
-
api_key: string;
|
|
807
|
-
kid: string;
|
|
808
|
-
};
|
|
809
|
-
const { kid } = createBody;
|
|
810
|
-
|
|
811
|
-
getUserId.mockRejectedValue(new UnauthorizedError('auth failed'));
|
|
812
|
-
const response = await app.fetch!(
|
|
813
|
-
castRequest(
|
|
814
|
-
new Request(`https://example.com${normalizedPath}/${kid}`, {
|
|
815
|
-
method: 'DELETE',
|
|
816
|
-
})
|
|
817
|
-
),
|
|
818
|
-
{},
|
|
819
|
-
ctx
|
|
820
|
-
);
|
|
821
|
-
expect(response.status).toBe(403);
|
|
822
|
-
}
|
|
823
|
-
);
|
|
824
|
-
|
|
825
|
-
test.each(routePrefixTestData)(
|
|
826
|
-
'delete :id fails if the key does not belong to the user with routePrefix: $routePrefix',
|
|
827
|
-
async ({ routePrefix, normalizedPath }) => {
|
|
828
|
-
const options: CreateApiKeyRouterOptions<unknown> = {
|
|
829
|
-
getUserId,
|
|
830
|
-
parseCreateApiKeyRequest,
|
|
831
|
-
issuer: new URL('https://example.com'),
|
|
832
|
-
aud: 'api-key',
|
|
833
|
-
db,
|
|
834
|
-
routePrefix,
|
|
835
|
-
};
|
|
836
|
-
|
|
837
|
-
const app = createApiKeyRouter(options);
|
|
838
|
-
|
|
839
|
-
const createResponse = await app.fetch!(
|
|
840
|
-
castRequest(
|
|
841
|
-
new Request(`https://example.com${normalizedPath}`, {
|
|
842
|
-
method: 'POST',
|
|
843
|
-
})
|
|
844
|
-
),
|
|
845
|
-
{},
|
|
846
|
-
ctx
|
|
847
|
-
);
|
|
848
|
-
expect(createResponse.status).toBe(200);
|
|
849
|
-
const createBody = (await createResponse.json()) as {
|
|
850
|
-
api_key: string;
|
|
851
|
-
kid: string;
|
|
852
|
-
};
|
|
853
|
-
const { kid } = createBody;
|
|
854
|
-
|
|
855
|
-
getUserId.mockResolvedValue(uuidv4());
|
|
856
|
-
const response = await app.fetch!(
|
|
857
|
-
castRequest(
|
|
858
|
-
new Request(`https://example.com${normalizedPath}/${kid}`, {
|
|
859
|
-
method: 'DELETE',
|
|
860
|
-
})
|
|
861
|
-
),
|
|
862
|
-
{},
|
|
863
|
-
ctx
|
|
864
|
-
);
|
|
865
|
-
expect(response.status).toBe(404);
|
|
866
|
-
}
|
|
867
|
-
);
|
|
868
|
-
|
|
869
|
-
test.each(routePrefixTestData)(
|
|
870
|
-
'delete :id fails if the key does not exist with routePrefix: $routePrefix',
|
|
871
|
-
async ({ routePrefix, normalizedPath }) => {
|
|
872
|
-
const options: CreateApiKeyRouterOptions<unknown> = {
|
|
873
|
-
getUserId,
|
|
874
|
-
parseCreateApiKeyRequest,
|
|
875
|
-
issuer: new URL('https://example.com'),
|
|
876
|
-
aud: 'api-key',
|
|
877
|
-
db,
|
|
878
|
-
routePrefix,
|
|
879
|
-
};
|
|
880
|
-
|
|
881
|
-
const app = createApiKeyRouter(options);
|
|
882
|
-
|
|
883
|
-
const response = await app.fetch!(
|
|
884
|
-
castRequest(
|
|
885
|
-
new Request(`https://example.com${normalizedPath}/non-existent-kid`, {
|
|
886
|
-
method: 'DELETE',
|
|
887
|
-
})
|
|
888
|
-
),
|
|
889
|
-
{},
|
|
890
|
-
ctx
|
|
891
|
-
);
|
|
892
|
-
expect(response.status).toBe(404);
|
|
893
|
-
}
|
|
894
|
-
);
|
|
895
|
-
|
|
896
|
-
test.each(routePrefixTestData)(
|
|
897
|
-
'returns the user-defined error when authentication fails with routePrefix: $routePrefix',
|
|
898
|
-
async ({ routePrefix, normalizedPath }) => {
|
|
899
|
-
const expressError: any = new Error('tempest in a teapot');
|
|
900
|
-
expressError.statusCode = 418;
|
|
901
|
-
expressError.statusMessage = 'I am a teapot';
|
|
902
|
-
getUserId.mockRejectedValue(expressError);
|
|
903
|
-
|
|
904
|
-
const options: CreateApiKeyRouterOptions<unknown> = {
|
|
905
|
-
getUserId,
|
|
906
|
-
parseCreateApiKeyRequest,
|
|
907
|
-
issuer: new URL('https://example.com'),
|
|
908
|
-
aud: 'api-key',
|
|
909
|
-
db,
|
|
910
|
-
routePrefix,
|
|
911
|
-
};
|
|
912
|
-
|
|
913
|
-
const app = createApiKeyRouter(options);
|
|
914
|
-
|
|
915
|
-
const promise = app.fetch!(
|
|
916
|
-
castRequest(
|
|
917
|
-
new Request(`https://example.com${normalizedPath}/my`, {
|
|
918
|
-
method: 'GET',
|
|
919
|
-
})
|
|
920
|
-
),
|
|
921
|
-
{},
|
|
922
|
-
ctx
|
|
923
|
-
);
|
|
924
|
-
await expect(promise).rejects.toThrow('tempest in a teapot');
|
|
925
|
-
}
|
|
926
|
-
);
|
|
927
|
-
|
|
928
|
-
test.each(routePrefixTestData)(
|
|
929
|
-
'returns a japikeyerror when authentication fails with routePrefix: $routePrefix',
|
|
930
|
-
async ({ routePrefix, normalizedPath }) => {
|
|
931
|
-
const underlying = new Error('bad header');
|
|
932
|
-
getUserId.mockRejectedValue(
|
|
933
|
-
new UnauthorizedError('my custom error', { cause: underlying })
|
|
934
|
-
);
|
|
935
|
-
|
|
936
|
-
const options: CreateApiKeyRouterOptions<unknown> = {
|
|
937
|
-
getUserId,
|
|
938
|
-
parseCreateApiKeyRequest,
|
|
939
|
-
issuer: new URL('https://example.com'),
|
|
940
|
-
aud: 'api-key',
|
|
941
|
-
db,
|
|
942
|
-
routePrefix,
|
|
943
|
-
};
|
|
944
|
-
|
|
945
|
-
const app = createApiKeyRouter(options);
|
|
946
|
-
|
|
947
|
-
const response = await app.fetch!(
|
|
948
|
-
castRequest(
|
|
949
|
-
new Request(`https://example.com${normalizedPath}/my`, {
|
|
950
|
-
method: 'GET',
|
|
951
|
-
})
|
|
952
|
-
),
|
|
953
|
-
{},
|
|
954
|
-
ctx
|
|
955
|
-
);
|
|
956
|
-
expect(response.status).toBe(403);
|
|
957
|
-
const body = await response.json();
|
|
958
|
-
expect(body).toEqual({
|
|
959
|
-
error: {
|
|
960
|
-
message: 'my custom error',
|
|
961
|
-
type: errorType.UNAUTHORIZED,
|
|
962
|
-
},
|
|
963
|
-
});
|
|
964
|
-
}
|
|
965
|
-
);
|
|
966
|
-
|
|
967
|
-
test.each(routePrefixTestData)(
|
|
968
|
-
'throws an error if a non-japikey error is thrown with routePrefix: $routePrefix',
|
|
969
|
-
async ({ routePrefix, normalizedPath }) => {
|
|
970
|
-
getUserId.mockImplementation(() => {
|
|
971
|
-
throw new Error('not a japikey error');
|
|
972
|
-
});
|
|
973
|
-
|
|
974
|
-
const options: CreateApiKeyRouterOptions<unknown> = {
|
|
975
|
-
getUserId,
|
|
976
|
-
parseCreateApiKeyRequest,
|
|
977
|
-
issuer: new URL('https://example.com'),
|
|
978
|
-
aud: 'api-key',
|
|
979
|
-
db,
|
|
980
|
-
routePrefix,
|
|
981
|
-
};
|
|
982
|
-
|
|
983
|
-
const app = createApiKeyRouter(options);
|
|
984
|
-
|
|
985
|
-
const promise = app.fetch!(
|
|
986
|
-
castRequest(
|
|
987
|
-
new Request(`https://example.com${normalizedPath}/my`, {
|
|
988
|
-
method: 'GET',
|
|
989
|
-
})
|
|
990
|
-
),
|
|
991
|
-
{},
|
|
992
|
-
ctx
|
|
993
|
-
);
|
|
994
|
-
await expect(promise).rejects.toThrow('not a japikey error');
|
|
995
|
-
}
|
|
996
|
-
);
|
|
997
|
-
|
|
998
|
-
test.each(routePrefixTestData)(
|
|
999
|
-
'returns a 404 for a non-existent route with routePrefix: $routePrefix',
|
|
1000
|
-
async ({ routePrefix, normalizedPath }) => {
|
|
1001
|
-
const options: CreateApiKeyRouterOptions<unknown> = {
|
|
1002
|
-
getUserId,
|
|
1003
|
-
parseCreateApiKeyRequest,
|
|
1004
|
-
issuer: new URL('https://example.com'),
|
|
1005
|
-
aud: 'api-key',
|
|
1006
|
-
db,
|
|
1007
|
-
routePrefix,
|
|
1008
|
-
};
|
|
1009
|
-
|
|
1010
|
-
const app = createApiKeyRouter(options);
|
|
1011
|
-
|
|
1012
|
-
const response = await app.fetch!(
|
|
1013
|
-
castRequest(
|
|
1014
|
-
new Request('https://example.com/non-existent-route', {
|
|
1015
|
-
method: 'GET',
|
|
1016
|
-
})
|
|
1017
|
-
),
|
|
1018
|
-
{},
|
|
1019
|
-
ctx
|
|
1020
|
-
);
|
|
1021
|
-
expect(response.status).toBe(404);
|
|
1022
|
-
}
|
|
1023
|
-
);
|
|
1024
|
-
});
|
|
1025
|
-
|
|
1026
|
-
describe('isJWKSPath', () => {
|
|
1027
|
-
const baseIssuer = new URL('https://example.com');
|
|
1028
|
-
|
|
1029
|
-
test('returns true for valid JWKS path with UUID', () => {
|
|
1030
|
-
const request = new Request(
|
|
1031
|
-
'https://example.com/123e4567-e89b-12d3-a456-426614174000/.well-known/jwks.json'
|
|
1032
|
-
);
|
|
1033
|
-
expect(isJWKSPath(request, baseIssuer)).toBe(true);
|
|
1034
|
-
});
|
|
1035
|
-
|
|
1036
|
-
test('returns false for valid JWKS path with trailing slash', () => {
|
|
1037
|
-
const request = new Request(
|
|
1038
|
-
'https://example.com/123e4567-e89b-12d3-a456-426614174000/.well-known/jwks.json/'
|
|
1039
|
-
);
|
|
1040
|
-
expect(isJWKSPath(request, baseIssuer)).toBe(false);
|
|
1041
|
-
});
|
|
1042
|
-
|
|
1043
|
-
test('returns false for invalid UUID', () => {
|
|
1044
|
-
const request = new Request(
|
|
1045
|
-
'https://example.com/not-a-uuid/.well-known/jwks.json'
|
|
1046
|
-
);
|
|
1047
|
-
expect(isJWKSPath(request, baseIssuer)).toBe(false);
|
|
1048
|
-
});
|
|
1049
|
-
|
|
1050
|
-
test('returns false for wrong path structure', () => {
|
|
1051
|
-
const request = new Request(
|
|
1052
|
-
'https://example.com/123e4567-e89b-12d3-a456-426614174000/wrong-path/jwks.json'
|
|
1053
|
-
);
|
|
1054
|
-
expect(isJWKSPath(request, baseIssuer)).toBe(false);
|
|
1055
|
-
});
|
|
1056
|
-
|
|
1057
|
-
test('returns false for wrong issuer', () => {
|
|
1058
|
-
const request = new Request(
|
|
1059
|
-
'https://different.com/123e4567-e89b-12d3-a456-426614174000/.well-known/jwks.json'
|
|
1060
|
-
);
|
|
1061
|
-
expect(isJWKSPath(request, baseIssuer)).toBe(false);
|
|
1062
|
-
});
|
|
1063
|
-
|
|
1064
|
-
test('returns false for missing .well-known', () => {
|
|
1065
|
-
const request = new Request(
|
|
1066
|
-
'https://example.com/123e4567-e89b-12d3-a456-426614174000/jwks.json'
|
|
1067
|
-
);
|
|
1068
|
-
expect(isJWKSPath(request, baseIssuer)).toBe(false);
|
|
1069
|
-
});
|
|
1070
|
-
|
|
1071
|
-
test('returns false for wrong filename', () => {
|
|
1072
|
-
const request = new Request(
|
|
1073
|
-
'https://example.com/123e4567-e89b-12d3-a456-426614174000/.well-known/keys.json'
|
|
1074
|
-
);
|
|
1075
|
-
expect(isJWKSPath(request, baseIssuer)).toBe(false);
|
|
1076
|
-
});
|
|
1077
|
-
|
|
1078
|
-
test('returns false for extra path segments', () => {
|
|
1079
|
-
const request = new Request(
|
|
1080
|
-
'https://example.com/extra/123e4567-e89b-12d3-a456-426614174000/.well-known/jwks.json'
|
|
1081
|
-
);
|
|
1082
|
-
expect(isJWKSPath(request, baseIssuer)).toBe(false);
|
|
1083
|
-
});
|
|
1084
|
-
|
|
1085
|
-
test('returns false for empty kid', () => {
|
|
1086
|
-
const request = new Request('https://example.com//.well-known/jwks.json');
|
|
1087
|
-
expect(isJWKSPath(request, baseIssuer)).toBe(false);
|
|
1088
|
-
});
|
|
1089
|
-
|
|
1090
|
-
test('returns false for non-JWKS path', () => {
|
|
1091
|
-
const request = new Request('https://example.com/api/users');
|
|
1092
|
-
expect(isJWKSPath(request, baseIssuer)).toBe(false);
|
|
1093
|
-
});
|
|
1094
|
-
|
|
1095
|
-
test('returns false for root path', () => {
|
|
1096
|
-
const request = new Request('https://example.com/');
|
|
1097
|
-
expect(isJWKSPath(request, baseIssuer)).toBe(false);
|
|
1098
|
-
});
|
|
1099
|
-
|
|
1100
|
-
test('returns false for empty path', () => {
|
|
1101
|
-
const request = new Request('https://example.com');
|
|
1102
|
-
expect(isJWKSPath(request, baseIssuer)).toBe(false);
|
|
1103
|
-
});
|
|
1104
|
-
|
|
1105
|
-
test('returns true for valid JWKS path with different base issuer', () => {
|
|
1106
|
-
const customIssuer = new URL('https://api.custom.com');
|
|
1107
|
-
const request = new Request(
|
|
1108
|
-
'https://api.custom.com/123e4567-e89b-12d3-a456-426614174000/.well-known/jwks.json'
|
|
1109
|
-
);
|
|
1110
|
-
expect(isJWKSPath(request, customIssuer)).toBe(true);
|
|
1111
|
-
});
|
|
1112
|
-
|
|
1113
|
-
test('returns false for valid JWKS path with wrong base issuer', () => {
|
|
1114
|
-
const customIssuer = new URL('https://api.custom.com');
|
|
1115
|
-
const request = new Request(
|
|
1116
|
-
'https://example.com/123e4567-e89b-12d3-a456-426614174000/.well-known/jwks.json'
|
|
1117
|
-
);
|
|
1118
|
-
expect(isJWKSPath(request, customIssuer)).toBe(false);
|
|
1119
|
-
});
|
|
1120
|
-
});
|