@suveren/gateway 0.4.1 → 0.6.0
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/bin/suveren-gateway.js +86 -5
- package/content/integrations/calendar.json +116 -22
- package/content/integrations/deploy-github.json +164 -0
- package/content/integrations/gmail.json +13 -4
- package/content/integrations/index.json +2 -1
- package/dist/control-plane/index.mjs +84 -7
- package/dist/mcp-server/http.mjs +147 -21
- package/dist/ui/assets/index-DecSrutK.js +105 -0
- package/dist/ui/index.html +1 -1
- package/node_modules/@hap/core/dist/index.d.mts +21 -0
- package/node_modules/@hap/core/dist/index.d.ts +21 -0
- package/node_modules/@hap/core/dist/index.js +17 -0
- package/node_modules/@hap/core/dist/index.mjs +17 -0
- package/node_modules/@hap/core/package.json +1 -1
- package/node_modules/@hap/core/src/gatekeeper.ts +28 -0
- package/node_modules/@hap/core/src/types.ts +21 -0
- package/node_modules/fast-uri/index.js +37 -1
- package/node_modules/fast-uri/package.json +1 -1
- package/node_modules/fast-uri/test/security.test.js +136 -0
- package/node_modules/hono/dist/cjs/helper/proxy/index.js +4 -0
- package/node_modules/hono/dist/cjs/jsx/base.js +1 -9
- package/node_modules/hono/dist/cjs/jsx/hooks/index.js +14 -11
- package/node_modules/hono/dist/cjs/middleware/cors/index.js +1 -1
- package/node_modules/hono/dist/cjs/middleware/language/language.js +10 -6
- package/node_modules/hono/dist/cjs/utils/cookie.js +2 -1
- package/node_modules/hono/dist/helper/proxy/index.js +4 -0
- package/node_modules/hono/dist/jsx/base.js +1 -9
- package/node_modules/hono/dist/jsx/hooks/index.js +14 -11
- package/node_modules/hono/dist/middleware/cors/index.js +1 -1
- package/node_modules/hono/dist/middleware/language/language.js +10 -6
- package/node_modules/hono/dist/utils/cookie.js +2 -1
- package/node_modules/hono/package.json +2 -3
- package/node_modules/ip-address/README.md +142 -128
- package/node_modules/ip-address/dist/common.d.ts +6 -0
- package/node_modules/ip-address/dist/common.js +17 -1
- package/node_modules/ip-address/dist/common.js.map +1 -1
- package/node_modules/ip-address/dist/ipv4.js +3 -12
- package/node_modules/ip-address/dist/ipv4.js.map +1 -1
- package/node_modules/ip-address/dist/ipv6.d.ts +9 -2
- package/node_modules/ip-address/dist/ipv6.js +35 -16
- package/node_modules/ip-address/dist/ipv6.js.map +1 -1
- package/node_modules/ip-address/package.json +5 -3
- package/node_modules/jose/README.md +13 -13
- package/node_modules/jose/dist/types/index.d.ts +6 -5
- package/node_modules/jose/dist/types/jwe/compact/decrypt.d.ts +17 -11
- package/node_modules/jose/dist/types/jwe/compact/encrypt.d.ts +7 -19
- package/node_modules/jose/dist/types/jwe/flattened/decrypt.d.ts +17 -11
- package/node_modules/jose/dist/types/jwe/flattened/encrypt.d.ts +7 -19
- package/node_modules/jose/dist/types/jwe/general/decrypt.d.ts +18 -13
- package/node_modules/jose/dist/types/jwe/general/encrypt.d.ts +20 -21
- package/node_modules/jose/dist/types/jwk/embedded.d.ts +8 -12
- package/node_modules/jose/dist/types/jwk/thumbprint.d.ts +0 -15
- package/node_modules/jose/dist/types/jwks/local.d.ts +12 -21
- package/node_modules/jose/dist/types/jwks/remote.d.ts +34 -165
- package/node_modules/jose/dist/types/jws/compact/sign.d.ts +2 -13
- package/node_modules/jose/dist/types/jws/compact/verify.d.ts +17 -13
- package/node_modules/jose/dist/types/jws/flattened/sign.d.ts +2 -13
- package/node_modules/jose/dist/types/jws/flattened/verify.d.ts +17 -13
- package/node_modules/jose/dist/types/jws/general/sign.d.ts +16 -17
- package/node_modules/jose/dist/types/jws/general/verify.d.ts +18 -15
- package/node_modules/jose/dist/types/jwt/decrypt.d.ts +17 -11
- package/node_modules/jose/dist/types/jwt/encrypt.d.ts +10 -34
- package/node_modules/jose/dist/types/jwt/sign.d.ts +2 -13
- package/node_modules/jose/dist/types/jwt/unsecured.d.ts +4 -13
- package/node_modules/jose/dist/types/jwt/verify.d.ts +14 -13
- package/node_modules/jose/dist/types/key/export.d.ts +0 -14
- package/node_modules/jose/dist/types/key/generate_key_pair.d.ts +7 -14
- package/node_modules/jose/dist/types/key/generate_secret.d.ts +16 -14
- package/node_modules/jose/dist/types/key/import.d.ts +18 -28
- package/node_modules/jose/dist/types/types.d.ts +239 -222
- package/node_modules/jose/dist/types/util/base64url.d.ts +11 -4
- package/node_modules/jose/dist/types/util/decode_jwt.d.ts +1 -9
- package/node_modules/jose/dist/types/util/decode_protected_header.d.ts +0 -8
- package/node_modules/jose/dist/types/util/errors.d.ts +128 -121
- package/node_modules/jose/dist/webapi/jwe/compact/decrypt.js +4 -22
- package/node_modules/jose/dist/webapi/jwe/flattened/decrypt.js +5 -153
- package/node_modules/jose/dist/webapi/jwe/flattened/encrypt.js +16 -111
- package/node_modules/jose/dist/webapi/jwe/general/decrypt.js +15 -3
- package/node_modules/jose/dist/webapi/jwe/general/encrypt.js +65 -81
- package/node_modules/jose/dist/webapi/jwk/embedded.js +5 -3
- package/node_modules/jose/dist/webapi/jwks/local.js +31 -72
- package/node_modules/jose/dist/webapi/jwks/remote.js +19 -28
- package/node_modules/jose/dist/webapi/jws/compact/sign.js +5 -2
- package/node_modules/jose/dist/webapi/jws/compact/verify.js +4 -16
- package/node_modules/jose/dist/webapi/jws/flattened/sign.js +7 -65
- package/node_modules/jose/dist/webapi/jws/flattened/verify.js +3 -89
- package/node_modules/jose/dist/webapi/jws/general/sign.js +19 -15
- package/node_modules/jose/dist/webapi/jws/general/verify.js +29 -9
- package/node_modules/jose/dist/webapi/jwt/decrypt.js +5 -5
- package/node_modules/jose/dist/webapi/jwt/sign.js +2 -3
- package/node_modules/jose/dist/webapi/jwt/unsecured.js +4 -3
- package/node_modules/jose/dist/webapi/jwt/verify.js +6 -6
- package/node_modules/jose/dist/webapi/key/export.js +37 -4
- package/node_modules/jose/dist/webapi/key/generate_key_pair.js +33 -82
- package/node_modules/jose/dist/webapi/key/import.js +8 -8
- package/node_modules/jose/dist/webapi/lib/asn1.js +53 -122
- package/node_modules/jose/dist/webapi/lib/buffer_utils.js +1 -0
- package/node_modules/jose/dist/webapi/lib/content_encryption.js +34 -109
- package/node_modules/jose/dist/webapi/lib/crypto_key.js +18 -126
- package/node_modules/jose/dist/webapi/lib/helpers.js +23 -0
- package/node_modules/jose/dist/webapi/lib/invalid_key_input.js +0 -1
- package/node_modules/jose/dist/webapi/lib/jwe_algorithms.js +116 -0
- package/node_modules/jose/dist/webapi/lib/jwe_decrypt.js +186 -0
- package/node_modules/jose/dist/webapi/lib/jwe_encrypt.js +110 -0
- package/node_modules/jose/dist/webapi/lib/jwk_to_key.js +8 -98
- package/node_modules/jose/dist/webapi/lib/jws_algorithms.js +76 -0
- package/node_modules/jose/dist/webapi/lib/jws_sign.js +74 -0
- package/node_modules/jose/dist/webapi/lib/jws_verify.js +107 -0
- package/node_modules/jose/dist/webapi/lib/jwt_claims_set.js +68 -109
- package/node_modules/jose/dist/webapi/lib/key.js +132 -0
- package/node_modules/jose/dist/webapi/lib/key_algorithm.js +10 -0
- package/node_modules/jose/dist/webapi/lib/key_descriptor.js +7 -0
- package/node_modules/jose/dist/webapi/lib/key_management.js +162 -92
- package/node_modules/jose/dist/webapi/lib/options.js +48 -0
- package/node_modules/jose/dist/webapi/lib/signing.js +14 -56
- package/node_modules/jose/dist/webapi/lib/type_checks.js +13 -18
- package/node_modules/jose/dist/webapi/util/base64url.js +13 -4
- package/node_modules/jose/dist/webapi/util/decode_jwt.js +2 -2
- package/node_modules/jose/dist/webapi/util/decode_protected_header.js +5 -15
- package/node_modules/jose/dist/webapi/util/errors.js +1 -1
- package/node_modules/jose/package.json +2 -4
- package/package.json +2 -2
- package/profiles/deploy/0.6.profile.json +168 -0
- package/profiles/deploy/0.7.profile.json +168 -0
- package/profiles/email/0.4.profile.json +9 -2
- package/profiles/index.json +3 -1
- package/dist/ui/assets/index-Cog54HOo.js +0 -105
- package/node_modules/jose/dist/webapi/lib/aesgcmkw.js +0 -15
- package/node_modules/jose/dist/webapi/lib/aeskw.js +0 -25
- package/node_modules/jose/dist/webapi/lib/check_key_type.js +0 -122
- package/node_modules/jose/dist/webapi/lib/ecdhes.js +0 -52
- package/node_modules/jose/dist/webapi/lib/key_to_jwk.js +0 -34
- package/node_modules/jose/dist/webapi/lib/normalize_key.js +0 -166
- package/node_modules/jose/dist/webapi/lib/pbes2kw.js +0 -42
- package/node_modules/jose/dist/webapi/lib/rsaes.js +0 -24
- package/node_modules/jose/dist/webapi/lib/validate_algorithms.js +0 -10
- package/node_modules/jose/dist/webapi/lib/validate_crit.js +0 -33
|
@@ -1,107 +1,17 @@
|
|
|
1
1
|
import { JOSENotSupported } from '../util/errors.js';
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
let keyUsages;
|
|
6
|
-
switch (jwk.kty) {
|
|
7
|
-
case 'AKP': {
|
|
8
|
-
switch (jwk.alg) {
|
|
9
|
-
case 'ML-DSA-44':
|
|
10
|
-
case 'ML-DSA-65':
|
|
11
|
-
case 'ML-DSA-87':
|
|
12
|
-
algorithm = { name: jwk.alg };
|
|
13
|
-
keyUsages = jwk.priv ? ['sign'] : ['verify'];
|
|
14
|
-
break;
|
|
15
|
-
default:
|
|
16
|
-
throw new JOSENotSupported(unsupportedAlg);
|
|
17
|
-
}
|
|
18
|
-
break;
|
|
19
|
-
}
|
|
20
|
-
case 'RSA': {
|
|
21
|
-
switch (jwk.alg) {
|
|
22
|
-
case 'PS256':
|
|
23
|
-
case 'PS384':
|
|
24
|
-
case 'PS512':
|
|
25
|
-
algorithm = { name: 'RSA-PSS', hash: `SHA-${jwk.alg.slice(-3)}` };
|
|
26
|
-
keyUsages = jwk.d ? ['sign'] : ['verify'];
|
|
27
|
-
break;
|
|
28
|
-
case 'RS256':
|
|
29
|
-
case 'RS384':
|
|
30
|
-
case 'RS512':
|
|
31
|
-
algorithm = { name: 'RSASSA-PKCS1-v1_5', hash: `SHA-${jwk.alg.slice(-3)}` };
|
|
32
|
-
keyUsages = jwk.d ? ['sign'] : ['verify'];
|
|
33
|
-
break;
|
|
34
|
-
case 'RSA-OAEP':
|
|
35
|
-
case 'RSA-OAEP-256':
|
|
36
|
-
case 'RSA-OAEP-384':
|
|
37
|
-
case 'RSA-OAEP-512':
|
|
38
|
-
algorithm = {
|
|
39
|
-
name: 'RSA-OAEP',
|
|
40
|
-
hash: `SHA-${parseInt(jwk.alg.slice(-3), 10) || 1}`,
|
|
41
|
-
};
|
|
42
|
-
keyUsages = jwk.d ? ['decrypt', 'unwrapKey'] : ['encrypt', 'wrapKey'];
|
|
43
|
-
break;
|
|
44
|
-
default:
|
|
45
|
-
throw new JOSENotSupported(unsupportedAlg);
|
|
46
|
-
}
|
|
47
|
-
break;
|
|
48
|
-
}
|
|
49
|
-
case 'EC': {
|
|
50
|
-
switch (jwk.alg) {
|
|
51
|
-
case 'ES256':
|
|
52
|
-
case 'ES384':
|
|
53
|
-
case 'ES512':
|
|
54
|
-
algorithm = {
|
|
55
|
-
name: 'ECDSA',
|
|
56
|
-
namedCurve: { ES256: 'P-256', ES384: 'P-384', ES512: 'P-521' }[jwk.alg],
|
|
57
|
-
};
|
|
58
|
-
keyUsages = jwk.d ? ['sign'] : ['verify'];
|
|
59
|
-
break;
|
|
60
|
-
case 'ECDH-ES':
|
|
61
|
-
case 'ECDH-ES+A128KW':
|
|
62
|
-
case 'ECDH-ES+A192KW':
|
|
63
|
-
case 'ECDH-ES+A256KW':
|
|
64
|
-
algorithm = { name: 'ECDH', namedCurve: jwk.crv };
|
|
65
|
-
keyUsages = jwk.d ? ['deriveBits'] : [];
|
|
66
|
-
break;
|
|
67
|
-
default:
|
|
68
|
-
throw new JOSENotSupported(unsupportedAlg);
|
|
69
|
-
}
|
|
70
|
-
break;
|
|
71
|
-
}
|
|
72
|
-
case 'OKP': {
|
|
73
|
-
switch (jwk.alg) {
|
|
74
|
-
case 'Ed25519':
|
|
75
|
-
case 'EdDSA':
|
|
76
|
-
algorithm = { name: 'Ed25519' };
|
|
77
|
-
keyUsages = jwk.d ? ['sign'] : ['verify'];
|
|
78
|
-
break;
|
|
79
|
-
case 'ECDH-ES':
|
|
80
|
-
case 'ECDH-ES+A128KW':
|
|
81
|
-
case 'ECDH-ES+A192KW':
|
|
82
|
-
case 'ECDH-ES+A256KW':
|
|
83
|
-
algorithm = { name: jwk.crv };
|
|
84
|
-
keyUsages = jwk.d ? ['deriveBits'] : [];
|
|
85
|
-
break;
|
|
86
|
-
default:
|
|
87
|
-
throw new JOSENotSupported(unsupportedAlg);
|
|
88
|
-
}
|
|
89
|
-
break;
|
|
90
|
-
}
|
|
91
|
-
default:
|
|
92
|
-
throw new JOSENotSupported('Invalid or unsupported JWK "kty" (Key Type) Parameter value');
|
|
2
|
+
export async function jwkToKey(entry, jwk) {
|
|
3
|
+
if (jwk.kty === 'RSA' && 'oth' in jwk && jwk.oth !== undefined) {
|
|
4
|
+
throw new JOSENotSupported('RSA JWK "oth" (Other Primes Info) Parameter value is not supported');
|
|
93
5
|
}
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
export async function jwkToKey(jwk) {
|
|
97
|
-
if (!jwk.alg) {
|
|
98
|
-
throw new TypeError('"alg" argument is required when "jwk.alg" is not present');
|
|
6
|
+
if (!entry.kty.includes(jwk.kty)) {
|
|
7
|
+
throw new JOSENotSupported('Invalid or unsupported JWK "alg" (Algorithm) Parameter value');
|
|
99
8
|
}
|
|
100
|
-
const {
|
|
9
|
+
const algorithm = entry.resolve?.({ kty: jwk.kty, crv: jwk.crv }) ?? entry.subtle;
|
|
10
|
+
const isPrivate = !!(jwk.d || jwk.priv);
|
|
101
11
|
const keyData = { ...jwk };
|
|
102
12
|
if (keyData.kty !== 'AKP') {
|
|
103
13
|
delete keyData.alg;
|
|
104
14
|
}
|
|
105
15
|
delete keyData.use;
|
|
106
|
-
return crypto.subtle.importKey('jwk', keyData, algorithm, jwk.ext ??
|
|
16
|
+
return crypto.subtle.importKey('jwk', keyData, algorithm, jwk.ext ?? !isPrivate, jwk.key_ops ?? entry.usages[isPrivate ? 1 : 0]);
|
|
107
17
|
}
|
|
@@ -0,0 +1,76 @@
|
|
|
1
|
+
import { JOSENotSupported } from '../util/errors.js';
|
|
2
|
+
import { table } from './key_descriptor.js';
|
|
3
|
+
const sig = [['verify'], ['sign']];
|
|
4
|
+
function hmac(bits) {
|
|
5
|
+
const subtle = { name: 'HMAC', hash: `SHA-${bits}` };
|
|
6
|
+
return { kty: ['oct'], secret: true, subtle, signing: subtle, usages: sig };
|
|
7
|
+
}
|
|
8
|
+
function rsa(bits, saltLength) {
|
|
9
|
+
const name = saltLength ? 'RSA-PSS' : 'RSASSA-PKCS1-v1_5';
|
|
10
|
+
const subtle = { name, hash: `SHA-${bits}` };
|
|
11
|
+
return {
|
|
12
|
+
kty: ['RSA'],
|
|
13
|
+
subtle,
|
|
14
|
+
signing: saltLength ? { ...subtle, saltLength } : subtle,
|
|
15
|
+
usages: sig,
|
|
16
|
+
minRsaBits: 2048,
|
|
17
|
+
};
|
|
18
|
+
}
|
|
19
|
+
function ecdsa(crv, bits) {
|
|
20
|
+
return {
|
|
21
|
+
kty: ['EC'],
|
|
22
|
+
crv,
|
|
23
|
+
subtle: { name: 'ECDSA', namedCurve: crv },
|
|
24
|
+
signing: { name: 'ECDSA', hash: `SHA-${bits}` },
|
|
25
|
+
usages: sig,
|
|
26
|
+
};
|
|
27
|
+
}
|
|
28
|
+
function eddsa() {
|
|
29
|
+
const subtle = { name: 'Ed25519' };
|
|
30
|
+
return {
|
|
31
|
+
kty: ['OKP'],
|
|
32
|
+
crv: 'Ed25519',
|
|
33
|
+
subtle,
|
|
34
|
+
signing: subtle,
|
|
35
|
+
usages: sig,
|
|
36
|
+
};
|
|
37
|
+
}
|
|
38
|
+
function mldsa(bits) {
|
|
39
|
+
const name = `ML-DSA-${bits}`;
|
|
40
|
+
const subtle = { name };
|
|
41
|
+
return {
|
|
42
|
+
kty: ['AKP'],
|
|
43
|
+
subtle,
|
|
44
|
+
signing: subtle,
|
|
45
|
+
usages: sig,
|
|
46
|
+
};
|
|
47
|
+
}
|
|
48
|
+
const JWS = table({
|
|
49
|
+
HS256: hmac(256),
|
|
50
|
+
HS384: hmac(384),
|
|
51
|
+
HS512: hmac(512),
|
|
52
|
+
RS256: rsa(256),
|
|
53
|
+
RS384: rsa(384),
|
|
54
|
+
RS512: rsa(512),
|
|
55
|
+
PS256: rsa(256, 32),
|
|
56
|
+
PS384: rsa(384, 48),
|
|
57
|
+
PS512: rsa(512, 64),
|
|
58
|
+
ES256: ecdsa('P-256', 256),
|
|
59
|
+
ES384: ecdsa('P-384', 384),
|
|
60
|
+
ES512: ecdsa('P-521', 512),
|
|
61
|
+
EdDSA: eddsa(),
|
|
62
|
+
Ed25519: eddsa(),
|
|
63
|
+
'ML-DSA-44': mldsa(44),
|
|
64
|
+
'ML-DSA-65': mldsa(65),
|
|
65
|
+
'ML-DSA-87': mldsa(87),
|
|
66
|
+
});
|
|
67
|
+
export function jwsAlgorithm(alg) {
|
|
68
|
+
const entry = JWS[alg];
|
|
69
|
+
if (!entry) {
|
|
70
|
+
throw new JOSENotSupported(`alg ${alg} is not supported either by JOSE or your javascript runtime`);
|
|
71
|
+
}
|
|
72
|
+
return entry;
|
|
73
|
+
}
|
|
74
|
+
export function maybeJWSAlgorithm(alg) {
|
|
75
|
+
return JWS[alg];
|
|
76
|
+
}
|
|
@@ -0,0 +1,74 @@
|
|
|
1
|
+
import { encode as b64u } from '../util/base64url.js';
|
|
2
|
+
import { sign } from './signing.js';
|
|
3
|
+
import { jwsAlgorithm } from './jws_algorithms.js';
|
|
4
|
+
import { isDisjoint } from './type_checks.js';
|
|
5
|
+
import { JWSInvalid } from '../util/errors.js';
|
|
6
|
+
import { concat, encode } from './buffer_utils.js';
|
|
7
|
+
import { validateCrit, validateCritDuplicates, JWS_RECOGNIZED } from './options.js';
|
|
8
|
+
import { prepareKey } from './key.js';
|
|
9
|
+
export function unencodedPayload(protectedHeader) {
|
|
10
|
+
return (protectedHeader?.b64 === false &&
|
|
11
|
+
Array.isArray(protectedHeader.crit) &&
|
|
12
|
+
protectedHeader.crit.includes('b64'));
|
|
13
|
+
}
|
|
14
|
+
export async function createSignature(input, key) {
|
|
15
|
+
const { protectedHeader, unprotectedHeader } = input;
|
|
16
|
+
if (!protectedHeader && !unprotectedHeader) {
|
|
17
|
+
throw new JWSInvalid('either setProtectedHeader or setUnprotectedHeader must be called before #sign()');
|
|
18
|
+
}
|
|
19
|
+
if (!isDisjoint(protectedHeader, unprotectedHeader)) {
|
|
20
|
+
throw new JWSInvalid('JWS Protected and JWS Unprotected Header Parameter names must be disjoint');
|
|
21
|
+
}
|
|
22
|
+
const joseHeader = { ...protectedHeader, ...unprotectedHeader };
|
|
23
|
+
validateCritDuplicates(JWSInvalid, protectedHeader);
|
|
24
|
+
const extensions = validateCrit(JWSInvalid, JWS_RECOGNIZED, input.crit, protectedHeader, joseHeader);
|
|
25
|
+
let b64 = true;
|
|
26
|
+
if (extensions.includes('b64')) {
|
|
27
|
+
b64 = protectedHeader.b64;
|
|
28
|
+
if (typeof b64 !== 'boolean') {
|
|
29
|
+
throw new JWSInvalid('The "b64" (base64url-encode payload) Header Parameter must be a boolean');
|
|
30
|
+
}
|
|
31
|
+
}
|
|
32
|
+
const { alg } = joseHeader;
|
|
33
|
+
if (typeof alg !== 'string' || !alg) {
|
|
34
|
+
throw new JWSInvalid('JWS "alg" (Algorithm) Header Parameter missing or invalid');
|
|
35
|
+
}
|
|
36
|
+
const entry = jwsAlgorithm(alg);
|
|
37
|
+
let payloadS;
|
|
38
|
+
let payloadB;
|
|
39
|
+
if (b64) {
|
|
40
|
+
const encoded = (input.encoded ??= []);
|
|
41
|
+
encoded[0] ??= b64u(input.payload);
|
|
42
|
+
encoded[1] ??= encode(encoded[0]);
|
|
43
|
+
payloadS = encoded[0];
|
|
44
|
+
payloadB = encoded[1];
|
|
45
|
+
}
|
|
46
|
+
else {
|
|
47
|
+
payloadB = input.payload;
|
|
48
|
+
payloadS = '';
|
|
49
|
+
}
|
|
50
|
+
let protectedHeaderString;
|
|
51
|
+
let protectedHeaderBytes;
|
|
52
|
+
if (protectedHeader) {
|
|
53
|
+
protectedHeaderString = b64u(JSON.stringify(protectedHeader));
|
|
54
|
+
protectedHeaderBytes = encode(protectedHeaderString);
|
|
55
|
+
}
|
|
56
|
+
else {
|
|
57
|
+
protectedHeaderString = '';
|
|
58
|
+
protectedHeaderBytes = new Uint8Array();
|
|
59
|
+
}
|
|
60
|
+
const data = concat(protectedHeaderBytes, encode('.'), payloadB);
|
|
61
|
+
const k = await prepareKey(entry, key, 'sign');
|
|
62
|
+
const signature = await sign(entry, k, data);
|
|
63
|
+
const jws = {
|
|
64
|
+
signature: b64u(signature),
|
|
65
|
+
payload: payloadS,
|
|
66
|
+
};
|
|
67
|
+
if (protectedHeader) {
|
|
68
|
+
jws.protected = protectedHeaderString;
|
|
69
|
+
}
|
|
70
|
+
if (unprotectedHeader) {
|
|
71
|
+
jws.header = unprotectedHeader;
|
|
72
|
+
}
|
|
73
|
+
return jws;
|
|
74
|
+
}
|
|
@@ -0,0 +1,107 @@
|
|
|
1
|
+
import { verify } from './signing.js';
|
|
2
|
+
import { jwsAlgorithm } from './jws_algorithms.js';
|
|
3
|
+
import { JOSEAlgNotAllowed, JWSInvalid, JWSSignatureVerificationFailed } from '../util/errors.js';
|
|
4
|
+
import { concat, decoder, encoder, encode } from './buffer_utils.js';
|
|
5
|
+
import { decodeBase64url, encodeBase64url, parseJoseHeader } from './helpers.js';
|
|
6
|
+
import { isDisjoint } from './type_checks.js';
|
|
7
|
+
import { validateCrit, validateAlgorithms, JWS_RECOGNIZED } from './options.js';
|
|
8
|
+
import { prepareKey } from './key.js';
|
|
9
|
+
export function verifyResult(jws, verified) {
|
|
10
|
+
const [payload, parsedProt, , key, resolvedKey] = verified;
|
|
11
|
+
const result = { payload };
|
|
12
|
+
if (jws.protected !== undefined) {
|
|
13
|
+
result.protectedHeader = parsedProt;
|
|
14
|
+
}
|
|
15
|
+
if (jws.header !== undefined) {
|
|
16
|
+
result.unprotectedHeader = jws.header;
|
|
17
|
+
}
|
|
18
|
+
if (resolvedKey) {
|
|
19
|
+
return { ...result, key };
|
|
20
|
+
}
|
|
21
|
+
return result;
|
|
22
|
+
}
|
|
23
|
+
export function prepareVerify(options) {
|
|
24
|
+
return [options && validateAlgorithms('algorithms', options.algorithms), options?.crit];
|
|
25
|
+
}
|
|
26
|
+
export async function verifySignature(jws, shared, key) {
|
|
27
|
+
const { protected: encodedProtected, header, payload: inputPayload } = jws;
|
|
28
|
+
let parsedProt = {};
|
|
29
|
+
if (encodedProtected) {
|
|
30
|
+
parsedProt = parseJoseHeader(encodedProtected, JWSInvalid, 'JWS Protected Header is invalid');
|
|
31
|
+
}
|
|
32
|
+
let joseHeader;
|
|
33
|
+
if (header !== undefined) {
|
|
34
|
+
if (!isDisjoint(parsedProt, header)) {
|
|
35
|
+
throw new JWSInvalid('JWS Protected and JWS Unprotected Header Parameter names must be disjoint');
|
|
36
|
+
}
|
|
37
|
+
joseHeader = { ...parsedProt, ...header };
|
|
38
|
+
}
|
|
39
|
+
else {
|
|
40
|
+
joseHeader = parsedProt;
|
|
41
|
+
}
|
|
42
|
+
const extensions = validateCrit(JWSInvalid, JWS_RECOGNIZED, shared[1], parsedProt, joseHeader);
|
|
43
|
+
let b64 = true;
|
|
44
|
+
if (extensions.includes('b64')) {
|
|
45
|
+
b64 = parsedProt.b64;
|
|
46
|
+
if (typeof b64 !== 'boolean') {
|
|
47
|
+
throw new JWSInvalid('The "b64" (base64url-encode payload) Header Parameter must be a boolean');
|
|
48
|
+
}
|
|
49
|
+
}
|
|
50
|
+
const { alg } = joseHeader;
|
|
51
|
+
if (typeof alg !== 'string' || !alg) {
|
|
52
|
+
throw new JWSInvalid('JWS "alg" (Algorithm) Header Parameter missing or invalid');
|
|
53
|
+
}
|
|
54
|
+
if (shared[0] && !shared[0].has(alg)) {
|
|
55
|
+
throw new JOSEAlgNotAllowed('"alg" (Algorithm) Header Parameter value not allowed');
|
|
56
|
+
}
|
|
57
|
+
if (b64) {
|
|
58
|
+
if (typeof inputPayload !== 'string') {
|
|
59
|
+
throw new JWSInvalid('JWS Payload must be a string');
|
|
60
|
+
}
|
|
61
|
+
}
|
|
62
|
+
else if (typeof inputPayload !== 'string' && !(inputPayload instanceof Uint8Array)) {
|
|
63
|
+
throw new JWSInvalid('JWS Payload must be a string or an Uint8Array instance');
|
|
64
|
+
}
|
|
65
|
+
let resolvedKey = false;
|
|
66
|
+
if (typeof key === 'function') {
|
|
67
|
+
key = await key(parsedProt, jws);
|
|
68
|
+
resolvedKey = true;
|
|
69
|
+
}
|
|
70
|
+
const entry = jwsAlgorithm(alg);
|
|
71
|
+
const data = concat(encodedProtected !== undefined ? encode(encodedProtected) : new Uint8Array(), encode('.'), typeof inputPayload === 'string'
|
|
72
|
+
? b64
|
|
73
|
+
?
|
|
74
|
+
(shared[2] ??= encodeBase64url(inputPayload, 'payload', JWSInvalid))
|
|
75
|
+
: encoder.encode(inputPayload)
|
|
76
|
+
: inputPayload);
|
|
77
|
+
const signature = decodeBase64url(jws.signature, 'signature', JWSInvalid);
|
|
78
|
+
const k = await prepareKey(entry, key, 'verify');
|
|
79
|
+
const verified = await verify(entry, k, signature, data);
|
|
80
|
+
if (!verified) {
|
|
81
|
+
throw new JWSSignatureVerificationFailed();
|
|
82
|
+
}
|
|
83
|
+
let payload;
|
|
84
|
+
if (b64) {
|
|
85
|
+
payload = decodeBase64url(inputPayload, 'payload', JWSInvalid);
|
|
86
|
+
}
|
|
87
|
+
else if (typeof inputPayload === 'string') {
|
|
88
|
+
payload = encoder.encode(inputPayload);
|
|
89
|
+
}
|
|
90
|
+
else {
|
|
91
|
+
payload = inputPayload;
|
|
92
|
+
}
|
|
93
|
+
return [payload, parsedProt, b64, k, resolvedKey];
|
|
94
|
+
}
|
|
95
|
+
export async function verifyCompact(jws, shared, key) {
|
|
96
|
+
if (jws instanceof Uint8Array) {
|
|
97
|
+
jws = decoder.decode(jws);
|
|
98
|
+
}
|
|
99
|
+
if (typeof jws !== 'string') {
|
|
100
|
+
throw new JWSInvalid('Compact JWS must be a string or Uint8Array');
|
|
101
|
+
}
|
|
102
|
+
const { 0: protectedHeader, 1: payload, 2: signature, length } = jws.split('.');
|
|
103
|
+
if (length !== 3) {
|
|
104
|
+
throw new JWSInvalid('Invalid Compact JWS');
|
|
105
|
+
}
|
|
106
|
+
return verifySignature({ payload, protected: protectedHeader, signature }, shared, key);
|
|
107
|
+
}
|
|
@@ -1,57 +1,24 @@
|
|
|
1
1
|
import { JWTClaimValidationFailed, JWTExpired, JWTInvalid } from '../util/errors.js';
|
|
2
|
-
import { encoder,
|
|
2
|
+
import { encoder, strictDecoder } from './buffer_utils.js';
|
|
3
3
|
import { isObject } from './type_checks.js';
|
|
4
4
|
const epoch = (date) => Math.floor(date.getTime() / 1000);
|
|
5
|
-
const
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
5
|
+
const multipliers = {
|
|
6
|
+
s: 1,
|
|
7
|
+
m: 60,
|
|
8
|
+
h: 3600,
|
|
9
|
+
d: 86400,
|
|
10
|
+
w: 604800,
|
|
11
|
+
y: 31557600,
|
|
12
|
+
};
|
|
10
13
|
const REGEX = /^(\+|\-)? ?(\d+|\d+\.\d+) ?(seconds?|secs?|s|minutes?|mins?|m|hours?|hrs?|h|days?|d|weeks?|w|years?|yrs?|y)(?: (ago|from now))?$/i;
|
|
14
|
+
const checkFailed = 'check_failed';
|
|
11
15
|
export function secs(str) {
|
|
12
16
|
const matched = REGEX.exec(str);
|
|
13
17
|
if (!matched || (matched[4] && matched[1])) {
|
|
14
18
|
throw new TypeError('Invalid time period format');
|
|
15
19
|
}
|
|
16
20
|
const value = parseFloat(matched[2]);
|
|
17
|
-
const
|
|
18
|
-
let numericDate;
|
|
19
|
-
switch (unit) {
|
|
20
|
-
case 'sec':
|
|
21
|
-
case 'secs':
|
|
22
|
-
case 'second':
|
|
23
|
-
case 'seconds':
|
|
24
|
-
case 's':
|
|
25
|
-
numericDate = Math.round(value);
|
|
26
|
-
break;
|
|
27
|
-
case 'minute':
|
|
28
|
-
case 'minutes':
|
|
29
|
-
case 'min':
|
|
30
|
-
case 'mins':
|
|
31
|
-
case 'm':
|
|
32
|
-
numericDate = Math.round(value * minute);
|
|
33
|
-
break;
|
|
34
|
-
case 'hour':
|
|
35
|
-
case 'hours':
|
|
36
|
-
case 'hr':
|
|
37
|
-
case 'hrs':
|
|
38
|
-
case 'h':
|
|
39
|
-
numericDate = Math.round(value * hour);
|
|
40
|
-
break;
|
|
41
|
-
case 'day':
|
|
42
|
-
case 'days':
|
|
43
|
-
case 'd':
|
|
44
|
-
numericDate = Math.round(value * day);
|
|
45
|
-
break;
|
|
46
|
-
case 'week':
|
|
47
|
-
case 'weeks':
|
|
48
|
-
case 'w':
|
|
49
|
-
numericDate = Math.round(value * week);
|
|
50
|
-
break;
|
|
51
|
-
default:
|
|
52
|
-
numericDate = Math.round(value * year);
|
|
53
|
-
break;
|
|
54
|
-
}
|
|
21
|
+
const numericDate = Math.round(value * multipliers[matched[3][0].toLowerCase()]);
|
|
55
22
|
if (matched[1] === '-' || matched[4] === 'ago') {
|
|
56
23
|
return -numericDate;
|
|
57
24
|
}
|
|
@@ -63,6 +30,13 @@ function validateInput(label, input) {
|
|
|
63
30
|
}
|
|
64
31
|
return input;
|
|
65
32
|
}
|
|
33
|
+
function numericDate(value, label) {
|
|
34
|
+
if (typeof value === 'number')
|
|
35
|
+
return validateInput(label, value);
|
|
36
|
+
if (value instanceof Date)
|
|
37
|
+
return validateInput(label, epoch(value));
|
|
38
|
+
return epoch(new Date()) + secs(value);
|
|
39
|
+
}
|
|
66
40
|
const normalizeTyp = (value) => {
|
|
67
41
|
if (value.includes('/')) {
|
|
68
42
|
return value.toLowerCase();
|
|
@@ -74,14 +48,26 @@ const checkAudiencePresence = (audPayload, audOption) => {
|
|
|
74
48
|
return audOption.includes(audPayload);
|
|
75
49
|
}
|
|
76
50
|
if (Array.isArray(audPayload)) {
|
|
77
|
-
return audOption.some(
|
|
51
|
+
return audOption.some((aud) => audPayload.includes(aud));
|
|
78
52
|
}
|
|
79
53
|
return false;
|
|
80
54
|
};
|
|
55
|
+
function validateNumericDate(payload, claim, required = false) {
|
|
56
|
+
const value = payload[claim];
|
|
57
|
+
if (value === undefined && !required)
|
|
58
|
+
return undefined;
|
|
59
|
+
if (typeof value !== 'number') {
|
|
60
|
+
throw new JWTClaimValidationFailed(`"${claim}" claim must be a number`, payload, claim, 'invalid');
|
|
61
|
+
}
|
|
62
|
+
return value;
|
|
63
|
+
}
|
|
64
|
+
function unexpectedClaim(payload, claim) {
|
|
65
|
+
throw new JWTClaimValidationFailed(`unexpected "${claim}" claim value`, payload, claim, checkFailed);
|
|
66
|
+
}
|
|
81
67
|
export function validateClaimsSet(protectedHeader, encodedPayload, options = {}) {
|
|
82
68
|
let payload;
|
|
83
69
|
try {
|
|
84
|
-
payload = JSON.parse(
|
|
70
|
+
payload = JSON.parse(strictDecoder.decode(encodedPayload));
|
|
85
71
|
}
|
|
86
72
|
catch {
|
|
87
73
|
}
|
|
@@ -92,7 +78,7 @@ export function validateClaimsSet(protectedHeader, encodedPayload, options = {})
|
|
|
92
78
|
if (typ &&
|
|
93
79
|
(typeof protectedHeader.typ !== 'string' ||
|
|
94
80
|
normalizeTyp(protectedHeader.typ) !== normalizeTyp(typ))) {
|
|
95
|
-
throw new JWTClaimValidationFailed('unexpected "typ" JWT header value', payload, 'typ',
|
|
81
|
+
throw new JWTClaimValidationFailed('unexpected "typ" JWT header value', payload, 'typ', checkFailed);
|
|
96
82
|
}
|
|
97
83
|
const { requiredClaims = [], issuer, subject, audience, maxTokenAge } = options;
|
|
98
84
|
const presenceCheck = [...requiredClaims];
|
|
@@ -105,64 +91,56 @@ export function validateClaimsSet(protectedHeader, encodedPayload, options = {})
|
|
|
105
91
|
if (issuer !== undefined)
|
|
106
92
|
presenceCheck.push('iss');
|
|
107
93
|
for (const claim of new Set(presenceCheck.reverse())) {
|
|
108
|
-
if (!(claim
|
|
94
|
+
if (!Object.hasOwn(payload, claim)) {
|
|
109
95
|
throw new JWTClaimValidationFailed(`missing required "${claim}" claim`, payload, claim, 'missing');
|
|
110
96
|
}
|
|
111
97
|
}
|
|
112
|
-
if (issuer &&
|
|
98
|
+
if (issuer !== undefined &&
|
|
113
99
|
!(Array.isArray(issuer) ? issuer : [issuer]).includes(payload.iss)) {
|
|
114
|
-
|
|
100
|
+
unexpectedClaim(payload, 'iss');
|
|
115
101
|
}
|
|
116
|
-
if (subject && payload.sub !== subject) {
|
|
117
|
-
|
|
102
|
+
if (subject !== undefined && payload.sub !== subject) {
|
|
103
|
+
unexpectedClaim(payload, 'sub');
|
|
118
104
|
}
|
|
119
|
-
if (audience &&
|
|
105
|
+
if (audience !== undefined &&
|
|
120
106
|
!checkAudiencePresence(payload.aud, typeof audience === 'string' ? [audience] : audience)) {
|
|
121
|
-
|
|
122
|
-
}
|
|
123
|
-
let tolerance;
|
|
124
|
-
switch (typeof options.clockTolerance) {
|
|
125
|
-
case 'string':
|
|
126
|
-
tolerance = secs(options.clockTolerance);
|
|
127
|
-
break;
|
|
128
|
-
case 'number':
|
|
129
|
-
tolerance = options.clockTolerance;
|
|
130
|
-
break;
|
|
131
|
-
case 'undefined':
|
|
132
|
-
tolerance = 0;
|
|
133
|
-
break;
|
|
134
|
-
default:
|
|
135
|
-
throw new TypeError('Invalid clockTolerance option type');
|
|
107
|
+
unexpectedClaim(payload, 'aud');
|
|
136
108
|
}
|
|
137
|
-
const {
|
|
138
|
-
|
|
139
|
-
if (
|
|
140
|
-
|
|
109
|
+
const { clockTolerance } = options;
|
|
110
|
+
let tolerance = 0;
|
|
111
|
+
if (typeof clockTolerance === 'string') {
|
|
112
|
+
tolerance = secs(clockTolerance);
|
|
141
113
|
}
|
|
142
|
-
if (
|
|
143
|
-
if (typeof
|
|
144
|
-
throw new
|
|
145
|
-
}
|
|
146
|
-
if (payload.nbf > now + tolerance) {
|
|
147
|
-
throw new JWTClaimValidationFailed('"nbf" claim timestamp check failed', payload, 'nbf', 'check_failed');
|
|
114
|
+
else if (clockTolerance !== undefined) {
|
|
115
|
+
if (typeof clockTolerance !== 'number') {
|
|
116
|
+
throw new TypeError('Invalid clockTolerance option type');
|
|
148
117
|
}
|
|
118
|
+
tolerance = clockTolerance;
|
|
149
119
|
}
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
|
|
120
|
+
validateInput('clockTolerance option', tolerance);
|
|
121
|
+
const { currentDate } = options;
|
|
122
|
+
const now = validateInput('currentDate option', epoch(currentDate || new Date()));
|
|
123
|
+
const iat = validateNumericDate(payload, 'iat', maxTokenAge !== undefined);
|
|
124
|
+
const nbf = validateNumericDate(payload, 'nbf');
|
|
125
|
+
if (nbf !== undefined) {
|
|
126
|
+
if (nbf > now + tolerance) {
|
|
127
|
+
throw new JWTClaimValidationFailed('"nbf" claim timestamp check failed', payload, 'nbf', checkFailed);
|
|
153
128
|
}
|
|
154
|
-
|
|
155
|
-
|
|
129
|
+
}
|
|
130
|
+
const exp = validateNumericDate(payload, 'exp');
|
|
131
|
+
if (exp !== undefined) {
|
|
132
|
+
if (exp <= now - tolerance) {
|
|
133
|
+
throw new JWTExpired('"exp" claim timestamp check failed', payload, 'exp', checkFailed);
|
|
156
134
|
}
|
|
157
135
|
}
|
|
158
|
-
if (maxTokenAge) {
|
|
159
|
-
const age = now -
|
|
136
|
+
if (maxTokenAge !== undefined) {
|
|
137
|
+
const age = now - iat;
|
|
160
138
|
const max = typeof maxTokenAge === 'number' ? maxTokenAge : secs(maxTokenAge);
|
|
161
139
|
if (age - tolerance > max) {
|
|
162
|
-
throw new JWTExpired('"iat" claim timestamp check failed (too far in the past)', payload, 'iat',
|
|
140
|
+
throw new JWTExpired('"iat" claim timestamp check failed (too far in the past)', payload, 'iat', checkFailed);
|
|
163
141
|
}
|
|
164
142
|
if (age < 0 - tolerance) {
|
|
165
|
-
throw new JWTClaimValidationFailed('"iat" claim timestamp check failed (it should be in the past)', payload, 'iat',
|
|
143
|
+
throw new JWTClaimValidationFailed('"iat" claim timestamp check failed (it should be in the past)', payload, 'iat', checkFailed);
|
|
166
144
|
}
|
|
167
145
|
}
|
|
168
146
|
return payload;
|
|
@@ -200,39 +178,20 @@ export class JWTClaimsBuilder {
|
|
|
200
178
|
this.#payload.jti = value;
|
|
201
179
|
}
|
|
202
180
|
set nbf(value) {
|
|
203
|
-
|
|
204
|
-
this.#payload.nbf = validateInput('setNotBefore', value);
|
|
205
|
-
}
|
|
206
|
-
else if (value instanceof Date) {
|
|
207
|
-
this.#payload.nbf = validateInput('setNotBefore', epoch(value));
|
|
208
|
-
}
|
|
209
|
-
else {
|
|
210
|
-
this.#payload.nbf = epoch(new Date()) + secs(value);
|
|
211
|
-
}
|
|
181
|
+
this.#payload.nbf = numericDate(value, 'setNotBefore');
|
|
212
182
|
}
|
|
213
183
|
set exp(value) {
|
|
214
|
-
|
|
215
|
-
this.#payload.exp = validateInput('setExpirationTime', value);
|
|
216
|
-
}
|
|
217
|
-
else if (value instanceof Date) {
|
|
218
|
-
this.#payload.exp = validateInput('setExpirationTime', epoch(value));
|
|
219
|
-
}
|
|
220
|
-
else {
|
|
221
|
-
this.#payload.exp = epoch(new Date()) + secs(value);
|
|
222
|
-
}
|
|
184
|
+
this.#payload.exp = numericDate(value, 'setExpirationTime');
|
|
223
185
|
}
|
|
224
186
|
set iat(value) {
|
|
225
187
|
if (value === undefined) {
|
|
226
188
|
this.#payload.iat = epoch(new Date());
|
|
227
189
|
}
|
|
228
|
-
else if (value instanceof Date) {
|
|
229
|
-
this.#payload.iat = validateInput('setIssuedAt', epoch(value));
|
|
230
|
-
}
|
|
231
190
|
else if (typeof value === 'string') {
|
|
232
191
|
this.#payload.iat = validateInput('setIssuedAt', epoch(new Date()) + secs(value));
|
|
233
192
|
}
|
|
234
193
|
else {
|
|
235
|
-
this.#payload.iat =
|
|
194
|
+
this.#payload.iat = numericDate(value, 'setIssuedAt');
|
|
236
195
|
}
|
|
237
196
|
}
|
|
238
197
|
}
|