@yielded/jose 0.1.0-beta.19
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/README.md +30 -0
- package/THIRD_PARTY_NOTICES.md +53 -0
- package/dist/Errors.d.mts +33 -0
- package/dist/Errors.mjs +49 -0
- package/dist/Errors.mjs.map +1 -0
- package/dist/Jwe.d.mts +35 -0
- package/dist/Jwe.mjs +66 -0
- package/dist/Jwe.mjs.map +1 -0
- package/dist/Jwk.d.mts +273 -0
- package/dist/Jwk.mjs +195 -0
- package/dist/Jwk.mjs.map +1 -0
- package/dist/Jwks.d.mts +71 -0
- package/dist/Jwks.mjs +169 -0
- package/dist/Jwks.mjs.map +1 -0
- package/dist/Jws.d.mts +53 -0
- package/dist/Jws.mjs +108 -0
- package/dist/Jws.mjs.map +1 -0
- package/dist/Jwt.d.mts +69 -0
- package/dist/Jwt.mjs +145 -0
- package/dist/Jwt.mjs.map +1 -0
- package/dist/index.d.mts +7 -0
- package/dist/index.mjs +7 -0
- package/dist/internal/algorithms.mjs +11 -0
- package/dist/internal/algorithms.mjs.map +1 -0
- package/dist/internal/encoding.mjs +39 -0
- package/dist/internal/encoding.mjs.map +1 -0
- package/dist/internal/keyInput.mjs +44 -0
- package/dist/internal/keyInput.mjs.map +1 -0
- package/dist/internal/keyPolicy.mjs +11 -0
- package/dist/internal/keyPolicy.mjs.map +1 -0
- package/package.json +1 -0
- package/src/Errors.ts +55 -0
- package/src/Jwe.ts +117 -0
- package/src/Jwk.ts +269 -0
- package/src/Jwks.ts +366 -0
- package/src/Jws.ts +206 -0
- package/src/Jwt.ts +187 -0
- package/src/index.ts +6 -0
- package/src/internal/algorithms.ts +10 -0
- package/src/internal/encoding.ts +72 -0
- package/src/internal/keyInput.ts +75 -0
- package/src/internal/keyPolicy.ts +18 -0
package/dist/Jwk.mjs
ADDED
|
@@ -0,0 +1,195 @@
|
|
|
1
|
+
import { InvalidKey } from "./Errors.mjs";
|
|
2
|
+
import { algorithms } from "./internal/algorithms.mjs";
|
|
3
|
+
import { decode, reveal } from "./internal/encoding.mjs";
|
|
4
|
+
import { detach, jwk } from "./internal/keyInput.mjs";
|
|
5
|
+
import { Effect, Redacted, Schema } from "effect";
|
|
6
|
+
import * as Signature$1 from "@yielded/crypto/Signature";
|
|
7
|
+
//#region src/Jwk.ts
|
|
8
|
+
const AsymmetricAlgorithm = Schema.Literals([
|
|
9
|
+
"RS256",
|
|
10
|
+
"PS256",
|
|
11
|
+
"ES256",
|
|
12
|
+
"EdDSA"
|
|
13
|
+
]);
|
|
14
|
+
const Algorithm = Schema.Literals([
|
|
15
|
+
"HS256",
|
|
16
|
+
"RS256",
|
|
17
|
+
"PS256",
|
|
18
|
+
"ES256",
|
|
19
|
+
"EdDSA"
|
|
20
|
+
]);
|
|
21
|
+
const encodedBytes = Schema.String.check(Schema.isPattern(/^[A-Za-z0-9_-]+$/), Schema.isMaxLength(16384));
|
|
22
|
+
const operations = Schema.Array(Schema.NonEmptyString).check(Schema.isMaxLength(256), Schema.makeFilter((values) => new Set(values).size === values.length));
|
|
23
|
+
const metadata = {
|
|
24
|
+
kid: Schema.optionalKey(Schema.String.check(Schema.isMaxLength(256))),
|
|
25
|
+
alg: Schema.optionalKey(Schema.NonEmptyString),
|
|
26
|
+
use: Schema.optionalKey(Schema.Literals(["sig", "enc"])),
|
|
27
|
+
key_ops: Schema.optionalKey(operations)
|
|
28
|
+
};
|
|
29
|
+
const rsa = {
|
|
30
|
+
...metadata,
|
|
31
|
+
kty: Schema.Literal("RSA"),
|
|
32
|
+
n: encodedBytes,
|
|
33
|
+
e: encodedBytes
|
|
34
|
+
};
|
|
35
|
+
const ec = {
|
|
36
|
+
...metadata,
|
|
37
|
+
kty: Schema.Literal("EC"),
|
|
38
|
+
crv: Schema.Literal("P-256"),
|
|
39
|
+
x: encodedBytes,
|
|
40
|
+
y: encodedBytes
|
|
41
|
+
};
|
|
42
|
+
const okp = {
|
|
43
|
+
...metadata,
|
|
44
|
+
kty: Schema.Literal("OKP"),
|
|
45
|
+
crv: Schema.Literal("Ed25519"),
|
|
46
|
+
x: encodedBytes
|
|
47
|
+
};
|
|
48
|
+
const noSecret = {
|
|
49
|
+
d: Schema.optionalKey(Schema.Never),
|
|
50
|
+
k: Schema.optionalKey(Schema.Never)
|
|
51
|
+
};
|
|
52
|
+
const PublicJwk = Schema.Union([
|
|
53
|
+
Schema.Struct({
|
|
54
|
+
...rsa,
|
|
55
|
+
...noSecret
|
|
56
|
+
}),
|
|
57
|
+
Schema.Struct({
|
|
58
|
+
...ec,
|
|
59
|
+
...noSecret
|
|
60
|
+
}),
|
|
61
|
+
Schema.Struct({
|
|
62
|
+
...okp,
|
|
63
|
+
...noSecret
|
|
64
|
+
})
|
|
65
|
+
]);
|
|
66
|
+
const PrivateJwk = Schema.Union([
|
|
67
|
+
Schema.Struct({
|
|
68
|
+
...rsa,
|
|
69
|
+
d: encodedBytes,
|
|
70
|
+
p: encodedBytes,
|
|
71
|
+
q: encodedBytes,
|
|
72
|
+
dp: encodedBytes,
|
|
73
|
+
dq: encodedBytes,
|
|
74
|
+
qi: encodedBytes,
|
|
75
|
+
oth: Schema.optionalKey(Schema.Never)
|
|
76
|
+
}),
|
|
77
|
+
Schema.Struct({
|
|
78
|
+
...ec,
|
|
79
|
+
d: encodedBytes
|
|
80
|
+
}),
|
|
81
|
+
Schema.Struct({
|
|
82
|
+
...okp,
|
|
83
|
+
d: encodedBytes
|
|
84
|
+
})
|
|
85
|
+
]);
|
|
86
|
+
const SecretJwk = Schema.Struct({
|
|
87
|
+
...metadata,
|
|
88
|
+
kty: Schema.Literal("oct"),
|
|
89
|
+
k: encodedBytes
|
|
90
|
+
});
|
|
91
|
+
const read = (schema, input) => Effect.try({
|
|
92
|
+
try: () => jwk(input),
|
|
93
|
+
catch: () => InvalidKey.make({})
|
|
94
|
+
}).pipe(Effect.flatMap(Effect.fromResult), Effect.flatMap((captured) => Schema.decodeUnknownEffect(schema)(captured, { reportInput: false }).pipe(Effect.mapError(() => InvalidKey.make({})))), Effect.map(detach));
|
|
95
|
+
const bytes = (value) => decode(value).pipe(Effect.mapError(() => InvalidKey.make({})));
|
|
96
|
+
const snapshot = (jwk) => {
|
|
97
|
+
if (jwk.key_ops !== void 0) Object.freeze(jwk.key_ops);
|
|
98
|
+
return Object.freeze(jwk);
|
|
99
|
+
};
|
|
100
|
+
const publicParameters = Effect.fnUntraced(function* (jwk, algorithm) {
|
|
101
|
+
if (jwk.alg !== void 0 && jwk.alg !== algorithm) return yield* InvalidKey.make({});
|
|
102
|
+
switch (jwk.kty) {
|
|
103
|
+
case "EC":
|
|
104
|
+
if (algorithm !== "ES256") return yield* InvalidKey.make({});
|
|
105
|
+
return {
|
|
106
|
+
algorithm: "ECDSA-P256-SHA256",
|
|
107
|
+
x: yield* bytes(jwk.x),
|
|
108
|
+
y: yield* bytes(jwk.y)
|
|
109
|
+
};
|
|
110
|
+
case "OKP":
|
|
111
|
+
if (algorithm !== "EdDSA") return yield* InvalidKey.make({});
|
|
112
|
+
return {
|
|
113
|
+
algorithm: "Ed25519",
|
|
114
|
+
x: yield* bytes(jwk.x)
|
|
115
|
+
};
|
|
116
|
+
case "RSA":
|
|
117
|
+
if (algorithm !== "RS256" && algorithm !== "PS256") return yield* InvalidKey.make({});
|
|
118
|
+
return {
|
|
119
|
+
algorithm: algorithms[algorithm],
|
|
120
|
+
n: yield* bytes(jwk.n),
|
|
121
|
+
e: yield* bytes(jwk.e)
|
|
122
|
+
};
|
|
123
|
+
}
|
|
124
|
+
});
|
|
125
|
+
const importPublic = Effect.fnUntraced(function* (input, selected) {
|
|
126
|
+
const algorithm = yield* read(AsymmetricAlgorithm, selected);
|
|
127
|
+
const jwk = yield* read(PublicJwk, input);
|
|
128
|
+
const parameters = yield* publicParameters(jwk, algorithm);
|
|
129
|
+
const material = yield* (yield* Signature$1.Signature).encodePublicKey(parameters).pipe(Effect.catchTag("CryptoInvalidInput", () => InvalidKey.make({})));
|
|
130
|
+
return Object.freeze({
|
|
131
|
+
_tag: "PublicKey",
|
|
132
|
+
algorithm,
|
|
133
|
+
jwk: snapshot(jwk),
|
|
134
|
+
material
|
|
135
|
+
});
|
|
136
|
+
});
|
|
137
|
+
const importPrivate = Effect.fnUntraced(function* (input, selected) {
|
|
138
|
+
const algorithm = yield* read(AsymmetricAlgorithm, selected);
|
|
139
|
+
const jwk = yield* read(PrivateJwk, yield* reveal(input).pipe(Effect.mapError(() => InvalidKey.make({}))));
|
|
140
|
+
const publicPart = yield* publicParameters(jwk, algorithm);
|
|
141
|
+
const d = yield* bytes(jwk.d);
|
|
142
|
+
const parameters = yield* read(Signature$1.PrivateKeyParameters, {
|
|
143
|
+
...publicPart,
|
|
144
|
+
d,
|
|
145
|
+
...jwk.kty === "RSA" ? {
|
|
146
|
+
p: yield* bytes(jwk.p),
|
|
147
|
+
q: yield* bytes(jwk.q),
|
|
148
|
+
dp: yield* bytes(jwk.dp),
|
|
149
|
+
dq: yield* bytes(jwk.dq),
|
|
150
|
+
qi: yield* bytes(jwk.qi)
|
|
151
|
+
} : {}
|
|
152
|
+
});
|
|
153
|
+
const material = yield* (yield* Signature$1.Signature).encodePrivateKey(Redacted.make(parameters)).pipe(Effect.catchTag("CryptoInvalidInput", () => InvalidKey.make({})), Effect.ensuring(Effect.sync(() => {
|
|
154
|
+
parameters.d.fill(0);
|
|
155
|
+
if ("p" in parameters) for (const part of [
|
|
156
|
+
parameters.p,
|
|
157
|
+
parameters.q,
|
|
158
|
+
parameters.dp,
|
|
159
|
+
parameters.dq,
|
|
160
|
+
parameters.qi
|
|
161
|
+
]) part.fill(0);
|
|
162
|
+
})));
|
|
163
|
+
return Object.freeze({
|
|
164
|
+
_tag: "PrivateKey",
|
|
165
|
+
algorithm,
|
|
166
|
+
jwk: Redacted.make(snapshot(jwk)),
|
|
167
|
+
material
|
|
168
|
+
});
|
|
169
|
+
});
|
|
170
|
+
const importSecret = Effect.fnUntraced(function* (input, selected) {
|
|
171
|
+
const algorithm = yield* read(Schema.Literals(["HS256", "dir"]), selected);
|
|
172
|
+
const jwk = yield* read(SecretJwk, yield* reveal(input).pipe(Effect.mapError(() => InvalidKey.make({}))));
|
|
173
|
+
const material = yield* bytes(jwk.k);
|
|
174
|
+
if (jwk.alg !== void 0 && jwk.alg !== algorithm || material.length < 32 || algorithm === "dir" && material.length !== 32) {
|
|
175
|
+
material.fill(0);
|
|
176
|
+
return yield* InvalidKey.make({});
|
|
177
|
+
}
|
|
178
|
+
return Object.freeze({
|
|
179
|
+
_tag: "SecretKey",
|
|
180
|
+
algorithm,
|
|
181
|
+
jwk: Redacted.make(snapshot(jwk)),
|
|
182
|
+
material: Redacted.make(material)
|
|
183
|
+
});
|
|
184
|
+
});
|
|
185
|
+
const exportPublic = Effect.fnUntraced(function* (key) {
|
|
186
|
+
return yield* read(PublicJwk, key.jwk);
|
|
187
|
+
});
|
|
188
|
+
const exportPrivate = Effect.fnUntraced(function* (key) {
|
|
189
|
+
const value = yield* reveal(key.jwk).pipe(Effect.mapError(() => InvalidKey.make({})));
|
|
190
|
+
return Redacted.make(yield* read(Schema.Union([PrivateJwk, SecretJwk]), value));
|
|
191
|
+
});
|
|
192
|
+
//#endregion
|
|
193
|
+
export { Algorithm, AsymmetricAlgorithm, PrivateJwk, PublicJwk, SecretJwk, exportPrivate, exportPublic, importPrivate, importPublic, importSecret };
|
|
194
|
+
|
|
195
|
+
//# sourceMappingURL=Jwk.mjs.map
|
package/dist/Jwk.mjs.map
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"Jwk.mjs","names":["KeyInput.jwk","KeyInput.detach","Signature"],"sources":["../src/Jwk.ts"],"sourcesContent":["import * as Signature from \"@yielded/crypto/Signature\";\nimport { Effect, Redacted, Schema } from \"effect\";\n\nimport { InvalidKey } from \"./Errors\";\nimport { algorithms } from \"./internal/algorithms\";\nimport { decode, reveal } from \"./internal/encoding\";\nimport * as KeyInput from \"./internal/keyInput\";\n\nexport const AsymmetricAlgorithm = Schema.Literals([\"RS256\", \"PS256\", \"ES256\", \"EdDSA\"]);\nexport type AsymmetricAlgorithm = typeof AsymmetricAlgorithm.Type;\nexport const Algorithm = Schema.Literals([\"HS256\", \"RS256\", \"PS256\", \"ES256\", \"EdDSA\"]);\nexport type Algorithm = typeof Algorithm.Type;\n\nconst encodedBytes = Schema.String.check(\n Schema.isPattern(/^[A-Za-z0-9_-]+$/),\n Schema.isMaxLength(16384),\n);\n\nconst operations = Schema.Array(Schema.NonEmptyString).check(\n Schema.isMaxLength(KeyInput.maxArrayLength),\n Schema.makeFilter((values) => new Set(values).size === values.length),\n);\n\nconst metadata = {\n kid: Schema.optionalKey(Schema.String.check(Schema.isMaxLength(256))),\n alg: Schema.optionalKey(Schema.NonEmptyString),\n use: Schema.optionalKey(Schema.Literals([\"sig\", \"enc\"])),\n key_ops: Schema.optionalKey(operations),\n};\n\nconst rsa = { ...metadata, kty: Schema.Literal(\"RSA\"), n: encodedBytes, e: encodedBytes };\n\nconst ec = {\n ...metadata,\n kty: Schema.Literal(\"EC\"),\n crv: Schema.Literal(\"P-256\"),\n x: encodedBytes,\n y: encodedBytes,\n};\n\nconst okp = {\n ...metadata,\n kty: Schema.Literal(\"OKP\"),\n crv: Schema.Literal(\"Ed25519\"),\n x: encodedBytes,\n};\n\nconst noSecret = { d: Schema.optionalKey(Schema.Never), k: Schema.optionalKey(Schema.Never) };\n\nexport const PublicJwk = Schema.Union([\n Schema.Struct({ ...rsa, ...noSecret }),\n Schema.Struct({ ...ec, ...noSecret }),\n Schema.Struct({ ...okp, ...noSecret }),\n]);\n\nexport type PublicJwk = typeof PublicJwk.Type;\n\nexport const PrivateJwk = Schema.Union([\n Schema.Struct({\n ...rsa,\n d: encodedBytes,\n p: encodedBytes,\n q: encodedBytes,\n dp: encodedBytes,\n dq: encodedBytes,\n qi: encodedBytes,\n oth: Schema.optionalKey(Schema.Never),\n }),\n Schema.Struct({ ...ec, d: encodedBytes }),\n Schema.Struct({ ...okp, d: encodedBytes }),\n]);\n\nexport type PrivateJwk = typeof PrivateJwk.Type;\n\nexport const SecretJwk = Schema.Struct({\n ...metadata,\n kty: Schema.Literal(\"oct\"),\n k: encodedBytes,\n});\n\nexport type SecretJwk = typeof SecretJwk.Type;\n\n/** Validated, algorithm-bound key snapshots. Private material remains Redacted. */\nexport interface PublicKey {\n readonly _tag: \"PublicKey\";\n readonly algorithm: AsymmetricAlgorithm;\n readonly jwk: PublicJwk;\n readonly material: Uint8Array;\n}\n\nexport interface PrivateKey {\n readonly _tag: \"PrivateKey\";\n readonly algorithm: AsymmetricAlgorithm;\n readonly jwk: Redacted.Redacted<PrivateJwk>;\n readonly material: Redacted.Redacted<Uint8Array>;\n}\n\nexport interface SecretKey {\n readonly _tag: \"SecretKey\";\n readonly algorithm: \"HS256\" | \"dir\";\n readonly jwk: Redacted.Redacted<SecretJwk>;\n readonly material: Redacted.Redacted<Uint8Array>;\n}\n\nexport type SigningKey = PrivateKey | SecretKey;\nexport type VerificationKey = PublicKey | SecretKey;\n\nconst read = <S extends Schema.Constraint>(schema: S, input: unknown) =>\n Effect.try({\n try: () => KeyInput.jwk(input),\n catch: () => InvalidKey.make({}),\n }).pipe(\n Effect.flatMap(Effect.fromResult),\n Effect.flatMap((captured) =>\n Schema.decodeUnknownEffect(schema)(captured, { reportInput: false }).pipe(\n Effect.mapError(() => InvalidKey.make({})),\n ),\n ),\n Effect.map(KeyInput.detach),\n );\n\nconst bytes = (value: string) => decode(value).pipe(Effect.mapError(() => InvalidKey.make({})));\n\nconst snapshot = <A extends PublicJwk | PrivateJwk | SecretJwk>(jwk: A): A => {\n if (jwk.key_ops !== undefined) Object.freeze(jwk.key_ops);\n\n return Object.freeze(jwk);\n};\n\nconst publicParameters = Effect.fnUntraced(function* (\n jwk: PublicJwk | PrivateJwk,\n algorithm: AsymmetricAlgorithm,\n): Effect.fn.Return<Signature.PublicKeyParameters, InvalidKey> {\n if (jwk.alg !== undefined && jwk.alg !== algorithm) return yield* InvalidKey.make({});\n switch (jwk.kty) {\n case \"EC\":\n if (algorithm !== \"ES256\") return yield* InvalidKey.make({});\n\n return { algorithm: \"ECDSA-P256-SHA256\", x: yield* bytes(jwk.x), y: yield* bytes(jwk.y) };\n case \"OKP\":\n if (algorithm !== \"EdDSA\") return yield* InvalidKey.make({});\n\n return { algorithm: \"Ed25519\", x: yield* bytes(jwk.x) };\n case \"RSA\":\n if (algorithm !== \"RS256\" && algorithm !== \"PS256\") return yield* InvalidKey.make({});\n\n return { algorithm: algorithms[algorithm], n: yield* bytes(jwk.n), e: yield* bytes(jwk.e) };\n }\n});\n\nexport const importPublic = Effect.fnUntraced(function* (\n input: unknown,\n selected: AsymmetricAlgorithm,\n) {\n const algorithm = yield* read(AsymmetricAlgorithm, selected);\n const jwk = yield* read(PublicJwk, input);\n const parameters = yield* publicParameters(jwk, algorithm);\n const signatures = yield* Signature.Signature;\n\n const material = yield* signatures\n .encodePublicKey(parameters)\n .pipe(Effect.catchTag(\"CryptoInvalidInput\", () => InvalidKey.make({})));\n\n return Object.freeze({\n _tag: \"PublicKey\",\n algorithm,\n jwk: snapshot(jwk),\n material,\n }) satisfies PublicKey;\n});\n\nexport const importPrivate = Effect.fnUntraced(function* (\n input: Redacted.Redacted<unknown>,\n selected: AsymmetricAlgorithm,\n) {\n const algorithm = yield* read(AsymmetricAlgorithm, selected);\n\n const jwk = yield* read(\n PrivateJwk,\n yield* reveal(input).pipe(Effect.mapError(() => InvalidKey.make({}))),\n );\n\n const publicPart = yield* publicParameters(jwk, algorithm);\n const d = yield* bytes(jwk.d);\n\n const parameters = yield* read(Signature.PrivateKeyParameters, {\n ...publicPart,\n d,\n ...(jwk.kty === \"RSA\"\n ? {\n p: yield* bytes(jwk.p),\n q: yield* bytes(jwk.q),\n dp: yield* bytes(jwk.dp),\n dq: yield* bytes(jwk.dq),\n qi: yield* bytes(jwk.qi),\n }\n : {}),\n });\n\n const signatures = yield* Signature.Signature;\n\n const material = yield* signatures.encodePrivateKey(Redacted.make(parameters)).pipe(\n Effect.catchTag(\"CryptoInvalidInput\", () => InvalidKey.make({})),\n Effect.ensuring(\n Effect.sync(() => {\n parameters.d.fill(0);\n if (\"p\" in parameters)\n for (const part of [\n parameters.p,\n parameters.q,\n parameters.dp,\n parameters.dq,\n parameters.qi,\n ])\n part.fill(0);\n }),\n ),\n );\n\n return Object.freeze({\n _tag: \"PrivateKey\",\n algorithm,\n jwk: Redacted.make(snapshot(jwk)),\n material,\n }) satisfies PrivateKey;\n});\n\nexport const importSecret = Effect.fnUntraced(function* (\n input: Redacted.Redacted<unknown>,\n selected: \"HS256\" | \"dir\",\n) {\n const algorithm = yield* read(Schema.Literals([\"HS256\", \"dir\"]), selected);\n\n const jwk = yield* read(\n SecretJwk,\n yield* reveal(input).pipe(Effect.mapError(() => InvalidKey.make({}))),\n );\n\n const material = yield* bytes(jwk.k);\n\n if (\n (jwk.alg !== undefined && jwk.alg !== algorithm) ||\n material.length < 32 ||\n (algorithm === \"dir\" && material.length !== 32)\n ) {\n material.fill(0);\n\n return yield* InvalidKey.make({});\n }\n\n return Object.freeze({\n _tag: \"SecretKey\",\n algorithm,\n jwk: Redacted.make(snapshot(jwk)),\n material: Redacted.make(material),\n }) satisfies SecretKey;\n});\n\nexport const exportPublic = Effect.fnUntraced(function* (key: PublicKey) {\n return yield* read(PublicJwk, key.jwk);\n});\n\nexport const exportPrivate = Effect.fnUntraced(function* (key: PrivateKey | SecretKey) {\n const value = yield* reveal<PrivateJwk | SecretJwk>(key.jwk).pipe(\n Effect.mapError(() => InvalidKey.make({})),\n );\n\n return Redacted.make(yield* read(Schema.Union([PrivateJwk, SecretJwk]), value));\n});\n"],"mappings":"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"}
|
package/dist/Jwks.d.mts
ADDED
|
@@ -0,0 +1,71 @@
|
|
|
1
|
+
import { InvalidKey, JwksUnavailable, KeyError } from "./Errors.mjs";
|
|
2
|
+
import { PublicKey } from "./Jwk.mjs";
|
|
3
|
+
import { Context, Effect, Layer, Schema } from "effect";
|
|
4
|
+
import { Signature } from "@yielded/crypto/Signature";
|
|
5
|
+
import { HttpClient } from "effect/http";
|
|
6
|
+
//#region src/Jwks.d.ts
|
|
7
|
+
export declare const KeySet: Schema.Struct<{
|
|
8
|
+
readonly keys: Schema.$Array<Schema.Union<readonly [Schema.Struct<{
|
|
9
|
+
readonly kid: Schema.optionalKey<Schema.String>;
|
|
10
|
+
readonly alg: Schema.optionalKey<Schema.NonEmptyString>;
|
|
11
|
+
readonly use: Schema.optionalKey<Schema.Literals<readonly ["sig", "enc"]>>;
|
|
12
|
+
readonly key_ops: Schema.optionalKey<Schema.$Array<Schema.NonEmptyString>>;
|
|
13
|
+
readonly kty: Schema.Literal<"RSA">;
|
|
14
|
+
readonly n: Schema.String;
|
|
15
|
+
readonly e: Schema.String;
|
|
16
|
+
readonly d: Schema.optionalKey<Schema.Never>;
|
|
17
|
+
readonly k: Schema.optionalKey<Schema.Never>;
|
|
18
|
+
}>, Schema.Struct<{
|
|
19
|
+
readonly kid: Schema.optionalKey<Schema.String>;
|
|
20
|
+
readonly alg: Schema.optionalKey<Schema.NonEmptyString>;
|
|
21
|
+
readonly use: Schema.optionalKey<Schema.Literals<readonly ["sig", "enc"]>>;
|
|
22
|
+
readonly key_ops: Schema.optionalKey<Schema.$Array<Schema.NonEmptyString>>;
|
|
23
|
+
readonly kty: Schema.Literal<"EC">;
|
|
24
|
+
readonly crv: Schema.Literal<"P-256">;
|
|
25
|
+
readonly x: Schema.String;
|
|
26
|
+
readonly y: Schema.String;
|
|
27
|
+
readonly d: Schema.optionalKey<Schema.Never>;
|
|
28
|
+
readonly k: Schema.optionalKey<Schema.Never>;
|
|
29
|
+
}>, Schema.Struct<{
|
|
30
|
+
readonly kid: Schema.optionalKey<Schema.String>;
|
|
31
|
+
readonly alg: Schema.optionalKey<Schema.NonEmptyString>;
|
|
32
|
+
readonly use: Schema.optionalKey<Schema.Literals<readonly ["sig", "enc"]>>;
|
|
33
|
+
readonly key_ops: Schema.optionalKey<Schema.$Array<Schema.NonEmptyString>>;
|
|
34
|
+
readonly kty: Schema.Literal<"OKP">;
|
|
35
|
+
readonly crv: Schema.Literal<"Ed25519">;
|
|
36
|
+
readonly x: Schema.String;
|
|
37
|
+
readonly d: Schema.optionalKey<Schema.Never>;
|
|
38
|
+
readonly k: Schema.optionalKey<Schema.Never>;
|
|
39
|
+
}>]>>;
|
|
40
|
+
}>;
|
|
41
|
+
export type KeySet = typeof KeySet.Type;
|
|
42
|
+
export declare const Selection: Schema.Struct<{
|
|
43
|
+
readonly algorithm: Schema.Literals<readonly ["RS256", "PS256", "ES256", "EdDSA"]>;
|
|
44
|
+
readonly kid: Schema.optionalKey<Schema.String>;
|
|
45
|
+
}>;
|
|
46
|
+
export type Selection = typeof Selection.Type;
|
|
47
|
+
declare const Jwks_base: Context.ServiceClass<Jwks, "@yielded/jose/Jwks", {
|
|
48
|
+
readonly resolve: (selection: Selection) => Effect.Effect<PublicKey, KeyError, Signature>;
|
|
49
|
+
}>;
|
|
50
|
+
export declare class Jwks extends Jwks_base {}
|
|
51
|
+
/** One immutable public key set; local selection never requires HttpClient. */
|
|
52
|
+
export declare const layerLocal: (input: unknown) => Layer.Layer<Jwks, InvalidKey>;
|
|
53
|
+
export interface RemoteOptions {
|
|
54
|
+
/** Trusted application configuration, never a URL from a token. HTTPS only. */
|
|
55
|
+
readonly url: string;
|
|
56
|
+
readonly cacheTimeMs?: number;
|
|
57
|
+
readonly cooldownMs?: number;
|
|
58
|
+
readonly timeoutMs?: number;
|
|
59
|
+
readonly maxResponseBytes?: number;
|
|
60
|
+
readonly maxKeys?: number;
|
|
61
|
+
readonly maxWaiters?: number;
|
|
62
|
+
}
|
|
63
|
+
/**
|
|
64
|
+
* One trusted HTTPS endpoint per Layer. The supplied client owns egress policy
|
|
65
|
+
* and must not follow redirects; FetchHttpClient is configured to reject them.
|
|
66
|
+
* Responses from another URL are rejected with every client. No stale-on-error
|
|
67
|
+
* fallback, global cache, background polling, or token-supplied key URLs.
|
|
68
|
+
*/
|
|
69
|
+
export declare const layerRemote: (input: RemoteOptions) => Layer.Layer<Jwks, JwksUnavailable, HttpClient.HttpClient>;
|
|
70
|
+
//#endregion
|
|
71
|
+
//# sourceMappingURL=Jwks.d.mts.map
|
package/dist/Jwks.mjs
ADDED
|
@@ -0,0 +1,169 @@
|
|
|
1
|
+
import { AmbiguousKey, InvalidKey, JwksUnavailable, KeyNotFound } from "./Errors.mjs";
|
|
2
|
+
import { json } from "./internal/encoding.mjs";
|
|
3
|
+
import { detach, keySet, object } from "./internal/keyInput.mjs";
|
|
4
|
+
import { AsymmetricAlgorithm, PublicJwk, importPublic } from "./Jwk.mjs";
|
|
5
|
+
import { Clock, Context, Deferred, Effect, Exit, Layer, Option, Schema, Scope, Semaphore, Stream, SynchronizedRef } from "effect";
|
|
6
|
+
import { FetchHttpClient, HttpClient } from "effect/http";
|
|
7
|
+
//#region src/Jwks.ts
|
|
8
|
+
const KeySet = Schema.Struct({ keys: Schema.Array(PublicJwk).check(Schema.isMaxLength(256)) });
|
|
9
|
+
const Selection = Schema.Struct({
|
|
10
|
+
algorithm: AsymmetricAlgorithm,
|
|
11
|
+
kid: Schema.optionalKey(Schema.String.check(Schema.isMaxLength(256)))
|
|
12
|
+
});
|
|
13
|
+
var Jwks = class extends Context.Service()("@yielded/jose/Jwks") {};
|
|
14
|
+
/** One immutable public key set; local selection never requires HttpClient. */
|
|
15
|
+
const layerLocal = (input) => Layer.effect(Jwks, Effect.gen(function* () {
|
|
16
|
+
const set = yield* readSet(input);
|
|
17
|
+
return Jwks.of({ resolve: Effect.fnUntraced(function* (input) {
|
|
18
|
+
const selection = yield* readSelection(input);
|
|
19
|
+
return yield* importPublic(yield* select(set, selection), selection.algorithm);
|
|
20
|
+
}) });
|
|
21
|
+
}));
|
|
22
|
+
const readSet = (input) => Effect.try({
|
|
23
|
+
try: () => keySet(input),
|
|
24
|
+
catch: () => InvalidKey.make({})
|
|
25
|
+
}).pipe(Effect.flatMap(Effect.fromResult), Effect.flatMap((captured) => Schema.decodeUnknownEffect(KeySet)(captured, { reportInput: false }).pipe(Effect.mapError(() => InvalidKey.make({})))), Effect.map((set) => {
|
|
26
|
+
for (const key of set.keys) {
|
|
27
|
+
detach(key);
|
|
28
|
+
if (key.key_ops !== void 0) Object.freeze(key.key_ops);
|
|
29
|
+
Object.freeze(key);
|
|
30
|
+
}
|
|
31
|
+
Object.freeze(set.keys);
|
|
32
|
+
return Object.freeze(detach(set));
|
|
33
|
+
}));
|
|
34
|
+
const readSelection = (input) => Effect.try({
|
|
35
|
+
try: () => object(input),
|
|
36
|
+
catch: () => InvalidKey.make({})
|
|
37
|
+
}).pipe(Effect.flatMap((captured) => Schema.decodeUnknownEffect(Selection)(captured, { reportInput: false }).pipe(Effect.mapError(() => InvalidKey.make({})))), Effect.map(detach));
|
|
38
|
+
const select = Effect.fnUntraced(function* (set, selection) {
|
|
39
|
+
const candidates = set.keys.filter((key) => (selection.kid === void 0 || key.kid === selection.kid) && (key.alg === void 0 || key.alg === selection.algorithm) && (key.use === void 0 || key.use === "sig") && (key.key_ops === void 0 || key.key_ops.includes("verify")) && (selection.algorithm === "ES256" ? key.kty === "EC" : selection.algorithm === "EdDSA" ? key.kty === "OKP" : key.kty === "RSA"));
|
|
40
|
+
if (candidates.length === 0) return yield* KeyNotFound.make({});
|
|
41
|
+
if (candidates.length !== 1) return yield* AmbiguousKey.make({});
|
|
42
|
+
return candidates[0];
|
|
43
|
+
});
|
|
44
|
+
const bounded = (minimum, maximum) => Schema.Int.check(Schema.isBetween({
|
|
45
|
+
minimum,
|
|
46
|
+
maximum
|
|
47
|
+
}));
|
|
48
|
+
const RemoteConfiguration = Schema.Struct({
|
|
49
|
+
url: Schema.URLFromString.check(Schema.makeFilter((url) => url.protocol === "https:" && url.username === "" && url.password === "" && url.hash === "")),
|
|
50
|
+
cacheTimeMs: bounded(1, 864e5),
|
|
51
|
+
cooldownMs: bounded(0, 36e5),
|
|
52
|
+
timeoutMs: bounded(1, 6e4),
|
|
53
|
+
maxResponseBytes: bounded(1, 1048576),
|
|
54
|
+
maxKeys: bounded(1, 256),
|
|
55
|
+
maxWaiters: bounded(1, 1024)
|
|
56
|
+
});
|
|
57
|
+
/**
|
|
58
|
+
* One trusted HTTPS endpoint per Layer. The supplied client owns egress policy
|
|
59
|
+
* and must not follow redirects; FetchHttpClient is configured to reject them.
|
|
60
|
+
* Responses from another URL are rejected with every client. No stale-on-error
|
|
61
|
+
* fallback, global cache, background polling, or token-supplied key URLs.
|
|
62
|
+
*/
|
|
63
|
+
const layerRemote = (input) => {
|
|
64
|
+
const configuration = {
|
|
65
|
+
cacheTimeMs: 6e5,
|
|
66
|
+
cooldownMs: 3e4,
|
|
67
|
+
timeoutMs: 5e3,
|
|
68
|
+
maxResponseBytes: 131072,
|
|
69
|
+
maxKeys: 64,
|
|
70
|
+
maxWaiters: 64,
|
|
71
|
+
...input
|
|
72
|
+
};
|
|
73
|
+
return Layer.effect(Jwks, Effect.gen(function* () {
|
|
74
|
+
const options = yield* Schema.decodeUnknownEffect(RemoteConfiguration)(configuration, { reportInput: false }).pipe(Effect.mapError(() => JwksUnavailable.make({ reason: "configuration" })));
|
|
75
|
+
const client = HttpClient.withScope(yield* HttpClient.HttpClient);
|
|
76
|
+
const state = yield* SynchronizedRef.make({
|
|
77
|
+
_tag: "Open",
|
|
78
|
+
cache: void 0,
|
|
79
|
+
pending: void 0,
|
|
80
|
+
lastAttempt: -Infinity
|
|
81
|
+
});
|
|
82
|
+
const permits = yield* Semaphore.make(options.maxWaiters);
|
|
83
|
+
const scope = yield* Effect.acquireRelease(Scope.make(), (scope) => Effect.gen(function* () {
|
|
84
|
+
const previous = yield* SynchronizedRef.getAndSet(state, { _tag: "Closed" });
|
|
85
|
+
if (previous._tag === "Open" && previous.pending !== void 0) yield* Deferred.fail(previous.pending, JwksUnavailable.make({ reason: "closed" }));
|
|
86
|
+
yield* Scope.close(scope, Exit.void);
|
|
87
|
+
}));
|
|
88
|
+
const fetch = Effect.gen(function* () {
|
|
89
|
+
const response = yield* client.get(options.url.href, { headers: { accept: "application/json" } });
|
|
90
|
+
if (response.status !== 200 || response.url !== options.url.href) return yield* JwksUnavailable.make({ reason: "response" });
|
|
91
|
+
const buffer = new Uint8Array(options.maxResponseBytes);
|
|
92
|
+
const length = yield* Stream.runFoldEffect(response.stream, () => 0, (length, chunk) => {
|
|
93
|
+
if (chunk.byteLength > buffer.length - length) return Effect.fail(JwksUnavailable.make({ reason: "response" }));
|
|
94
|
+
buffer.set(chunk, length);
|
|
95
|
+
return Effect.succeed(length + chunk.length);
|
|
96
|
+
});
|
|
97
|
+
const document = yield* json(buffer.subarray(0, length), "payload");
|
|
98
|
+
const keys = yield* readSet(document);
|
|
99
|
+
if (keys.keys.length > options.maxKeys) return yield* JwksUnavailable.make({ reason: "response" });
|
|
100
|
+
return {
|
|
101
|
+
keys,
|
|
102
|
+
expiresAt: (yield* Clock.currentTimeMillis) + options.cacheTimeMs
|
|
103
|
+
};
|
|
104
|
+
}).pipe(Effect.scoped, Effect.provideService(FetchHttpClient.RequestInit, {
|
|
105
|
+
redirect: "error",
|
|
106
|
+
credentials: "omit"
|
|
107
|
+
}), Effect.catchTag("HttpClientError", () => JwksUnavailable.make({ reason: "transport" })), Effect.catchTag(["JoseInvalidToken", "JoseInvalidKey"], () => JwksUnavailable.make({ reason: "response" })), Effect.timeoutOrElse({
|
|
108
|
+
duration: options.timeoutMs,
|
|
109
|
+
orElse: () => JwksUnavailable.make({ reason: "transport" })
|
|
110
|
+
}));
|
|
111
|
+
const refresh = (missingFrom) => Effect.uninterruptibleMask((restore) => Effect.gen(function* () {
|
|
112
|
+
const now = yield* restore(Clock.currentTimeMillis);
|
|
113
|
+
const result = yield* Deferred.make();
|
|
114
|
+
const admission = yield* SynchronizedRef.modify(state, (current) => {
|
|
115
|
+
if (current._tag === "Closed") return [{
|
|
116
|
+
_tag: "Rejected",
|
|
117
|
+
reason: "closed"
|
|
118
|
+
}, current];
|
|
119
|
+
if (current.cache !== void 0 && current.cache !== missingFrom && current.cache.expiresAt > now) return [{
|
|
120
|
+
_tag: "Cached",
|
|
121
|
+
snapshot: current.cache
|
|
122
|
+
}, current];
|
|
123
|
+
if (current.pending !== void 0) return [{
|
|
124
|
+
_tag: "Join",
|
|
125
|
+
result: current.pending
|
|
126
|
+
}, current];
|
|
127
|
+
if (now - current.lastAttempt < options.cooldownMs) return [{
|
|
128
|
+
_tag: "Rejected",
|
|
129
|
+
reason: "cooldown"
|
|
130
|
+
}, current];
|
|
131
|
+
return [{
|
|
132
|
+
_tag: "Start",
|
|
133
|
+
result
|
|
134
|
+
}, {
|
|
135
|
+
...current,
|
|
136
|
+
pending: result,
|
|
137
|
+
lastAttempt: now
|
|
138
|
+
}];
|
|
139
|
+
});
|
|
140
|
+
if (admission._tag === "Rejected") return yield* JwksUnavailable.make({ reason: admission.reason });
|
|
141
|
+
if (admission._tag === "Cached") return admission.snapshot;
|
|
142
|
+
if (admission._tag === "Join") return yield* restore(Deferred.await(admission.result));
|
|
143
|
+
yield* fetch.pipe(Effect.onExit((exit) => SynchronizedRef.updateEffect(state, (current) => current._tag === "Closed" ? Effect.succeed(current) : Deferred.done(result, exit).pipe(Effect.as({
|
|
144
|
+
...current,
|
|
145
|
+
cache: Exit.isSuccess(exit) ? exit.value : current.cache,
|
|
146
|
+
pending: void 0
|
|
147
|
+
})))), Effect.forkIn(scope, { uninterruptible: false }));
|
|
148
|
+
return yield* restore(Deferred.await(result));
|
|
149
|
+
}));
|
|
150
|
+
const resolve = Effect.fnUntraced(function* (input) {
|
|
151
|
+
if ((yield* SynchronizedRef.get(state))._tag === "Closed") return yield* JwksUnavailable.make({ reason: "closed" });
|
|
152
|
+
const selection = yield* readSelection(input);
|
|
153
|
+
const now = yield* Clock.currentTimeMillis;
|
|
154
|
+
const snapshot = yield* SynchronizedRef.get(state);
|
|
155
|
+
if (snapshot._tag === "Closed") return yield* JwksUnavailable.make({ reason: "closed" });
|
|
156
|
+
const current = snapshot.cache !== void 0 && snapshot.cache.expiresAt > now ? snapshot.cache : yield* refresh();
|
|
157
|
+
const candidate = yield* select(current.keys, selection).pipe(Effect.catchTag("JoseKeyNotFound", () => refresh(current).pipe(Effect.mapError((error) => error.reason === "cooldown" ? KeyNotFound.make({}) : error), Effect.flatMap((snapshot) => select(snapshot.keys, selection)))));
|
|
158
|
+
return yield* importPublic(candidate, selection.algorithm);
|
|
159
|
+
});
|
|
160
|
+
return Jwks.of({ resolve: (selection) => resolve(selection).pipe(permits.withPermitsIfAvailable(1), Effect.flatMap(Option.match({
|
|
161
|
+
onNone: () => JwksUnavailable.make({ reason: "busy" }),
|
|
162
|
+
onSome: Effect.succeed
|
|
163
|
+
}))) });
|
|
164
|
+
}));
|
|
165
|
+
};
|
|
166
|
+
//#endregion
|
|
167
|
+
export { Jwks, KeySet, Selection, layerLocal, layerRemote };
|
|
168
|
+
|
|
169
|
+
//# sourceMappingURL=Jwks.mjs.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"Jwks.mjs","names":["KeyInput.keySet","KeyInput.detach","KeyInput.object"],"sources":["../src/Jwks.ts"],"sourcesContent":["import type { Signature } from \"@yielded/crypto/Signature\";\nimport {\n Clock,\n Context,\n Deferred,\n Effect,\n Exit,\n Layer,\n Option,\n Schema,\n Scope,\n Semaphore,\n Stream,\n SynchronizedRef,\n} from \"effect\";\nimport { FetchHttpClient, HttpClient } from \"effect/http\";\n\nimport { AmbiguousKey, InvalidKey, JwksUnavailable, KeyNotFound, type KeyError } from \"./Errors\";\nimport { json } from \"./internal/encoding\";\nimport * as KeyInput from \"./internal/keyInput\";\nimport { AsymmetricAlgorithm, importPublic, type PublicKey, PublicJwk } from \"./Jwk\";\n\nexport const KeySet = Schema.Struct({\n keys: Schema.Array(PublicJwk).check(Schema.isMaxLength(KeyInput.maxArrayLength)),\n});\n\nexport type KeySet = typeof KeySet.Type;\n\nexport const Selection = Schema.Struct({\n algorithm: AsymmetricAlgorithm,\n kid: Schema.optionalKey(Schema.String.check(Schema.isMaxLength(256))),\n});\n\nexport type Selection = typeof Selection.Type;\n\nexport class Jwks extends Context.Service<\n Jwks,\n {\n readonly resolve: (selection: Selection) => Effect.Effect<PublicKey, KeyError, Signature>;\n }\n>()(\"@yielded/jose/Jwks\") {}\n\n/** One immutable public key set; local selection never requires HttpClient. */\nexport const layerLocal = (input: unknown): Layer.Layer<Jwks, InvalidKey> =>\n Layer.effect(\n Jwks,\n Effect.gen(function* () {\n const set = yield* readSet(input);\n\n return Jwks.of({\n resolve: Effect.fnUntraced(function* (input) {\n const selection = yield* readSelection(input);\n\n return yield* importPublic(yield* select(set, selection), selection.algorithm);\n }),\n });\n }),\n );\n\nexport interface RemoteOptions {\n /** Trusted application configuration, never a URL from a token. HTTPS only. */\n readonly url: string;\n readonly cacheTimeMs?: number;\n readonly cooldownMs?: number;\n readonly timeoutMs?: number;\n readonly maxResponseBytes?: number;\n readonly maxKeys?: number;\n readonly maxWaiters?: number;\n}\n\nconst readSet = (input: unknown) =>\n Effect.try({\n try: () => KeyInput.keySet(input),\n catch: () => InvalidKey.make({}),\n }).pipe(\n Effect.flatMap(Effect.fromResult),\n Effect.flatMap((captured) =>\n Schema.decodeUnknownEffect(KeySet)(captured, { reportInput: false }).pipe(\n Effect.mapError(() => InvalidKey.make({})),\n ),\n ),\n Effect.map((set) => {\n for (const key of set.keys) {\n KeyInput.detach(key);\n if (key.key_ops !== undefined) Object.freeze(key.key_ops);\n Object.freeze(key);\n }\n Object.freeze(set.keys);\n\n return Object.freeze(KeyInput.detach(set));\n }),\n );\n\nconst readSelection = (input: Selection) =>\n Effect.try({\n try: () => KeyInput.object(input),\n catch: () => InvalidKey.make({}),\n }).pipe(\n Effect.flatMap((captured) =>\n Schema.decodeUnknownEffect(Selection)(captured, { reportInput: false }).pipe(\n Effect.mapError(() => InvalidKey.make({})),\n ),\n ),\n Effect.map(KeyInput.detach),\n );\n\nconst select = Effect.fnUntraced(function* (set: KeySet, selection: Selection) {\n const candidates = set.keys.filter(\n (key) =>\n (selection.kid === undefined || key.kid === selection.kid) &&\n (key.alg === undefined || key.alg === selection.algorithm) &&\n (key.use === undefined || key.use === \"sig\") &&\n (key.key_ops === undefined || key.key_ops.includes(\"verify\")) &&\n (selection.algorithm === \"ES256\"\n ? key.kty === \"EC\"\n : selection.algorithm === \"EdDSA\"\n ? key.kty === \"OKP\"\n : key.kty === \"RSA\"),\n );\n\n if (candidates.length === 0) return yield* KeyNotFound.make({});\n if (candidates.length !== 1) return yield* AmbiguousKey.make({});\n\n return candidates[0];\n});\n\nconst bounded = (minimum: number, maximum: number) =>\n Schema.Int.check(Schema.isBetween({ minimum, maximum }));\n\nconst RemoteConfiguration = Schema.Struct({\n url: Schema.URLFromString.check(\n Schema.makeFilter(\n (url) =>\n url.protocol === \"https:\" && url.username === \"\" && url.password === \"\" && url.hash === \"\",\n ),\n ),\n cacheTimeMs: bounded(1, 86400000),\n cooldownMs: bounded(0, 3600000),\n timeoutMs: bounded(1, 60000),\n maxResponseBytes: bounded(1, 1048576),\n maxKeys: bounded(1, KeyInput.maxArrayLength),\n maxWaiters: bounded(1, 1024),\n});\n\ninterface Snapshot {\n readonly keys: KeySet;\n readonly expiresAt: number;\n}\n\ntype RemoteState =\n | { readonly _tag: \"Closed\" }\n | {\n readonly _tag: \"Open\";\n readonly cache: Snapshot | undefined;\n readonly pending: Deferred.Deferred<Snapshot, JwksUnavailable> | undefined;\n readonly lastAttempt: number;\n };\n\ntype Admission =\n | { readonly _tag: \"Rejected\"; readonly reason: \"closed\" | \"cooldown\" }\n | { readonly _tag: \"Cached\"; readonly snapshot: Snapshot }\n | {\n readonly _tag: \"Join\" | \"Start\";\n readonly result: Deferred.Deferred<Snapshot, JwksUnavailable>;\n };\n\n/**\n * One trusted HTTPS endpoint per Layer. The supplied client owns egress policy\n * and must not follow redirects; FetchHttpClient is configured to reject them.\n * Responses from another URL are rejected with every client. No stale-on-error\n * fallback, global cache, background polling, or token-supplied key URLs.\n */\nexport const layerRemote = (\n input: RemoteOptions,\n): Layer.Layer<Jwks, JwksUnavailable, HttpClient.HttpClient> => {\n const configuration = {\n cacheTimeMs: 600000,\n cooldownMs: 30000,\n timeoutMs: 5000,\n maxResponseBytes: 131072,\n maxKeys: 64,\n maxWaiters: 64,\n ...input,\n };\n\n return Layer.effect(\n Jwks,\n Effect.gen(function* () {\n const options = yield* Schema.decodeUnknownEffect(RemoteConfiguration)(configuration, {\n reportInput: false,\n }).pipe(Effect.mapError(() => JwksUnavailable.make({ reason: \"configuration\" })));\n\n const client = HttpClient.withScope(yield* HttpClient.HttpClient);\n\n const state = yield* SynchronizedRef.make<RemoteState>({\n _tag: \"Open\",\n cache: undefined,\n pending: undefined,\n lastAttempt: -Infinity,\n });\n\n const permits = yield* Semaphore.make(options.maxWaiters);\n\n const scope = yield* Effect.acquireRelease(Scope.make(), (scope) =>\n Effect.gen(function* () {\n const previous = yield* SynchronizedRef.getAndSet(state, { _tag: \"Closed\" });\n\n // A scoped fiber can be interrupted before its first instruction, so\n // its onExit handler alone cannot release pending lookup waiters.\n if (previous._tag === \"Open\" && previous.pending !== undefined)\n yield* Deferred.fail(previous.pending, JwksUnavailable.make({ reason: \"closed\" }));\n yield* Scope.close(scope, Exit.void);\n }),\n );\n\n const fetch = Effect.gen(function* () {\n const response = yield* client.get(options.url.href, {\n headers: { accept: \"application/json\" },\n });\n\n if (response.status !== 200 || response.url !== options.url.href)\n return yield* JwksUnavailable.make({ reason: \"response\" });\n const buffer = new Uint8Array(options.maxResponseBytes);\n\n const length = yield* Stream.runFoldEffect(\n response.stream,\n () => 0,\n (length, chunk) => {\n if (chunk.byteLength > buffer.length - length)\n return Effect.fail(JwksUnavailable.make({ reason: \"response\" }));\n buffer.set(chunk, length);\n\n return Effect.succeed(length + chunk.length);\n },\n );\n\n const document = yield* json(buffer.subarray(0, length), \"payload\");\n const keys = yield* readSet(document);\n\n if (keys.keys.length > options.maxKeys)\n return yield* JwksUnavailable.make({ reason: \"response\" });\n\n return {\n keys,\n expiresAt: (yield* Clock.currentTimeMillis) + options.cacheTimeMs,\n } satisfies Snapshot;\n }).pipe(\n Effect.scoped,\n Effect.provideService(FetchHttpClient.RequestInit, {\n redirect: \"error\",\n credentials: \"omit\",\n }),\n Effect.catchTag(\"HttpClientError\", () => JwksUnavailable.make({ reason: \"transport\" })),\n Effect.catchTag([\"JoseInvalidToken\", \"JoseInvalidKey\"], () =>\n JwksUnavailable.make({ reason: \"response\" }),\n ),\n Effect.timeoutOrElse({\n duration: options.timeoutMs,\n orElse: () => JwksUnavailable.make({ reason: \"transport\" }),\n }),\n );\n\n const refresh = (missingFrom?: Snapshot) =>\n Effect.uninterruptibleMask((restore) =>\n Effect.gen(function* () {\n const now = yield* restore(Clock.currentTimeMillis);\n const result = yield* Deferred.make<Snapshot, JwksUnavailable>();\n\n // Recheck cache and ownership together after the caller's snapshot.\n // A missing key bypasses only the snapshot that failed selection.\n const admission = yield* SynchronizedRef.modify(\n state,\n (current): readonly [Admission, RemoteState] => {\n if (current._tag === \"Closed\")\n return [{ _tag: \"Rejected\", reason: \"closed\" }, current];\n if (\n current.cache !== undefined &&\n current.cache !== missingFrom &&\n current.cache.expiresAt > now\n )\n return [{ _tag: \"Cached\", snapshot: current.cache }, current];\n if (current.pending !== undefined)\n return [{ _tag: \"Join\", result: current.pending }, current];\n if (now - current.lastAttempt < options.cooldownMs)\n return [{ _tag: \"Rejected\", reason: \"cooldown\" }, current];\n\n return [\n { _tag: \"Start\", result },\n { ...current, pending: result, lastAttempt: now },\n ];\n },\n );\n\n if (admission._tag === \"Rejected\")\n return yield* JwksUnavailable.make({ reason: admission.reason });\n if (admission._tag === \"Cached\") return admission.snapshot;\n if (admission._tag === \"Join\") return yield* restore(Deferred.await(admission.result));\n\n // Admission through fork registration is uninterruptible. Network\n // work and waiter suspension never run under the state lock.\n yield* fetch.pipe(\n Effect.onExit((exit) =>\n SynchronizedRef.updateEffect(state, (current) =>\n current._tag === \"Closed\"\n ? Effect.succeed(current)\n : Deferred.done(result, exit).pipe(\n // Keep pending ownership until publication, serialized\n // with shutdown so a closed owner cannot publish success.\n Effect.as({\n ...current,\n cache: Exit.isSuccess(exit) ? exit.value : current.cache,\n pending: undefined,\n }),\n ),\n ),\n ),\n Effect.forkIn(scope, { uninterruptible: false }),\n );\n\n return yield* restore(Deferred.await(result));\n }),\n );\n\n const resolve = Effect.fnUntraced(function* (input: Selection) {\n if ((yield* SynchronizedRef.get(state))._tag === \"Closed\")\n return yield* JwksUnavailable.make({ reason: \"closed\" });\n const selection = yield* readSelection(input);\n const now = yield* Clock.currentTimeMillis;\n const snapshot = yield* SynchronizedRef.get(state);\n\n if (snapshot._tag === \"Closed\") return yield* JwksUnavailable.make({ reason: \"closed\" });\n\n const current =\n snapshot.cache !== undefined && snapshot.cache.expiresAt > now\n ? snapshot.cache\n : yield* refresh();\n\n const candidate = yield* select(current.keys, selection).pipe(\n Effect.catchTag(\"JoseKeyNotFound\", () =>\n refresh(current).pipe(\n Effect.mapError((error) =>\n error.reason === \"cooldown\" ? KeyNotFound.make({}) : error,\n ),\n Effect.flatMap((snapshot) => select(snapshot.keys, selection)),\n ),\n ),\n );\n\n return yield* importPublic(candidate, selection.algorithm);\n });\n\n return Jwks.of({\n resolve: (selection) =>\n resolve(selection).pipe(\n permits.withPermitsIfAvailable(1),\n Effect.flatMap(\n Option.match({\n onNone: () => JwksUnavailable.make({ reason: \"busy\" }),\n onSome: Effect.succeed,\n }),\n ),\n ),\n });\n }),\n );\n};\n"],"mappings":"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"}
|
package/dist/Jws.d.mts
ADDED
|
@@ -0,0 +1,53 @@
|
|
|
1
|
+
import { AlgorithmNotAllowed, AmbiguousKey, InvalidKey, InvalidToken, JwksUnavailable, KeyNotFound, SignatureVerificationFailed } from "./Errors.mjs";
|
|
2
|
+
import { SigningKey, VerificationKey } from "./Jwk.mjs";
|
|
3
|
+
import { Jwks } from "./Jwks.mjs";
|
|
4
|
+
import { Effect, Redacted, Schema } from "effect";
|
|
5
|
+
import { Signature } from "@yielded/crypto/Signature";
|
|
6
|
+
import { Hmac } from "@yielded/crypto/Hmac";
|
|
7
|
+
//#region src/Jws.d.ts
|
|
8
|
+
export declare const Header: Schema.Struct<{
|
|
9
|
+
readonly alg: Schema.Literals<readonly ["HS256", "RS256", "PS256", "ES256", "EdDSA"]>;
|
|
10
|
+
readonly kid: Schema.optionalKey<Schema.String>;
|
|
11
|
+
readonly typ: Schema.optionalKey<Schema.NonEmptyString>;
|
|
12
|
+
readonly cty: Schema.optionalKey<Schema.NonEmptyString>;
|
|
13
|
+
}>;
|
|
14
|
+
export type Header = typeof Header.Type;
|
|
15
|
+
export declare const VerifyOptions: Schema.Struct<{
|
|
16
|
+
readonly algorithms: Schema.$Array<Schema.Literals<readonly ["HS256", "RS256", "PS256", "ES256", "EdDSA"]>>;
|
|
17
|
+
}>;
|
|
18
|
+
export type VerifyOptions = typeof VerifyOptions.Type;
|
|
19
|
+
export interface Verified {
|
|
20
|
+
readonly protectedHeader: Header;
|
|
21
|
+
readonly payload: Redacted.Redacted<Uint8Array>;
|
|
22
|
+
}
|
|
23
|
+
export type Requirements = Hmac | Signature;
|
|
24
|
+
export declare const sign: (payload: Redacted.Redacted<Uint8Array<ArrayBufferLike>>, key: SigningKey, input: {
|
|
25
|
+
readonly alg: "ES256" | "EdDSA" | "HS256" | "PS256" | "RS256";
|
|
26
|
+
readonly kid?: string;
|
|
27
|
+
readonly typ?: string;
|
|
28
|
+
readonly cty?: string;
|
|
29
|
+
}) => Effect.Effect<Redacted.Redacted<string>, InvalidKey | InvalidToken | import("@yielded/crypto/Errors").OperationError, Hmac | Signature>;
|
|
30
|
+
export declare const verify: (token: Redacted.Redacted<string>, key: VerificationKey, options: {
|
|
31
|
+
readonly algorithms: readonly ("ES256" | "EdDSA" | "HS256" | "PS256" | "RS256")[];
|
|
32
|
+
}) => Effect.Effect<{
|
|
33
|
+
protectedHeader: {
|
|
34
|
+
readonly alg: "ES256" | "EdDSA" | "HS256" | "PS256" | "RS256";
|
|
35
|
+
readonly kid?: string;
|
|
36
|
+
readonly typ?: string;
|
|
37
|
+
readonly cty?: string;
|
|
38
|
+
};
|
|
39
|
+
payload: Redacted.Redacted<Uint8Array<ArrayBufferLike>>;
|
|
40
|
+
}, AlgorithmNotAllowed | InvalidKey | InvalidToken | SignatureVerificationFailed | import("@yielded/crypto/Errors").OperationError, Hmac | Signature>;
|
|
41
|
+
export declare const verifyWithKeySet: (token: Redacted.Redacted<string>, options: {
|
|
42
|
+
readonly algorithms: readonly ("ES256" | "EdDSA" | "HS256" | "PS256" | "RS256")[];
|
|
43
|
+
}) => Effect.Effect<{
|
|
44
|
+
protectedHeader: {
|
|
45
|
+
readonly alg: "ES256" | "EdDSA" | "HS256" | "PS256" | "RS256";
|
|
46
|
+
readonly kid?: string;
|
|
47
|
+
readonly typ?: string;
|
|
48
|
+
readonly cty?: string;
|
|
49
|
+
};
|
|
50
|
+
payload: Redacted.Redacted<Uint8Array<ArrayBufferLike>>;
|
|
51
|
+
}, AlgorithmNotAllowed | AmbiguousKey | import("@yielded/crypto/Errors").CryptoUnavailable | import("@yielded/crypto/Errors").InvalidInput | InvalidKey | InvalidToken | JwksUnavailable | KeyNotFound | SignatureVerificationFailed | import("@yielded/crypto/Errors").UnsupportedAlgorithm, Jwks | Signature>;
|
|
52
|
+
//#endregion
|
|
53
|
+
//# sourceMappingURL=Jws.d.mts.map
|