@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/Jws.mjs
ADDED
|
@@ -0,0 +1,108 @@
|
|
|
1
|
+
import { AlgorithmNotAllowed, InvalidKey, InvalidToken, SignatureVerificationFailed } from "./Errors.mjs";
|
|
2
|
+
import { algorithms } from "./internal/algorithms.mjs";
|
|
3
|
+
import { JsonObject, decode, json, parse, reveal, split, stringify, utf8 } from "./internal/encoding.mjs";
|
|
4
|
+
import { Algorithm } from "./Jwk.mjs";
|
|
5
|
+
import { Jwks } from "./Jwks.mjs";
|
|
6
|
+
import { check } from "./internal/keyPolicy.mjs";
|
|
7
|
+
import { Effect, Redacted, Schema } from "effect";
|
|
8
|
+
import { Signature } from "@yielded/crypto/Signature";
|
|
9
|
+
import { Base64Url } from "effect/encoding";
|
|
10
|
+
import { Hmac } from "@yielded/crypto/Hmac";
|
|
11
|
+
//#region src/Jws.ts
|
|
12
|
+
const Header = Schema.Struct({
|
|
13
|
+
alg: Algorithm,
|
|
14
|
+
kid: Schema.optionalKey(Schema.String.check(Schema.isMaxLength(256))),
|
|
15
|
+
typ: Schema.optionalKey(Schema.NonEmptyString),
|
|
16
|
+
cty: Schema.optionalKey(Schema.NonEmptyString)
|
|
17
|
+
});
|
|
18
|
+
const VerifyOptions = Schema.Struct({ algorithms: Schema.Array(Algorithm).check(Schema.isMinLength(1)) });
|
|
19
|
+
const readHeader = Effect.fnUntraced(function* (input) {
|
|
20
|
+
const raw = yield* parse(JsonObject, input, "header");
|
|
21
|
+
if (raw.crit !== void 0 || raw.b64 !== void 0) return yield* InvalidToken.make({ reason: "header" });
|
|
22
|
+
return yield* parse(Header, raw, "header");
|
|
23
|
+
});
|
|
24
|
+
const parseCompact = Effect.fnUntraced(function* (token, options) {
|
|
25
|
+
const policy = yield* parse(VerifyOptions, options, "parameters");
|
|
26
|
+
const [protectedPart, payloadPart, signaturePart] = yield* split(token, 3);
|
|
27
|
+
if (protectedPart.length > 4096) return yield* InvalidToken.make({ reason: "header" });
|
|
28
|
+
const protectedHeader = yield* readHeader(yield* json(yield* decode(protectedPart), "header"));
|
|
29
|
+
if (!policy.algorithms.includes(protectedHeader.alg)) return yield* AlgorithmNotAllowed.make({});
|
|
30
|
+
return {
|
|
31
|
+
protectedHeader,
|
|
32
|
+
payload: yield* decode(payloadPart),
|
|
33
|
+
signature: yield* decode(signaturePart),
|
|
34
|
+
signingInput: utf8(`${protectedPart}.${payloadPart}`)
|
|
35
|
+
};
|
|
36
|
+
});
|
|
37
|
+
const verifyAsymmetric = Effect.fnUntraced(function* (parsed, key) {
|
|
38
|
+
const algorithm = parsed.protectedHeader.alg;
|
|
39
|
+
if (algorithm === "HS256" || key.algorithm !== algorithm) return yield* InvalidKey.make({});
|
|
40
|
+
yield* check(key.jwk, algorithm, "verify");
|
|
41
|
+
if (!(yield* (yield* Signature).verify({
|
|
42
|
+
algorithm: algorithms[algorithm],
|
|
43
|
+
publicKey: key.material,
|
|
44
|
+
data: parsed.signingInput,
|
|
45
|
+
signature: parsed.signature
|
|
46
|
+
}))) return yield* SignatureVerificationFailed.make({});
|
|
47
|
+
return {
|
|
48
|
+
protectedHeader: parsed.protectedHeader,
|
|
49
|
+
payload: Redacted.make(parsed.payload)
|
|
50
|
+
};
|
|
51
|
+
});
|
|
52
|
+
const sign = Effect.fnUntraced(function* (payload, key, input) {
|
|
53
|
+
const header = yield* readHeader(input);
|
|
54
|
+
if (key.algorithm !== header.alg) return yield* InvalidKey.make({});
|
|
55
|
+
yield* check(yield* reveal(key.jwk), header.alg, "sign");
|
|
56
|
+
const data = yield* parse(Schema.Uint8Array.check(Schema.isMaxLength(45e3)), yield* reveal(payload), "payload");
|
|
57
|
+
const protectedPart = Base64Url.encode(yield* stringify(header).pipe(Effect.mapError(() => InvalidToken.make({ reason: "header" }))));
|
|
58
|
+
if (protectedPart.length > 4096) return yield* InvalidToken.make({ reason: "header" });
|
|
59
|
+
const signingInput = `${protectedPart}.${Base64Url.encode(data)}`;
|
|
60
|
+
let signature;
|
|
61
|
+
if (key._tag === "SecretKey") {
|
|
62
|
+
if (header.alg !== "HS256") return yield* InvalidKey.make({});
|
|
63
|
+
signature = yield* (yield* Hmac).sign({
|
|
64
|
+
algorithm: "SHA-256",
|
|
65
|
+
key: key.material,
|
|
66
|
+
data: utf8(signingInput)
|
|
67
|
+
});
|
|
68
|
+
} else {
|
|
69
|
+
if (header.alg === "HS256") return yield* InvalidKey.make({});
|
|
70
|
+
signature = yield* (yield* Signature).sign({
|
|
71
|
+
algorithm: algorithms[header.alg],
|
|
72
|
+
privateKey: key.material,
|
|
73
|
+
data: utf8(signingInput)
|
|
74
|
+
});
|
|
75
|
+
}
|
|
76
|
+
return Redacted.make(`${signingInput}.${Base64Url.encode(signature)}`);
|
|
77
|
+
});
|
|
78
|
+
const verify = Effect.fnUntraced(function* (token, key, options) {
|
|
79
|
+
const parsed = yield* parseCompact(token, options);
|
|
80
|
+
if (key._tag !== "SecretKey") return yield* verifyAsymmetric(parsed, key);
|
|
81
|
+
const algorithm = parsed.protectedHeader.alg;
|
|
82
|
+
if (key.algorithm !== algorithm) return yield* InvalidKey.make({});
|
|
83
|
+
yield* check(yield* reveal(key.jwk), algorithm, "verify");
|
|
84
|
+
if (algorithm !== "HS256") return yield* InvalidKey.make({});
|
|
85
|
+
if (!(yield* (yield* Hmac).verify({
|
|
86
|
+
algorithm: "SHA-256",
|
|
87
|
+
key: key.material,
|
|
88
|
+
data: parsed.signingInput,
|
|
89
|
+
tag: parsed.signature
|
|
90
|
+
}))) return yield* SignatureVerificationFailed.make({});
|
|
91
|
+
return {
|
|
92
|
+
protectedHeader: parsed.protectedHeader,
|
|
93
|
+
payload: Redacted.make(parsed.payload)
|
|
94
|
+
};
|
|
95
|
+
});
|
|
96
|
+
const verifyWithKeySet = Effect.fnUntraced(function* (token, options) {
|
|
97
|
+
const parsed = yield* parseCompact(token, options);
|
|
98
|
+
if (parsed.protectedHeader.alg === "HS256") return yield* InvalidKey.make({});
|
|
99
|
+
const key = yield* (yield* Jwks).resolve({
|
|
100
|
+
algorithm: parsed.protectedHeader.alg,
|
|
101
|
+
...parsed.protectedHeader.kid === void 0 ? {} : { kid: parsed.protectedHeader.kid }
|
|
102
|
+
});
|
|
103
|
+
return yield* verifyAsymmetric(parsed, key);
|
|
104
|
+
});
|
|
105
|
+
//#endregion
|
|
106
|
+
export { Header, VerifyOptions, sign, verify, verifyWithKeySet };
|
|
107
|
+
|
|
108
|
+
//# sourceMappingURL=Jws.mjs.map
|
package/dist/Jws.mjs.map
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"Jws.mjs","names":["KeyPolicy.check"],"sources":["../src/Jws.ts"],"sourcesContent":["import { Hmac } from \"@yielded/crypto/Hmac\";\nimport { Signature } from \"@yielded/crypto/Signature\";\nimport { Effect, Redacted, Schema } from \"effect\";\nimport { Base64Url } from \"effect/encoding\";\n\nimport {\n AlgorithmNotAllowed,\n InvalidKey,\n InvalidToken,\n SignatureVerificationFailed,\n} from \"./Errors\";\nimport { algorithms } from \"./internal/algorithms\";\nimport {\n decode,\n json,\n JsonObject,\n parse,\n reveal,\n split,\n stringify,\n utf8,\n} from \"./internal/encoding\";\nimport * as KeyPolicy from \"./internal/keyPolicy\";\nimport {\n Algorithm,\n type SigningKey,\n type VerificationKey,\n type PublicKey,\n type PrivateJwk,\n type SecretJwk,\n} from \"./Jwk\";\nimport { Jwks } from \"./Jwks\";\n\nexport const Header = Schema.Struct({\n alg: Algorithm,\n kid: Schema.optionalKey(Schema.String.check(Schema.isMaxLength(256))),\n typ: Schema.optionalKey(Schema.NonEmptyString),\n cty: Schema.optionalKey(Schema.NonEmptyString),\n});\n\nexport type Header = typeof Header.Type;\n\nexport const VerifyOptions = Schema.Struct({\n algorithms: Schema.Array(Algorithm).check(Schema.isMinLength(1)),\n});\n\nexport type VerifyOptions = typeof VerifyOptions.Type;\n\nexport interface Verified {\n readonly protectedHeader: Header;\n readonly payload: Redacted.Redacted<Uint8Array>;\n}\n\nexport type Requirements = Hmac | Signature;\n\nconst readHeader = Effect.fnUntraced(function* (input: unknown) {\n const raw = yield* parse(JsonObject, input, \"header\");\n\n // No extension handlers are installed. These options change authenticated\n // processing and may never be silently stripped by the structural schema.\n if (raw.crit !== undefined || raw.b64 !== undefined)\n return yield* InvalidToken.make({ reason: \"header\" });\n\n return yield* parse(Header, raw, \"header\");\n});\n\nconst parseCompact = Effect.fnUntraced(function* (\n token: Redacted.Redacted<string>,\n options: VerifyOptions,\n) {\n const policy = yield* parse(VerifyOptions, options, \"parameters\");\n const [protectedPart, payloadPart, signaturePart] = yield* split(token, 3);\n\n if (protectedPart.length > 4096) return yield* InvalidToken.make({ reason: \"header\" });\n const protectedHeader = yield* readHeader(yield* json(yield* decode(protectedPart), \"header\"));\n\n if (!policy.algorithms.includes(protectedHeader.alg)) return yield* AlgorithmNotAllowed.make({});\n const payload = yield* decode(payloadPart);\n const signature = yield* decode(signaturePart);\n\n return {\n protectedHeader,\n payload,\n signature,\n signingInput: utf8(`${protectedPart}.${payloadPart}`),\n };\n});\n\nconst verifyAsymmetric = Effect.fnUntraced(function* (\n parsed: Effect.Success<ReturnType<typeof parseCompact>>,\n key: PublicKey,\n) {\n const algorithm = parsed.protectedHeader.alg;\n\n if (algorithm === \"HS256\" || key.algorithm !== algorithm) return yield* InvalidKey.make({});\n yield* KeyPolicy.check(key.jwk, algorithm, \"verify\");\n const signatures = yield* Signature;\n\n const valid = yield* signatures.verify({\n algorithm: algorithms[algorithm],\n publicKey: key.material,\n data: parsed.signingInput,\n signature: parsed.signature,\n });\n\n if (!valid) return yield* SignatureVerificationFailed.make({});\n\n return {\n protectedHeader: parsed.protectedHeader,\n payload: Redacted.make(parsed.payload),\n } satisfies Verified;\n});\n\nexport const sign = Effect.fnUntraced(function* (\n payload: Redacted.Redacted<Uint8Array>,\n key: SigningKey,\n input: Header,\n) {\n const header = yield* readHeader(input);\n\n if (key.algorithm !== header.alg) return yield* InvalidKey.make({});\n yield* KeyPolicy.check(yield* reveal<PrivateJwk | SecretJwk>(key.jwk), header.alg, \"sign\");\n\n const data = yield* parse(\n Schema.Uint8Array.check(Schema.isMaxLength(45000)),\n yield* reveal(payload),\n \"payload\",\n );\n\n const protectedPart = Base64Url.encode(\n yield* stringify(header).pipe(Effect.mapError(() => InvalidToken.make({ reason: \"header\" }))),\n );\n\n if (protectedPart.length > 4096) return yield* InvalidToken.make({ reason: \"header\" });\n const signingInput = `${protectedPart}.${Base64Url.encode(data)}`;\n let signature: Uint8Array;\n\n if (key._tag === \"SecretKey\") {\n if (header.alg !== \"HS256\") return yield* InvalidKey.make({});\n const hmac = yield* Hmac;\n\n signature = yield* hmac.sign({\n algorithm: \"SHA-256\",\n key: key.material,\n data: utf8(signingInput),\n });\n } else {\n if (header.alg === \"HS256\") return yield* InvalidKey.make({});\n const signatures = yield* Signature;\n\n signature = yield* signatures.sign({\n algorithm: algorithms[header.alg],\n privateKey: key.material,\n data: utf8(signingInput),\n });\n }\n\n return Redacted.make(`${signingInput}.${Base64Url.encode(signature)}`);\n});\n\nexport const verify = Effect.fnUntraced(function* (\n token: Redacted.Redacted<string>,\n key: VerificationKey,\n options: VerifyOptions,\n) {\n const parsed = yield* parseCompact(token, options);\n\n if (key._tag !== \"SecretKey\") return yield* verifyAsymmetric(parsed, key);\n const algorithm = parsed.protectedHeader.alg;\n\n if (key.algorithm !== algorithm) return yield* InvalidKey.make({});\n yield* KeyPolicy.check(yield* reveal(key.jwk), algorithm, \"verify\");\n if (algorithm !== \"HS256\") return yield* InvalidKey.make({});\n const hmac = yield* Hmac;\n\n const valid = yield* hmac.verify({\n algorithm: \"SHA-256\",\n key: key.material,\n data: parsed.signingInput,\n tag: parsed.signature,\n });\n\n if (!valid) return yield* SignatureVerificationFailed.make({});\n\n return {\n protectedHeader: parsed.protectedHeader,\n payload: Redacted.make(parsed.payload),\n } satisfies Verified;\n});\n\nexport const verifyWithKeySet = Effect.fnUntraced(function* (\n token: Redacted.Redacted<string>,\n options: VerifyOptions,\n) {\n const parsed = yield* parseCompact(token, options);\n\n if (parsed.protectedHeader.alg === \"HS256\") return yield* InvalidKey.make({});\n const keys = yield* Jwks;\n\n const key = yield* keys.resolve({\n algorithm: parsed.protectedHeader.alg,\n ...(parsed.protectedHeader.kid === undefined ? {} : { kid: parsed.protectedHeader.kid }),\n });\n\n return yield* verifyAsymmetric(parsed, key);\n});\n"],"mappings":"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"}
|
package/dist/Jwt.d.mts
ADDED
|
@@ -0,0 +1,69 @@
|
|
|
1
|
+
import { ClaimValidationFailed, InvalidToken, VerifyError } from "./Errors.mjs";
|
|
2
|
+
import { SigningKey, VerificationKey } from "./Jwk.mjs";
|
|
3
|
+
import { Jwks } from "./Jwks.mjs";
|
|
4
|
+
import { Header, Requirements } from "./Jws.mjs";
|
|
5
|
+
import { Effect, Redacted, Schema } from "effect";
|
|
6
|
+
import { Signature } from "@yielded/crypto/Signature";
|
|
7
|
+
//#region src/Jwt.d.ts
|
|
8
|
+
export declare const RegisteredClaims: Schema.Struct<{
|
|
9
|
+
readonly iss: Schema.optionalKey<Schema.String>;
|
|
10
|
+
readonly sub: Schema.optionalKey<Schema.String>;
|
|
11
|
+
readonly aud: Schema.optionalKey<Schema.Union<readonly [Schema.String, Schema.$Array<Schema.String>]>>;
|
|
12
|
+
readonly exp: Schema.optionalKey<Schema.Finite>;
|
|
13
|
+
readonly nbf: Schema.optionalKey<Schema.Finite>;
|
|
14
|
+
readonly iat: Schema.optionalKey<Schema.Finite>;
|
|
15
|
+
readonly jti: Schema.optionalKey<Schema.String>;
|
|
16
|
+
}>;
|
|
17
|
+
export type RegisteredClaims = typeof RegisteredClaims.Type;
|
|
18
|
+
export declare const VerifyOptions: Schema.Struct<{
|
|
19
|
+
readonly algorithms: Schema.$Array<Schema.Literals<readonly ["HS256", "RS256", "PS256", "ES256", "EdDSA"]>>;
|
|
20
|
+
readonly issuer: Schema.optionalKey<Schema.Union<readonly [Schema.String, Schema.$Array<Schema.String>]>>;
|
|
21
|
+
readonly audience: Schema.optionalKey<Schema.Union<readonly [Schema.String, Schema.$Array<Schema.String>]>>;
|
|
22
|
+
readonly subject: Schema.optionalKey<Schema.String>;
|
|
23
|
+
readonly typ: Schema.optionalKey<Schema.NonEmptyString>;
|
|
24
|
+
readonly clockTolerance: Schema.optionalKey<Schema.Finite>;
|
|
25
|
+
readonly maxTokenAge: Schema.optionalKey<Schema.Finite>;
|
|
26
|
+
readonly requiredClaims: Schema.optionalKey<Schema.$Array<Schema.Literals<readonly ["iss", "sub", "aud", "exp", "nbf", "iat", "jti"]>>>;
|
|
27
|
+
}>;
|
|
28
|
+
export type VerifyOptions = typeof VerifyOptions.Type;
|
|
29
|
+
export interface Verified<A> {
|
|
30
|
+
readonly claims: A;
|
|
31
|
+
readonly registeredClaims: RegisteredClaims;
|
|
32
|
+
readonly protectedHeader: Header;
|
|
33
|
+
}
|
|
34
|
+
export type JwtError = VerifyError | ClaimValidationFailed | Schema.SchemaError;
|
|
35
|
+
export declare const sign: <S extends Schema.Constraint>(schema: S, claims: S["Type"], key: SigningKey, header: {
|
|
36
|
+
readonly alg: "ES256" | "EdDSA" | "HS256" | "PS256" | "RS256";
|
|
37
|
+
readonly kid?: string;
|
|
38
|
+
readonly typ?: string;
|
|
39
|
+
readonly cty?: string;
|
|
40
|
+
}) => Effect.Effect<Redacted.Redacted<string>, JwtError, S["EncodingServices"] | Requirements>;
|
|
41
|
+
export declare const verify: <S extends Schema.Constraint>(schema: S, token: Redacted.Redacted<string>, key: VerificationKey, options: {
|
|
42
|
+
readonly algorithms: readonly ("ES256" | "EdDSA" | "HS256" | "PS256" | "RS256")[];
|
|
43
|
+
readonly issuer?: string | readonly string[];
|
|
44
|
+
readonly audience?: string | readonly string[];
|
|
45
|
+
readonly subject?: string;
|
|
46
|
+
readonly typ?: string;
|
|
47
|
+
readonly clockTolerance?: number;
|
|
48
|
+
readonly maxTokenAge?: number;
|
|
49
|
+
readonly requiredClaims?: readonly ("aud" | "exp" | "iat" | "iss" | "jti" | "nbf" | "sub")[];
|
|
50
|
+
}) => Effect.Effect<Verified<S["Type"]>, JwtError, S["DecodingServices"] | Requirements>;
|
|
51
|
+
export declare const verifyWithKeySet: <S extends Schema.Constraint>(schema: S, token: Redacted.Redacted<string>, options: {
|
|
52
|
+
readonly algorithms: readonly ("ES256" | "EdDSA" | "HS256" | "PS256" | "RS256")[];
|
|
53
|
+
readonly issuer?: string | readonly string[];
|
|
54
|
+
readonly audience?: string | readonly string[];
|
|
55
|
+
readonly subject?: string;
|
|
56
|
+
readonly typ?: string;
|
|
57
|
+
readonly clockTolerance?: number;
|
|
58
|
+
readonly maxTokenAge?: number;
|
|
59
|
+
readonly requiredClaims?: readonly ("aud" | "exp" | "iat" | "iss" | "jti" | "nbf" | "sub")[];
|
|
60
|
+
}) => Effect.Effect<Verified<S["Type"]>, JwtError, Jwks | Signature | S["DecodingServices"]>;
|
|
61
|
+
/** Parsing only. This result does not establish authenticity or validate claims. */
|
|
62
|
+
export declare const decodeUnverified: (token: Redacted.Redacted<string>) => Effect.Effect<{
|
|
63
|
+
readonly _tag: "UnverifiedJwt";
|
|
64
|
+
readonly claims: Redacted.Redacted<{
|
|
65
|
+
readonly [x: string]: Schema.Json;
|
|
66
|
+
}>;
|
|
67
|
+
}, InvalidToken, never>;
|
|
68
|
+
//#endregion
|
|
69
|
+
//# sourceMappingURL=Jwt.d.mts.map
|
package/dist/Jwt.mjs
ADDED
|
@@ -0,0 +1,145 @@
|
|
|
1
|
+
import { ClaimValidationFailed } from "./Errors.mjs";
|
|
2
|
+
import { JsonObject, decode, json, parse, reveal, split, stringify, utf8 } from "./internal/encoding.mjs";
|
|
3
|
+
import { VerifyOptions as VerifyOptions$1, sign as sign$1, verify as verify$1, verifyWithKeySet as verifyWithKeySet$1 } from "./Jws.mjs";
|
|
4
|
+
import { Clock, Effect, Redacted, Schema } from "effect";
|
|
5
|
+
//#region src/Jwt.ts
|
|
6
|
+
const RegisteredClaims = Schema.Struct({
|
|
7
|
+
iss: Schema.optionalKey(Schema.String),
|
|
8
|
+
sub: Schema.optionalKey(Schema.String),
|
|
9
|
+
aud: Schema.optionalKey(Schema.Union([Schema.String, Schema.Array(Schema.String)])),
|
|
10
|
+
exp: Schema.optionalKey(Schema.Finite),
|
|
11
|
+
nbf: Schema.optionalKey(Schema.Finite),
|
|
12
|
+
iat: Schema.optionalKey(Schema.Finite),
|
|
13
|
+
jti: Schema.optionalKey(Schema.String)
|
|
14
|
+
});
|
|
15
|
+
const expected = Schema.Union([Schema.String, Schema.Array(Schema.String).check(Schema.isMinLength(1))]);
|
|
16
|
+
const seconds = Schema.Finite.check(Schema.isGreaterThanOrEqualTo(0));
|
|
17
|
+
const VerifyOptions = Schema.Struct({
|
|
18
|
+
...VerifyOptions$1.fields,
|
|
19
|
+
issuer: Schema.optionalKey(expected),
|
|
20
|
+
audience: Schema.optionalKey(expected),
|
|
21
|
+
subject: Schema.optionalKey(Schema.String),
|
|
22
|
+
typ: Schema.optionalKey(Schema.NonEmptyString),
|
|
23
|
+
clockTolerance: Schema.optionalKey(seconds),
|
|
24
|
+
maxTokenAge: Schema.optionalKey(seconds),
|
|
25
|
+
requiredClaims: Schema.optionalKey(Schema.Array(Schema.Literals([
|
|
26
|
+
"iss",
|
|
27
|
+
"sub",
|
|
28
|
+
"aud",
|
|
29
|
+
"exp",
|
|
30
|
+
"nbf",
|
|
31
|
+
"iat",
|
|
32
|
+
"jti"
|
|
33
|
+
])))
|
|
34
|
+
});
|
|
35
|
+
const checkClaims = Effect.fnUntraced(function* (claims, header, options) {
|
|
36
|
+
const registered = yield* Schema.decodeUnknownEffect(RegisteredClaims)(claims, { reportInput: false }).pipe(Effect.mapError(() => ClaimValidationFailed.make({
|
|
37
|
+
claim: "registered",
|
|
38
|
+
reason: "invalid"
|
|
39
|
+
})));
|
|
40
|
+
for (const claim of options.requiredClaims ?? []) if (registered[claim] === void 0) return yield* ClaimValidationFailed.make({
|
|
41
|
+
claim,
|
|
42
|
+
reason: "missing"
|
|
43
|
+
});
|
|
44
|
+
if (options.issuer !== void 0) {
|
|
45
|
+
if (registered.iss === void 0) return yield* ClaimValidationFailed.make({
|
|
46
|
+
claim: "iss",
|
|
47
|
+
reason: "missing"
|
|
48
|
+
});
|
|
49
|
+
if (!(typeof options.issuer === "string" ? [options.issuer] : options.issuer).includes(registered.iss)) return yield* ClaimValidationFailed.make({
|
|
50
|
+
claim: "iss",
|
|
51
|
+
reason: "mismatch"
|
|
52
|
+
});
|
|
53
|
+
}
|
|
54
|
+
if (options.subject !== void 0) {
|
|
55
|
+
if (registered.sub === void 0) return yield* ClaimValidationFailed.make({
|
|
56
|
+
claim: "sub",
|
|
57
|
+
reason: "missing"
|
|
58
|
+
});
|
|
59
|
+
if (options.subject !== registered.sub) return yield* ClaimValidationFailed.make({
|
|
60
|
+
claim: "sub",
|
|
61
|
+
reason: "mismatch"
|
|
62
|
+
});
|
|
63
|
+
}
|
|
64
|
+
if (options.audience !== void 0) {
|
|
65
|
+
if (registered.aud === void 0) return yield* ClaimValidationFailed.make({
|
|
66
|
+
claim: "aud",
|
|
67
|
+
reason: "missing"
|
|
68
|
+
});
|
|
69
|
+
const expected = typeof options.audience === "string" ? [options.audience] : options.audience;
|
|
70
|
+
const actual = typeof registered.aud === "string" ? [registered.aud] : registered.aud;
|
|
71
|
+
if (!expected.some((audience) => actual.includes(audience))) return yield* ClaimValidationFailed.make({
|
|
72
|
+
claim: "aud",
|
|
73
|
+
reason: "mismatch"
|
|
74
|
+
});
|
|
75
|
+
}
|
|
76
|
+
const normalizedType = (value) => {
|
|
77
|
+
const type = value.toLowerCase();
|
|
78
|
+
return type.includes("/") ? type : `application/${type}`;
|
|
79
|
+
};
|
|
80
|
+
if (options.typ !== void 0 && (header.typ === void 0 || normalizedType(header.typ) !== normalizedType(options.typ))) return yield* ClaimValidationFailed.make({
|
|
81
|
+
claim: "typ",
|
|
82
|
+
reason: "mismatch"
|
|
83
|
+
});
|
|
84
|
+
const now = Math.floor((yield* Clock.currentTimeMillis) / 1e3);
|
|
85
|
+
const tolerance = options.clockTolerance ?? 0;
|
|
86
|
+
if (registered.exp !== void 0 && registered.exp <= now - tolerance) return yield* ClaimValidationFailed.make({
|
|
87
|
+
claim: "exp",
|
|
88
|
+
reason: "expired"
|
|
89
|
+
});
|
|
90
|
+
if (registered.nbf !== void 0 && registered.nbf > now + tolerance) return yield* ClaimValidationFailed.make({
|
|
91
|
+
claim: "nbf",
|
|
92
|
+
reason: "notYetValid"
|
|
93
|
+
});
|
|
94
|
+
if (options.maxTokenAge !== void 0) {
|
|
95
|
+
if (registered.iat === void 0) return yield* ClaimValidationFailed.make({
|
|
96
|
+
claim: "iat",
|
|
97
|
+
reason: "missing"
|
|
98
|
+
});
|
|
99
|
+
if (registered.iat > now + tolerance) return yield* ClaimValidationFailed.make({
|
|
100
|
+
claim: "iat",
|
|
101
|
+
reason: "future"
|
|
102
|
+
});
|
|
103
|
+
if (now - registered.iat - tolerance > options.maxTokenAge) return yield* ClaimValidationFailed.make({
|
|
104
|
+
claim: "iat",
|
|
105
|
+
reason: "tooOld"
|
|
106
|
+
});
|
|
107
|
+
}
|
|
108
|
+
return registered;
|
|
109
|
+
});
|
|
110
|
+
const decodeClaims = Effect.fnUntraced(function* (schema, verified, options) {
|
|
111
|
+
const wire = yield* json(yield* reveal(verified.payload), "payload");
|
|
112
|
+
const registeredClaims = yield* checkClaims(wire, verified.protectedHeader, options);
|
|
113
|
+
return {
|
|
114
|
+
claims: yield* Schema.decodeUnknownEffect(schema)(wire, { reportInput: false }),
|
|
115
|
+
registeredClaims,
|
|
116
|
+
protectedHeader: verified.protectedHeader
|
|
117
|
+
};
|
|
118
|
+
});
|
|
119
|
+
const sign = Effect.fnUntraced(function* (schema, claims, key, header) {
|
|
120
|
+
const encoded = yield* Schema.encodeEffect(schema)(claims, { reportInput: false });
|
|
121
|
+
const wire = yield* Schema.decodeUnknownEffect(JsonObject)(encoded, { reportInput: false });
|
|
122
|
+
yield* Schema.decodeUnknownEffect(RegisteredClaims)(wire, { reportInput: false });
|
|
123
|
+
return yield* sign$1(Redacted.make(utf8(yield* stringify(wire))), key, header);
|
|
124
|
+
});
|
|
125
|
+
const verify = Effect.fnUntraced(function* (schema, token, key, options) {
|
|
126
|
+
const policy = yield* parse(VerifyOptions, options, "parameters");
|
|
127
|
+
return yield* decodeClaims(schema, yield* verify$1(token, key, policy), policy);
|
|
128
|
+
});
|
|
129
|
+
const verifyWithKeySet = Effect.fnUntraced(function* (schema, token, options) {
|
|
130
|
+
const policy = yield* parse(VerifyOptions, options, "parameters");
|
|
131
|
+
return yield* decodeClaims(schema, yield* verifyWithKeySet$1(token, policy), policy);
|
|
132
|
+
});
|
|
133
|
+
/** Parsing only. This result does not establish authenticity or validate claims. */
|
|
134
|
+
const decodeUnverified = Effect.fnUntraced(function* (token) {
|
|
135
|
+
const [, payload] = yield* split(token, 3);
|
|
136
|
+
const claims = yield* json(yield* decode(payload), "payload");
|
|
137
|
+
return {
|
|
138
|
+
_tag: "UnverifiedJwt",
|
|
139
|
+
claims: Redacted.make(claims)
|
|
140
|
+
};
|
|
141
|
+
});
|
|
142
|
+
//#endregion
|
|
143
|
+
export { RegisteredClaims, VerifyOptions, decodeUnverified, sign, verify, verifyWithKeySet };
|
|
144
|
+
|
|
145
|
+
//# sourceMappingURL=Jwt.mjs.map
|
package/dist/Jwt.mjs.map
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"Jwt.mjs","names":["JwsVerifyOptions","Jws.sign","Jws.verify","Jws.verifyWithKeySet"],"sources":["../src/Jwt.ts"],"sourcesContent":["import type { Signature } from \"@yielded/crypto/Signature\";\nimport { Clock, Effect, Redacted, Schema } from \"effect\";\n\nimport { ClaimValidationFailed, type VerifyError } from \"./Errors\";\nimport {\n decode,\n json,\n JsonObject,\n parse,\n reveal,\n split,\n stringify,\n utf8,\n} from \"./internal/encoding\";\nimport type { SigningKey, VerificationKey } from \"./Jwk\";\nimport type { Jwks } from \"./Jwks\";\nimport { type Header, type Requirements, VerifyOptions as JwsVerifyOptions } from \"./Jws\";\nimport * as Jws from \"./Jws\";\n\nexport const RegisteredClaims = Schema.Struct({\n iss: Schema.optionalKey(Schema.String),\n sub: Schema.optionalKey(Schema.String),\n aud: Schema.optionalKey(Schema.Union([Schema.String, Schema.Array(Schema.String)])),\n exp: Schema.optionalKey(Schema.Finite),\n nbf: Schema.optionalKey(Schema.Finite),\n iat: Schema.optionalKey(Schema.Finite),\n jti: Schema.optionalKey(Schema.String),\n});\n\nexport type RegisteredClaims = typeof RegisteredClaims.Type;\n\nconst expected = Schema.Union([\n Schema.String,\n Schema.Array(Schema.String).check(Schema.isMinLength(1)),\n]);\n\nconst seconds = Schema.Finite.check(Schema.isGreaterThanOrEqualTo(0));\n\nexport const VerifyOptions = Schema.Struct({\n ...JwsVerifyOptions.fields,\n issuer: Schema.optionalKey(expected),\n audience: Schema.optionalKey(expected),\n subject: Schema.optionalKey(Schema.String),\n typ: Schema.optionalKey(Schema.NonEmptyString),\n clockTolerance: Schema.optionalKey(seconds),\n maxTokenAge: Schema.optionalKey(seconds),\n requiredClaims: Schema.optionalKey(\n Schema.Array(Schema.Literals([\"iss\", \"sub\", \"aud\", \"exp\", \"nbf\", \"iat\", \"jti\"])),\n ),\n});\n\nexport type VerifyOptions = typeof VerifyOptions.Type;\n\nexport interface Verified<A> {\n readonly claims: A;\n readonly registeredClaims: RegisteredClaims;\n readonly protectedHeader: Header;\n}\n\nexport type JwtError = VerifyError | ClaimValidationFailed | Schema.SchemaError;\n\nconst checkClaims = Effect.fnUntraced(function* (\n claims: Readonly<Record<string, Schema.Json>>,\n header: Header,\n options: VerifyOptions,\n) {\n const registered = yield* Schema.decodeUnknownEffect(RegisteredClaims)(claims, {\n reportInput: false,\n }).pipe(\n Effect.mapError(() => ClaimValidationFailed.make({ claim: \"registered\", reason: \"invalid\" })),\n );\n\n for (const claim of options.requiredClaims ?? []) {\n if (registered[claim] === undefined)\n return yield* ClaimValidationFailed.make({ claim, reason: \"missing\" });\n }\n if (options.issuer !== undefined) {\n if (registered.iss === undefined)\n return yield* ClaimValidationFailed.make({ claim: \"iss\", reason: \"missing\" });\n const expected = typeof options.issuer === \"string\" ? [options.issuer] : options.issuer;\n\n if (!expected.includes(registered.iss))\n return yield* ClaimValidationFailed.make({ claim: \"iss\", reason: \"mismatch\" });\n }\n if (options.subject !== undefined) {\n if (registered.sub === undefined)\n return yield* ClaimValidationFailed.make({ claim: \"sub\", reason: \"missing\" });\n if (options.subject !== registered.sub)\n return yield* ClaimValidationFailed.make({ claim: \"sub\", reason: \"mismatch\" });\n }\n if (options.audience !== undefined) {\n if (registered.aud === undefined)\n return yield* ClaimValidationFailed.make({ claim: \"aud\", reason: \"missing\" });\n const expected = typeof options.audience === \"string\" ? [options.audience] : options.audience;\n const actual = typeof registered.aud === \"string\" ? [registered.aud] : registered.aud;\n\n if (!expected.some((audience) => actual.includes(audience)))\n return yield* ClaimValidationFailed.make({ claim: \"aud\", reason: \"mismatch\" });\n }\n\n const normalizedType = (value: string) => {\n const type = value.toLowerCase();\n\n return type.includes(\"/\") ? type : `application/${type}`;\n };\n\n if (\n options.typ !== undefined &&\n (header.typ === undefined || normalizedType(header.typ) !== normalizedType(options.typ))\n )\n return yield* ClaimValidationFailed.make({ claim: \"typ\", reason: \"mismatch\" });\n const now = Math.floor((yield* Clock.currentTimeMillis) / 1000);\n const tolerance = options.clockTolerance ?? 0;\n\n if (registered.exp !== undefined && registered.exp <= now - tolerance)\n return yield* ClaimValidationFailed.make({ claim: \"exp\", reason: \"expired\" });\n if (registered.nbf !== undefined && registered.nbf > now + tolerance)\n return yield* ClaimValidationFailed.make({ claim: \"nbf\", reason: \"notYetValid\" });\n if (options.maxTokenAge !== undefined) {\n if (registered.iat === undefined)\n return yield* ClaimValidationFailed.make({ claim: \"iat\", reason: \"missing\" });\n if (registered.iat > now + tolerance)\n return yield* ClaimValidationFailed.make({ claim: \"iat\", reason: \"future\" });\n if (now - registered.iat - tolerance > options.maxTokenAge)\n return yield* ClaimValidationFailed.make({ claim: \"iat\", reason: \"tooOld\" });\n }\n\n return registered;\n});\n\nconst decodeClaims = Effect.fnUntraced(function* <S extends Schema.Constraint>(\n schema: S,\n verified: Jws.Verified,\n options: VerifyOptions,\n) {\n const wire = yield* json(yield* reveal(verified.payload), \"payload\");\n const registeredClaims = yield* checkClaims(wire, verified.protectedHeader, options);\n const claims = yield* Schema.decodeUnknownEffect(schema)(wire, { reportInput: false });\n\n return { claims, registeredClaims, protectedHeader: verified.protectedHeader } satisfies Verified<\n S[\"Type\"]\n >;\n});\n\nexport const sign = Effect.fnUntraced(function* <S extends Schema.Constraint>(\n schema: S,\n claims: S[\"Type\"],\n key: SigningKey,\n header: Header,\n): Effect.fn.Return<Redacted.Redacted<string>, JwtError, Requirements | S[\"EncodingServices\"]> {\n const encoded = yield* Schema.encodeEffect(schema)(claims, { reportInput: false });\n const wire = yield* Schema.decodeUnknownEffect(JsonObject)(encoded, { reportInput: false });\n\n yield* Schema.decodeUnknownEffect(RegisteredClaims)(wire, { reportInput: false });\n\n return yield* Jws.sign(Redacted.make(utf8(yield* stringify(wire))), key, header);\n});\n\nexport const verify = Effect.fnUntraced(function* <S extends Schema.Constraint>(\n schema: S,\n token: Redacted.Redacted<string>,\n key: VerificationKey,\n options: VerifyOptions,\n): Effect.fn.Return<Verified<S[\"Type\"]>, JwtError, Requirements | S[\"DecodingServices\"]> {\n // Capture all policy before asynchronous cryptography or key resolution.\n const policy = yield* parse(VerifyOptions, options, \"parameters\");\n\n return yield* decodeClaims(schema, yield* Jws.verify(token, key, policy), policy);\n});\n\nexport const verifyWithKeySet = Effect.fnUntraced(function* <S extends Schema.Constraint>(\n schema: S,\n token: Redacted.Redacted<string>,\n options: VerifyOptions,\n): Effect.fn.Return<Verified<S[\"Type\"]>, JwtError, Signature | Jwks | S[\"DecodingServices\"]> {\n const policy = yield* parse(VerifyOptions, options, \"parameters\");\n\n return yield* decodeClaims(schema, yield* Jws.verifyWithKeySet(token, policy), policy);\n});\n\n/** Parsing only. This result does not establish authenticity or validate claims. */\nexport const decodeUnverified = Effect.fnUntraced(function* (token: Redacted.Redacted<string>) {\n const [, payload] = yield* split(token, 3);\n const claims = yield* json(yield* decode(payload), \"payload\");\n\n return { _tag: \"UnverifiedJwt\", claims: Redacted.make(claims) } as const;\n});\n"],"mappings":"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"}
|
package/dist/index.d.mts
ADDED
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
import * as Errors from "./Errors.mjs";
|
|
2
|
+
import * as Jwk from "./Jwk.mjs";
|
|
3
|
+
import * as Jwks from "./Jwks.mjs";
|
|
4
|
+
import * as Jws from "./Jws.mjs";
|
|
5
|
+
import * as Jwt from "./Jwt.mjs";
|
|
6
|
+
import * as Jwe from "./Jwe.mjs";
|
|
7
|
+
export { Errors, Jwe, Jwk, Jwks, Jws, Jwt };
|
package/dist/index.mjs
ADDED
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
import * as Errors from "./Errors.mjs";
|
|
2
|
+
import * as Jwk from "./Jwk.mjs";
|
|
3
|
+
import * as Jwks from "./Jwks.mjs";
|
|
4
|
+
import * as Jws from "./Jws.mjs";
|
|
5
|
+
import * as Jwt from "./Jwt.mjs";
|
|
6
|
+
import * as Jwe from "./Jwe.mjs";
|
|
7
|
+
export { Errors, Jwe, Jwk, Jwks, Jws, Jwt };
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
//#region src/internal/algorithms.ts
|
|
2
|
+
const algorithms = {
|
|
3
|
+
ES256: "ECDSA-P256-SHA256",
|
|
4
|
+
RS256: "RSASSA-PKCS1-v1_5-SHA256",
|
|
5
|
+
PS256: "RSA-PSS-SHA256",
|
|
6
|
+
EdDSA: "Ed25519"
|
|
7
|
+
};
|
|
8
|
+
//#endregion
|
|
9
|
+
export { algorithms };
|
|
10
|
+
|
|
11
|
+
//# sourceMappingURL=algorithms.mjs.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"algorithms.mjs","names":[],"sources":["../../src/internal/algorithms.ts"],"sourcesContent":["import type { Algorithm } from \"@yielded/crypto/Signature\";\n\nimport type { AsymmetricAlgorithm } from \"../Jwk\";\n\nexport const algorithms = {\n ES256: \"ECDSA-P256-SHA256\",\n RS256: \"RSASSA-PKCS1-v1_5-SHA256\",\n PS256: \"RSA-PSS-SHA256\",\n EdDSA: \"Ed25519\",\n} satisfies Record<AsymmetricAlgorithm, Algorithm>;\n"],"mappings":";AAIA,MAAa,aAAa;CACxB,OAAO;CACP,OAAO;CACP,OAAO;CACP,OAAO;AACT"}
|
|
@@ -0,0 +1,39 @@
|
|
|
1
|
+
import { InvalidToken } from "../Errors.mjs";
|
|
2
|
+
import { Effect, Redacted, Result, Schema } from "effect";
|
|
3
|
+
import { Base64Url } from "effect/encoding";
|
|
4
|
+
//#region src/internal/encoding.ts
|
|
5
|
+
const JsonObject = Schema.Record(Schema.String, Schema.Json);
|
|
6
|
+
const utf8 = (value) => new TextEncoder().encode(value);
|
|
7
|
+
const Segment = Schema.String.check(Schema.isPattern(/^[A-Za-z0-9_-]*$/), Schema.isMaxLength(65536));
|
|
8
|
+
const Token = Schema.String.check(Schema.isMinLength(1), Schema.isMaxLength(65536));
|
|
9
|
+
const parse = (schema, input, reason) => Effect.suspend(() => Schema.decodeUnknownEffect(schema)(input, { reportInput: false })).pipe(Effect.mapError(() => InvalidToken.make({ reason })), Effect.catchDefect(() => InvalidToken.make({ reason })));
|
|
10
|
+
const reveal = (value) => Effect.try({
|
|
11
|
+
try: () => Redacted.value(value),
|
|
12
|
+
catch: () => InvalidToken.make({ reason: "parameters" })
|
|
13
|
+
});
|
|
14
|
+
const decode = Effect.fnUntraced(function* (value, reason = "serialization") {
|
|
15
|
+
yield* parse(Segment, value, reason);
|
|
16
|
+
const bytes = yield* Result.match(Base64Url.decode(value), {
|
|
17
|
+
onFailure: () => Effect.fail(InvalidToken.make({ reason })),
|
|
18
|
+
onSuccess: Effect.succeed
|
|
19
|
+
});
|
|
20
|
+
if (Base64Url.encode(bytes) !== value) return yield* InvalidToken.make({ reason });
|
|
21
|
+
return bytes;
|
|
22
|
+
});
|
|
23
|
+
const json = Effect.fnUntraced(function* (bytes, reason) {
|
|
24
|
+
const value = yield* Effect.try({
|
|
25
|
+
try: () => new TextDecoder("utf-8", { fatal: true }).decode(bytes),
|
|
26
|
+
catch: () => InvalidToken.make({ reason })
|
|
27
|
+
});
|
|
28
|
+
return yield* parse(Schema.fromJsonString(JsonObject), value, reason);
|
|
29
|
+
});
|
|
30
|
+
const stringify = (value) => Schema.encodeEffect(Schema.fromJsonString(JsonObject))(value, { reportInput: false });
|
|
31
|
+
const split = Effect.fnUntraced(function* (input, count) {
|
|
32
|
+
const parts = (yield* parse(Token, yield* reveal(input), "serialization")).split(".");
|
|
33
|
+
if (parts.length !== count) return yield* InvalidToken.make({ reason: "serialization" });
|
|
34
|
+
return parts;
|
|
35
|
+
});
|
|
36
|
+
//#endregion
|
|
37
|
+
export { JsonObject, decode, json, parse, reveal, split, stringify, utf8 };
|
|
38
|
+
|
|
39
|
+
//# sourceMappingURL=encoding.mjs.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"encoding.mjs","names":[],"sources":["../../src/internal/encoding.ts"],"sourcesContent":["import { Effect, Redacted, Result, Schema } from \"effect\";\nimport { Base64Url } from \"effect/encoding\";\n\nimport { InvalidToken } from \"../Errors\";\n\nexport const JsonObject = Schema.Record(Schema.String, Schema.Json);\nexport const utf8 = (value: string) => new TextEncoder().encode(value);\n\nconst Segment = Schema.String.check(\n Schema.isPattern(/^[A-Za-z0-9_-]*$/),\n Schema.isMaxLength(65536),\n);\n\nconst Token = Schema.String.check(Schema.isMinLength(1), Schema.isMaxLength(65536));\n\nexport const parse = <S extends Schema.Constraint>(\n schema: S,\n input: unknown,\n reason: InvalidToken[\"reason\"],\n) =>\n Effect.suspend(() => Schema.decodeUnknownEffect(schema)(input, { reportInput: false })).pipe(\n Effect.mapError(() => InvalidToken.make({ reason })),\n // Arbitrary nested input can still invoke foreign getters during decoding.\n Effect.catchDefect(() => InvalidToken.make({ reason })),\n );\n\nexport const reveal = <A>(value: Redacted.Redacted<A>) =>\n Effect.try({\n try: () => Redacted.value(value),\n catch: () => InvalidToken.make({ reason: \"parameters\" }),\n });\n\nexport const decode = Effect.fnUntraced(function* (\n value: string,\n reason: InvalidToken[\"reason\"] = \"serialization\",\n) {\n yield* parse(Segment, value, reason);\n\n const bytes = yield* Result.match(Base64Url.decode(value), {\n onFailure: () => Effect.fail(InvalidToken.make({ reason })),\n onSuccess: Effect.succeed,\n });\n\n // Canonical, unpadded base64url also rejects non-zero unused bits.\n if (Base64Url.encode(bytes) !== value) return yield* InvalidToken.make({ reason });\n\n return bytes;\n});\n\nexport const json = Effect.fnUntraced(function* (\n bytes: Uint8Array,\n reason: InvalidToken[\"reason\"],\n) {\n const value = yield* Effect.try({\n try: () => new TextDecoder(\"utf-8\", { fatal: true }).decode(bytes),\n catch: () => InvalidToken.make({ reason }),\n });\n\n return yield* parse(Schema.fromJsonString(JsonObject), value, reason);\n});\n\nexport const stringify = (value: Readonly<Record<string, Schema.Json>>) =>\n Schema.encodeEffect(Schema.fromJsonString(JsonObject))(value, { reportInput: false });\n\nexport const split = Effect.fnUntraced(function* (input: Redacted.Redacted<string>, count: number) {\n const value = yield* parse(Token, yield* reveal(input), \"serialization\");\n const parts = value.split(\".\");\n\n if (parts.length !== count) return yield* InvalidToken.make({ reason: \"serialization\" });\n\n return parts;\n});\n"],"mappings":";;;;AAKA,MAAa,aAAa,OAAO,OAAO,OAAO,QAAQ,OAAO,IAAI;AAClE,MAAa,QAAQ,UAAkB,IAAI,YAAY,CAAC,CAAC,OAAO,KAAK;AAErE,MAAM,UAAU,OAAO,OAAO,MAC5B,OAAO,UAAU,kBAAkB,GACnC,OAAO,YAAY,KAAK,CAC1B;AAEA,MAAM,QAAQ,OAAO,OAAO,MAAM,OAAO,YAAY,CAAC,GAAG,OAAO,YAAY,KAAK,CAAC;AAElF,MAAa,SACX,QACA,OACA,WAEA,OAAO,cAAc,OAAO,oBAAoB,MAAM,CAAC,CAAC,OAAO,EAAE,aAAa,MAAM,CAAC,CAAC,CAAC,CAAC,KACtF,OAAO,eAAe,aAAa,KAAK,EAAE,OAAO,CAAC,CAAC,GAEnD,OAAO,kBAAkB,aAAa,KAAK,EAAE,OAAO,CAAC,CAAC,CACxD;AAEF,MAAa,UAAa,UACxB,OAAO,IAAI;CACT,WAAW,SAAS,MAAM,KAAK;CAC/B,aAAa,aAAa,KAAK,EAAE,QAAQ,aAAa,CAAC;AACzD,CAAC;AAEH,MAAa,SAAS,OAAO,WAAW,WACtC,OACA,SAAiC,iBACjC;CACA,OAAO,MAAM,SAAS,OAAO,MAAM;CAEnC,MAAM,QAAQ,OAAO,OAAO,MAAM,UAAU,OAAO,KAAK,GAAG;EACzD,iBAAiB,OAAO,KAAK,aAAa,KAAK,EAAE,OAAO,CAAC,CAAC;EAC1D,WAAW,OAAO;CACpB,CAAC;CAGD,IAAI,UAAU,OAAO,KAAK,MAAM,OAAO,OAAO,OAAO,aAAa,KAAK,EAAE,OAAO,CAAC;CAEjF,OAAO;AACT,CAAC;AAED,MAAa,OAAO,OAAO,WAAW,WACpC,OACA,QACA;CACA,MAAM,QAAQ,OAAO,OAAO,IAAI;EAC9B,WAAW,IAAI,YAAY,SAAS,EAAE,OAAO,KAAK,CAAC,CAAC,CAAC,OAAO,KAAK;EACjE,aAAa,aAAa,KAAK,EAAE,OAAO,CAAC;CAC3C,CAAC;CAED,OAAO,OAAO,MAAM,OAAO,eAAe,UAAU,GAAG,OAAO,MAAM;AACtE,CAAC;AAED,MAAa,aAAa,UACxB,OAAO,aAAa,OAAO,eAAe,UAAU,CAAC,CAAC,CAAC,OAAO,EAAE,aAAa,MAAM,CAAC;AAEtF,MAAa,QAAQ,OAAO,WAAW,WAAW,OAAkC,OAAe;CAEjG,MAAM,SAAQ,OADO,MAAM,OAAO,OAAO,OAAO,KAAK,GAAG,eAAe,EAAA,CACnD,MAAM,GAAG;CAE7B,IAAI,MAAM,WAAW,OAAO,OAAO,OAAO,aAAa,KAAK,EAAE,QAAQ,gBAAgB,CAAC;CAEvF,OAAO;AACT,CAAC"}
|
|
@@ -0,0 +1,44 @@
|
|
|
1
|
+
import { InvalidKey } from "../Errors.mjs";
|
|
2
|
+
import { Predicate, Result, Schema } from "effect";
|
|
3
|
+
const ArrayLength = Schema.Int.check(Schema.isBetween({
|
|
4
|
+
minimum: 0,
|
|
5
|
+
maximum: 256
|
|
6
|
+
}));
|
|
7
|
+
/** Copy only own fields, evaluating accessors once before Schema validation. */
|
|
8
|
+
const object = (input) => {
|
|
9
|
+
if (!Predicate.isObject(input)) return input;
|
|
10
|
+
const descriptors = Object.getOwnPropertyDescriptors(input);
|
|
11
|
+
const copy = {};
|
|
12
|
+
Object.setPrototypeOf(copy, null);
|
|
13
|
+
for (const name of Object.getOwnPropertyNames(descriptors)) {
|
|
14
|
+
const descriptor = descriptors[name];
|
|
15
|
+
copy[name] = Object.hasOwn(descriptor, "value") ? descriptor.value : descriptor.get?.call(input);
|
|
16
|
+
}
|
|
17
|
+
return copy;
|
|
18
|
+
};
|
|
19
|
+
const array = (input) => Result.gen(function* () {
|
|
20
|
+
const length = yield* Schema.decodeUnknownResult(ArrayLength)(input.length).pipe(Result.mapError(() => InvalidKey.make({})));
|
|
21
|
+
return Array.from({ length }, (_, index) => Object.getOwnPropertyDescriptor(input, index)).map((descriptor) => descriptor === void 0 ? void 0 : Object.hasOwn(descriptor, "value") ? descriptor.value : descriptor.get?.call(input));
|
|
22
|
+
});
|
|
23
|
+
const jwk = (input) => Result.gen(function* () {
|
|
24
|
+
const copy = object(input);
|
|
25
|
+
if (Predicate.isObject(copy) && Array.isArray(copy.key_ops)) copy.key_ops = yield* array(copy.key_ops);
|
|
26
|
+
return copy;
|
|
27
|
+
});
|
|
28
|
+
const keySet = (input) => Result.gen(function* () {
|
|
29
|
+
const copy = object(input);
|
|
30
|
+
if (Predicate.isObject(copy) && Array.isArray(copy.keys)) {
|
|
31
|
+
const keys = yield* array(copy.keys);
|
|
32
|
+
copy.keys = yield* Result.all(keys.map(jwk));
|
|
33
|
+
}
|
|
34
|
+
return copy;
|
|
35
|
+
});
|
|
36
|
+
/** Struct decoding creates ordinary objects; detach owned output as well. */
|
|
37
|
+
const detach = (value) => {
|
|
38
|
+
if (Predicate.isObject(value)) Object.setPrototypeOf(value, null);
|
|
39
|
+
return value;
|
|
40
|
+
};
|
|
41
|
+
//#endregion
|
|
42
|
+
export { detach, jwk, keySet, object };
|
|
43
|
+
|
|
44
|
+
//# sourceMappingURL=keyInput.mjs.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"keyInput.mjs","names":[],"sources":["../../src/internal/keyInput.ts"],"sourcesContent":["import { Predicate, Result, Schema } from \"effect\";\n\nimport { InvalidKey } from \"../Errors\";\n\nexport const maxArrayLength = 256;\n\nconst ArrayLength = Schema.Int.check(Schema.isBetween({ minimum: 0, maximum: maxArrayLength }));\n\n/** Copy only own fields, evaluating accessors once before Schema validation. */\nexport const object = (input: unknown): unknown => {\n if (!Predicate.isObject(input)) return input;\n const descriptors = Object.getOwnPropertyDescriptors(input);\n const copy: Record<string, unknown> = {};\n\n Object.setPrototypeOf(copy, null);\n for (const name of Object.getOwnPropertyNames(descriptors)) {\n const descriptor = descriptors[name];\n\n copy[name] = Object.hasOwn(descriptor, \"value\")\n ? descriptor.value\n : descriptor.get?.call(input);\n }\n\n return copy;\n};\n\nconst array = (input: ReadonlyArray<unknown>) =>\n Result.gen(function* () {\n const length = yield* Schema.decodeUnknownResult(ArrayLength)(input.length).pipe(\n Result.mapError(() => InvalidKey.make({})),\n );\n\n // Capture bounded own slots before any accessor can mutate the array.\n const descriptors = Array.from({ length }, (_, index) =>\n Object.getOwnPropertyDescriptor(input, index),\n );\n\n return descriptors.map((descriptor) =>\n descriptor === undefined\n ? undefined\n : Object.hasOwn(descriptor, \"value\")\n ? descriptor.value\n : descriptor.get?.call(input),\n );\n });\n\nexport const jwk = (input: unknown) =>\n Result.gen(function* () {\n const copy = object(input);\n\n if (Predicate.isObject(copy) && Array.isArray(copy.key_ops))\n copy.key_ops = yield* array(copy.key_ops);\n\n return copy;\n });\n\nexport const keySet = (input: unknown) =>\n Result.gen(function* () {\n const copy = object(input);\n\n if (Predicate.isObject(copy) && Array.isArray(copy.keys)) {\n const keys = yield* array(copy.keys);\n\n copy.keys = yield* Result.all(keys.map(jwk));\n }\n\n return copy;\n });\n\n/** Struct decoding creates ordinary objects; detach owned output as well. */\nexport const detach = <A>(value: A): A => {\n if (Predicate.isObject(value)) Object.setPrototypeOf(value, null);\n\n return value;\n};\n"],"mappings":";;AAMA,MAAM,cAAc,OAAO,IAAI,MAAM,OAAO,UAAU;CAAE,SAAS;CAAG,SAAA;AAAwB,CAAC,CAAC;;AAG9F,MAAa,UAAU,UAA4B;CACjD,IAAI,CAAC,UAAU,SAAS,KAAK,GAAG,OAAO;CACvC,MAAM,cAAc,OAAO,0BAA0B,KAAK;CAC1D,MAAM,OAAgC,CAAC;CAEvC,OAAO,eAAe,MAAM,IAAI;CAChC,KAAK,MAAM,QAAQ,OAAO,oBAAoB,WAAW,GAAG;EAC1D,MAAM,aAAa,YAAY;EAE/B,KAAK,QAAQ,OAAO,OAAO,YAAY,OAAO,IAC1C,WAAW,QACX,WAAW,KAAK,KAAK,KAAK;CAChC;CAEA,OAAO;AACT;AAEA,MAAM,SAAS,UACb,OAAO,IAAI,aAAa;CACtB,MAAM,SAAS,OAAO,OAAO,oBAAoB,WAAW,CAAC,CAAC,MAAM,MAAM,CAAC,CAAC,KAC1E,OAAO,eAAe,WAAW,KAAK,CAAC,CAAC,CAAC,CAC3C;CAOA,OAJoB,MAAM,KAAK,EAAE,OAAO,IAAI,GAAG,UAC7C,OAAO,yBAAyB,OAAO,KAAK,CAG7B,CAAC,CAAC,KAAK,eACtB,eAAe,KAAA,IACX,KAAA,IACA,OAAO,OAAO,YAAY,OAAO,IAC/B,WAAW,QACX,WAAW,KAAK,KAAK,KAAK,CAClC;AACF,CAAC;AAEH,MAAa,OAAO,UAClB,OAAO,IAAI,aAAa;CACtB,MAAM,OAAO,OAAO,KAAK;CAEzB,IAAI,UAAU,SAAS,IAAI,KAAK,MAAM,QAAQ,KAAK,OAAO,GACxD,KAAK,UAAU,OAAO,MAAM,KAAK,OAAO;CAE1C,OAAO;AACT,CAAC;AAEH,MAAa,UAAU,UACrB,OAAO,IAAI,aAAa;CACtB,MAAM,OAAO,OAAO,KAAK;CAEzB,IAAI,UAAU,SAAS,IAAI,KAAK,MAAM,QAAQ,KAAK,IAAI,GAAG;EACxD,MAAM,OAAO,OAAO,MAAM,KAAK,IAAI;EAEnC,KAAK,OAAO,OAAO,OAAO,IAAI,KAAK,IAAI,GAAG,CAAC;CAC7C;CAEA,OAAO;AACT,CAAC;;AAGH,MAAa,UAAa,UAAgB;CACxC,IAAI,UAAU,SAAS,KAAK,GAAG,OAAO,eAAe,OAAO,IAAI;CAEhE,OAAO;AACT"}
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
import { InvalidKey } from "../Errors.mjs";
|
|
2
|
+
import { Effect } from "effect";
|
|
3
|
+
//#region src/internal/keyPolicy.ts
|
|
4
|
+
const check = (jwk, algorithm, operation) => {
|
|
5
|
+
const use = operation === "sign" || operation === "verify" ? "sig" : "enc";
|
|
6
|
+
return jwk.alg !== void 0 && jwk.alg !== algorithm || jwk.use !== void 0 && jwk.use !== use || jwk.key_ops !== void 0 && !jwk.key_ops.includes(operation) ? Effect.fail(InvalidKey.make({})) : Effect.void;
|
|
7
|
+
};
|
|
8
|
+
//#endregion
|
|
9
|
+
export { check };
|
|
10
|
+
|
|
11
|
+
//# sourceMappingURL=keyPolicy.mjs.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"keyPolicy.mjs","names":[],"sources":["../../src/internal/keyPolicy.ts"],"sourcesContent":["import { Effect } from \"effect\";\n\nimport { InvalidKey } from \"../Errors\";\nimport type { PrivateJwk, PublicJwk, SecretJwk } from \"../Jwk\";\n\nexport const check = (\n jwk: PublicJwk | PrivateJwk | SecretJwk,\n algorithm: string,\n operation: \"sign\" | \"verify\" | \"encrypt\" | \"decrypt\",\n) => {\n const use = operation === \"sign\" || operation === \"verify\" ? \"sig\" : \"enc\";\n\n return (jwk.alg !== undefined && jwk.alg !== algorithm) ||\n (jwk.use !== undefined && jwk.use !== use) ||\n (jwk.key_ops !== undefined && !jwk.key_ops.includes(operation))\n ? Effect.fail(InvalidKey.make({}))\n : Effect.void;\n};\n"],"mappings":";;;AAKA,MAAa,SACX,KACA,WACA,cACG;CACH,MAAM,MAAM,cAAc,UAAU,cAAc,WAAW,QAAQ;CAErE,OAAQ,IAAI,QAAQ,KAAA,KAAa,IAAI,QAAQ,aAC1C,IAAI,QAAQ,KAAA,KAAa,IAAI,QAAQ,OACrC,IAAI,YAAY,KAAA,KAAa,CAAC,IAAI,QAAQ,SAAS,SAAS,IAC3D,OAAO,KAAK,WAAW,KAAK,CAAC,CAAC,CAAC,IAC/B,OAAO;AACb"}
|
package/package.json
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"name":"@yielded/jose","version":"0.1.0-beta.19","dependencies":{"@yielded/crypto":"0.1.0-beta.19"},"devDependencies":{"@effect/platform-node":"4.0.0","@effect/vitest":"4.0.0","@types/node":"26.1.2","effect":"4.0.0","jose":"6.2.12","typescript":"7.0.2","vite-plus":"0.3.3"},"peerDependencies":{"effect":"^4.0.0"},"exports":{".":{"types":"./dist/index.d.mts","default":"./dist/index.mjs"},"./Errors":{"types":"./dist/Errors.d.mts","default":"./dist/Errors.mjs"},"./Jwk":{"types":"./dist/Jwk.d.mts","default":"./dist/Jwk.mjs"},"./Jwks":{"types":"./dist/Jwks.d.mts","default":"./dist/Jwks.mjs"},"./Jws":{"types":"./dist/Jws.d.mts","default":"./dist/Jws.mjs"},"./Jwt":{"types":"./dist/Jwt.d.mts","default":"./dist/Jwt.mjs"},"./Jwe":{"types":"./dist/Jwe.d.mts","default":"./dist/Jwe.mjs"}},"description":"Schema-typed Effect JOSE with explicit keys, cryptography, and remote JWKS trust.","homepage":"https://yielded.dev/auth/","bugs":{"url":"https://github.com/yielded-dev/auth/issues"},"license":"MIT","repository":{"type":"git","url":"git+https://github.com/yielded-dev/auth.git","directory":"packages/jose"},"files":["dist","src","THIRD_PARTY_NOTICES.md"],"type":"module","sideEffects":[],"publishConfig":{"access":"public"},"scripts":{"build":"vp pack","check":"tsc --noEmit -p tsconfig.json","test":"vp test"}}
|
package/src/Errors.ts
ADDED
|
@@ -0,0 +1,55 @@
|
|
|
1
|
+
import type { OperationError as CryptoError } from "@yielded/crypto/Errors";
|
|
2
|
+
import { Schema } from "effect";
|
|
3
|
+
|
|
4
|
+
/** Fixed diagnostics never include a token, claims, key, URL, or native cause. */
|
|
5
|
+
export class InvalidToken extends Schema.TaggedError<InvalidToken>()("JoseInvalidToken", {
|
|
6
|
+
reason: Schema.Literals(["serialization", "header", "payload", "parameters"]),
|
|
7
|
+
}) {}
|
|
8
|
+
|
|
9
|
+
export class AlgorithmNotAllowed extends Schema.TaggedError<AlgorithmNotAllowed>()(
|
|
10
|
+
"JoseAlgorithmNotAllowed",
|
|
11
|
+
{},
|
|
12
|
+
) {}
|
|
13
|
+
|
|
14
|
+
export class InvalidKey extends Schema.TaggedError<InvalidKey>()("JoseInvalidKey", {}) {}
|
|
15
|
+
|
|
16
|
+
export class SignatureVerificationFailed extends Schema.TaggedError<SignatureVerificationFailed>()(
|
|
17
|
+
"JoseSignatureVerificationFailed",
|
|
18
|
+
{},
|
|
19
|
+
) {}
|
|
20
|
+
|
|
21
|
+
export class DecryptionFailed extends Schema.TaggedError<DecryptionFailed>()(
|
|
22
|
+
"JoseDecryptionFailed",
|
|
23
|
+
{},
|
|
24
|
+
) {}
|
|
25
|
+
|
|
26
|
+
export class KeyNotFound extends Schema.TaggedError<KeyNotFound>()("JoseKeyNotFound", {}) {}
|
|
27
|
+
export class AmbiguousKey extends Schema.TaggedError<AmbiguousKey>()("JoseAmbiguousKey", {}) {}
|
|
28
|
+
|
|
29
|
+
export class JwksUnavailable extends Schema.TaggedError<JwksUnavailable>()("JoseJwksUnavailable", {
|
|
30
|
+
reason: Schema.Literals(["configuration", "transport", "response", "cooldown", "busy", "closed"]),
|
|
31
|
+
}) {}
|
|
32
|
+
|
|
33
|
+
export class ClaimValidationFailed extends Schema.TaggedError<ClaimValidationFailed>()(
|
|
34
|
+
"JoseClaimValidationFailed",
|
|
35
|
+
{
|
|
36
|
+
claim: Schema.Literals(["registered", "iss", "sub", "aud", "exp", "nbf", "iat", "jti", "typ"]),
|
|
37
|
+
reason: Schema.Literals([
|
|
38
|
+
"invalid",
|
|
39
|
+
"missing",
|
|
40
|
+
"mismatch",
|
|
41
|
+
"expired",
|
|
42
|
+
"notYetValid",
|
|
43
|
+
"tooOld",
|
|
44
|
+
"future",
|
|
45
|
+
]),
|
|
46
|
+
},
|
|
47
|
+
) {}
|
|
48
|
+
|
|
49
|
+
export type KeyError = InvalidKey | KeyNotFound | AmbiguousKey | JwksUnavailable | CryptoError;
|
|
50
|
+
|
|
51
|
+
export type VerifyError =
|
|
52
|
+
| KeyError
|
|
53
|
+
| InvalidToken
|
|
54
|
+
| AlgorithmNotAllowed
|
|
55
|
+
| SignatureVerificationFailed;
|