two-stroke 7.9.0 → 7.10.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.
- package/package.json +3 -5
- package/src/index.ts +25 -8
- package/src/util.ts +77 -0
- package/worker-configuration.d.ts +12101 -13317
package/package.json
CHANGED
|
@@ -12,7 +12,7 @@
|
|
|
12
12
|
"dependencies": {
|
|
13
13
|
"@cloudflare/vitest-pool-workers": "^0.18.0",
|
|
14
14
|
"@sentry/cli": "^3.6.0",
|
|
15
|
-
"@typescript-eslint/eslint-plugin": "^8.
|
|
15
|
+
"@typescript-eslint/eslint-plugin": "^8.63.0",
|
|
16
16
|
"@typescript-eslint/parser": "^8.62.1",
|
|
17
17
|
"@vitest/runner": "^4.1.8",
|
|
18
18
|
"@vitest/snapshot": "^4.1.8",
|
|
@@ -20,13 +20,11 @@
|
|
|
20
20
|
"eslint-config-two-stroke": "^1.5.7",
|
|
21
21
|
"eslint-config-typescript": "^3.0.0",
|
|
22
22
|
"jose": "^6.2.3",
|
|
23
|
-
"jwk-subtle": "^1.1.8",
|
|
24
23
|
"mime": "^4.1.0",
|
|
25
24
|
"miniflare": "^4.20260701.0",
|
|
26
25
|
"msw": "^2.14.6",
|
|
27
26
|
"openapi-fetch": "^0.17.0",
|
|
28
27
|
"openapi-typescript": "^7.13.0",
|
|
29
|
-
"pbkdf-subtle": "^1.1.8",
|
|
30
28
|
"toucan-js": "^4.1.1",
|
|
31
29
|
"zod": "^4.4.3"
|
|
32
30
|
},
|
|
@@ -58,12 +56,12 @@
|
|
|
58
56
|
"url": "https://github.com/change-engine/two-stroke"
|
|
59
57
|
},
|
|
60
58
|
"type": "module",
|
|
61
|
-
"version": "7.
|
|
59
|
+
"version": "7.10.1",
|
|
62
60
|
"devDependencies": {
|
|
63
61
|
"@types/eslint": "^9.6.1",
|
|
64
62
|
"eslint": "^10.6.0",
|
|
65
63
|
"prettier": "^3.9.4",
|
|
66
64
|
"typescript": "^6.0.3",
|
|
67
|
-
"vitest": "^4.1.
|
|
65
|
+
"vitest": "^4.1.10"
|
|
68
66
|
}
|
|
69
67
|
}
|
package/src/index.ts
CHANGED
|
@@ -1,9 +1,9 @@
|
|
|
1
|
-
import { verify as jwkVerify } from "jwk-subtle";
|
|
2
|
-
import { verify as pbkdfVerify } from "pbkdf-subtle";
|
|
3
1
|
import { Toucan } from "toucan-js";
|
|
4
2
|
import { type ZodSafeParseResult, z, ZodObject, ZodType } from "zod/v4";
|
|
5
3
|
import { openAPI } from "./open-api";
|
|
6
4
|
import { type Handler, type Route } from "./types";
|
|
5
|
+
import type { JSONWebKeySet } from "jose";
|
|
6
|
+
import { pbkdfVerify, jwkVerifyMulti } from "./util";
|
|
7
7
|
|
|
8
8
|
// eslint-disable-next-line @typescript-eslint/require-await
|
|
9
9
|
const noAuth = async () => null;
|
|
@@ -301,7 +301,7 @@ export function twoStroke<T>(
|
|
|
301
301
|
},
|
|
302
302
|
noAuth,
|
|
303
303
|
pbkdf:
|
|
304
|
-
(k: keyof T, customHeaderName
|
|
304
|
+
(k: keyof T, customHeaderName = "Authorization") =>
|
|
305
305
|
async ({ req, env }: { req: Request; env: T }) => {
|
|
306
306
|
const [scheme, token] = (req.headers.get(customHeaderName) ?? " ").split(" ");
|
|
307
307
|
if (
|
|
@@ -309,20 +309,28 @@ export function twoStroke<T>(
|
|
|
309
309
|
(await pbkdfVerify(env[k] as string, token ?? ""))
|
|
310
310
|
)
|
|
311
311
|
return;
|
|
312
|
-
throw Error("Invalid");
|
|
312
|
+
throw new Error("Invalid");
|
|
313
313
|
},
|
|
314
314
|
jwt:
|
|
315
315
|
<J>(k: keyof T, ak: keyof T) =>
|
|
316
316
|
async ({ req, env }: { req: Request; env: T }) => {
|
|
317
317
|
const [scheme, token] = (req.headers.get("Authorization") ?? " ").split(" ");
|
|
318
318
|
if (scheme === "Bearer") {
|
|
319
|
-
const
|
|
319
|
+
const rawIssuer = env[k] as string;
|
|
320
|
+
const rawAudience = env[ak] as string;
|
|
321
|
+
const issuer = rawIssuer.startsWith("{")
|
|
322
|
+
? (JSON.parse(rawIssuer) as Record<string, JSONWebKeySet | true>)
|
|
323
|
+
: { [rawIssuer]: true as const };
|
|
324
|
+
const audience = rawAudience.startsWith("[")
|
|
325
|
+
? (JSON.parse(rawAudience) as string[])
|
|
326
|
+
: [rawAudience];
|
|
327
|
+
const claims = await jwkVerifyMulti<J>(token ?? "", issuer, audience);
|
|
320
328
|
if (!claims) {
|
|
321
|
-
throw Error("Invalid");
|
|
329
|
+
throw new Error("Invalid");
|
|
322
330
|
}
|
|
323
331
|
return claims;
|
|
324
332
|
}
|
|
325
|
-
throw Error("Invalid");
|
|
333
|
+
throw new Error("Invalid");
|
|
326
334
|
},
|
|
327
335
|
queueHandler<I extends ZodType>(
|
|
328
336
|
input: I,
|
|
@@ -353,12 +361,20 @@ export function twoStroke<T>(
|
|
|
353
361
|
console.log("Queue batch finished");
|
|
354
362
|
};
|
|
355
363
|
},
|
|
356
|
-
put<
|
|
364
|
+
put<
|
|
365
|
+
I extends ZodType | undefined,
|
|
366
|
+
O extends ZodType,
|
|
367
|
+
A,
|
|
368
|
+
P extends string,
|
|
369
|
+
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
370
|
+
PP extends ZodObject<any> | undefined,
|
|
371
|
+
>(
|
|
357
372
|
auth: Route<T, A>["auth"],
|
|
358
373
|
path: P,
|
|
359
374
|
input: I,
|
|
360
375
|
output: O,
|
|
361
376
|
handler: Handler<T, I, O, A, P>,
|
|
377
|
+
params?: PP,
|
|
362
378
|
) {
|
|
363
379
|
routes.push({
|
|
364
380
|
auth,
|
|
@@ -368,6 +384,7 @@ export function twoStroke<T>(
|
|
|
368
384
|
input,
|
|
369
385
|
output,
|
|
370
386
|
handler,
|
|
387
|
+
params,
|
|
371
388
|
});
|
|
372
389
|
},
|
|
373
390
|
|
package/src/util.ts
ADDED
|
@@ -0,0 +1,77 @@
|
|
|
1
|
+
import {
|
|
2
|
+
type FlattenedJWSInput,
|
|
3
|
+
type JSONWebKeySet,
|
|
4
|
+
type JWSHeaderParameters,
|
|
5
|
+
createLocalJWKSet,
|
|
6
|
+
createRemoteJWKSet,
|
|
7
|
+
decodeJwt,
|
|
8
|
+
jwtVerify,
|
|
9
|
+
} from "jose";
|
|
10
|
+
|
|
11
|
+
export async function pbkdfVerify(key: string, password: string) {
|
|
12
|
+
return (
|
|
13
|
+
[
|
|
14
|
+
...new Uint8Array(
|
|
15
|
+
await crypto.subtle.deriveBits(
|
|
16
|
+
{
|
|
17
|
+
name: "PBKDF2",
|
|
18
|
+
hash: "SHA-256",
|
|
19
|
+
salt: new Uint8Array([...atob(key).slice(3, 19)].map((ch) => ch.charCodeAt(0))),
|
|
20
|
+
iterations: parseInt(
|
|
21
|
+
[...atob(key).slice(19, 22)].map((ch) => ch.charCodeAt(0).toString(16)).join(""),
|
|
22
|
+
16,
|
|
23
|
+
),
|
|
24
|
+
},
|
|
25
|
+
await crypto.subtle.importKey(
|
|
26
|
+
"raw",
|
|
27
|
+
new TextEncoder().encode(password),
|
|
28
|
+
"PBKDF2",
|
|
29
|
+
false,
|
|
30
|
+
["deriveBits"],
|
|
31
|
+
),
|
|
32
|
+
256,
|
|
33
|
+
),
|
|
34
|
+
),
|
|
35
|
+
]
|
|
36
|
+
.map((byte) => String.fromCharCode(byte))
|
|
37
|
+
.join("") === atob(key).slice(22, 54)
|
|
38
|
+
);
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
export async function jwkVerifyMulti<J>(
|
|
42
|
+
token: string,
|
|
43
|
+
issuers: Record<string, JSONWebKeySet | true>,
|
|
44
|
+
audiences: string[],
|
|
45
|
+
) {
|
|
46
|
+
const unverifiedClaims = decodeJwt(token);
|
|
47
|
+
if (!unverifiedClaims.iss) throw new Error("Invalid");
|
|
48
|
+
const issuer = issuers[unverifiedClaims.iss];
|
|
49
|
+
if (!issuer) throw new Error("Invalid");
|
|
50
|
+
|
|
51
|
+
let JWKS: (
|
|
52
|
+
protectedHeader?: JWSHeaderParameters,
|
|
53
|
+
token?: FlattenedJWSInput,
|
|
54
|
+
) => Promise<CryptoKey>;
|
|
55
|
+
|
|
56
|
+
if (issuer === true) {
|
|
57
|
+
// OIDC
|
|
58
|
+
const oidcRequest = await fetch(
|
|
59
|
+
`${unverifiedClaims.iss}${unverifiedClaims.iss.endsWith("/") ? "" : "/"}.well-known/openid-configuration`,
|
|
60
|
+
);
|
|
61
|
+
const oidc = await oidcRequest.json<{ jwks_uri: string }>();
|
|
62
|
+
JWKS = createRemoteJWKSet(new URL(oidc.jwks_uri));
|
|
63
|
+
} else {
|
|
64
|
+
JWKS = createLocalJWKSet(issuer);
|
|
65
|
+
}
|
|
66
|
+
|
|
67
|
+
const claims = (
|
|
68
|
+
await jwtVerify<J>(token ?? "", JWKS, {
|
|
69
|
+
algorithms: ["RS256", "ES256"],
|
|
70
|
+
audience: audiences,
|
|
71
|
+
})
|
|
72
|
+
).payload;
|
|
73
|
+
if (!claims) {
|
|
74
|
+
throw new Error("Invalid");
|
|
75
|
+
}
|
|
76
|
+
return claims;
|
|
77
|
+
}
|