@pulse-compute/jwt 1.0.0-beta.5 → 1.0.0-beta.6
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/README.md +78 -9
- package/as/index.as.ts +7 -2
- package/as/sign.as.ts +189 -0
- package/dist/crypto-verifier.d.ts.map +1 -1
- package/dist/crypto-verifier.js +8 -5
- package/dist/crypto-verifier.js.map +1 -1
- package/dist/index.d.ts +4 -0
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +9 -0
- package/dist/index.js.map +1 -1
- package/dist/internal/package-runtime.d.ts +5 -0
- package/dist/internal/package-runtime.d.ts.map +1 -1
- package/dist/internal/package-runtime.js +1 -0
- package/dist/internal/package-runtime.js.map +1 -1
- package/dist/options.d.ts +1 -0
- package/dist/options.d.ts.map +1 -1
- package/dist/options.js +16 -8
- package/dist/options.js.map +1 -1
- package/dist/provider.d.ts +1 -0
- package/dist/provider.d.ts.map +1 -1
- package/dist/provider.js +1 -0
- package/dist/provider.js.map +1 -1
- package/dist/sign.d.ts +27 -0
- package/dist/sign.d.ts.map +1 -0
- package/dist/sign.js +219 -0
- package/dist/sign.js.map +1 -0
- package/dist/token.d.ts +1 -0
- package/dist/token.d.ts.map +1 -1
- package/dist/token.js +4 -0
- package/dist/token.js.map +1 -1
- package/package.json +5 -5
- package/pulse.package.json +11 -5
- package/pulsewasm.compiler.cjs +156 -15
- package/pulsewasm.manifest.cjs +7 -5
- package/pulsewasm.native.cjs +6 -3
package/pulsewasm.compiler.cjs
CHANGED
|
@@ -483,7 +483,101 @@ function parseEs256Jwk(ts, sourceFile, node, diagnostics) {
|
|
|
483
483
|
return deepFreeze(output);
|
|
484
484
|
}
|
|
485
485
|
|
|
486
|
-
function
|
|
486
|
+
function parseRs256Jwk(ts, sourceFile, node, diagnostics) {
|
|
487
|
+
const members = objectMembers(
|
|
488
|
+
ts,
|
|
489
|
+
sourceFile,
|
|
490
|
+
node,
|
|
491
|
+
new Set(['kty', 'n', 'e', 'alg', 'use', 'key_ops', 'kid']),
|
|
492
|
+
diagnostics,
|
|
493
|
+
jwtContracts.JWT_DIAGNOSTIC_CODES.JWK_INVALID,
|
|
494
|
+
'RS256 public JWK'
|
|
495
|
+
);
|
|
496
|
+
const required = {};
|
|
497
|
+
for (const name of ['kty', 'n', 'e']) {
|
|
498
|
+
const value = members.has(name) ? literalString(ts, members.get(name)) : undefined;
|
|
499
|
+
if (value === undefined || value.length === 0) {
|
|
500
|
+
diagnostics.push(makeDiagnostic(
|
|
501
|
+
sourceFile,
|
|
502
|
+
members.get(name) || node,
|
|
503
|
+
jwtContracts.JWT_DIAGNOSTIC_CODES.JWK_INVALID,
|
|
504
|
+
`RS256 public JWK requires static ${name}.`,
|
|
505
|
+
'Use a bounded inline public RSA JWK.'
|
|
506
|
+
));
|
|
507
|
+
} else required[name] = value;
|
|
508
|
+
}
|
|
509
|
+
if (required.kty !== undefined && required.kty !== 'RSA') diagnostics.push(makeDiagnostic(
|
|
510
|
+
sourceFile,
|
|
511
|
+
members.get('kty') || node,
|
|
512
|
+
jwtContracts.JWT_DIAGNOSTIC_CODES.JWK_INVALID,
|
|
513
|
+
'RS256 public JWK kty must be RSA.',
|
|
514
|
+
'Use kty: "RSA".'
|
|
515
|
+
));
|
|
516
|
+
for (const name of ['n', 'e']) {
|
|
517
|
+
const text = required[name];
|
|
518
|
+
const bytes = typeof text === 'string' && /^[A-Za-z0-9_-]+$/.test(text) && text.length <= 683 ? Buffer.from(text, 'base64url') : Buffer.alloc(0);
|
|
519
|
+
const valid = bytes.length > 0 && bytes[0] !== 0 && bytes.toString('base64url') === text
|
|
520
|
+
&& (name === 'n' ? [256, 384, 512].includes(bytes.length) && bytes[0] >= 128 && (bytes.at(-1) & 1)
|
|
521
|
+
: bytes.length <= 4 && bytes.readUIntBE(0, bytes.length) >= 3 && (bytes.at(-1) & 1));
|
|
522
|
+
if (!valid) diagnostics.push(makeDiagnostic(sourceFile, members.get(name) || node,
|
|
523
|
+
jwtContracts.JWT_DIAGNOSTIC_CODES.JWK_INVALID, 'RSA requires canonical 2048/3072/4096-bit n and an odd 32-bit exponent >= 3.', 'Use a supported public RSA JWK.'));
|
|
524
|
+
}
|
|
525
|
+
|
|
526
|
+
const output = { ...required };
|
|
527
|
+
for (const name of ['alg', 'use', 'kid']) {
|
|
528
|
+
if (!members.has(name)) continue;
|
|
529
|
+
const value = parseOptionalString(
|
|
530
|
+
ts,
|
|
531
|
+
sourceFile,
|
|
532
|
+
members.get(name),
|
|
533
|
+
diagnostics,
|
|
534
|
+
`RS256 public JWK ${name}`,
|
|
535
|
+
jwtContracts.JWT_DIAGNOSTIC_CODES.JWK_INVALID
|
|
536
|
+
);
|
|
537
|
+
if (value !== undefined) output[name] = value;
|
|
538
|
+
}
|
|
539
|
+
if (output.alg !== undefined && output.alg !== 'RS256') diagnostics.push(makeDiagnostic(
|
|
540
|
+
sourceFile,
|
|
541
|
+
members.get('alg'),
|
|
542
|
+
jwtContracts.JWT_DIAGNOSTIC_CODES.KEY_ALGORITHM_MISMATCH,
|
|
543
|
+
'RS256 public JWK alg must be absent or RS256.',
|
|
544
|
+
'Remove alg or use alg: "RS256".'
|
|
545
|
+
));
|
|
546
|
+
if (output.use !== undefined && output.use !== 'sig') diagnostics.push(makeDiagnostic(
|
|
547
|
+
sourceFile,
|
|
548
|
+
members.get('use'),
|
|
549
|
+
jwtContracts.JWT_DIAGNOSTIC_CODES.JWK_INVALID,
|
|
550
|
+
'RS256 public JWK use must be absent or sig.',
|
|
551
|
+
'Remove use or use use: "sig".'
|
|
552
|
+
));
|
|
553
|
+
if (members.has('key_ops')) {
|
|
554
|
+
const operations = literalStringArray(
|
|
555
|
+
ts,
|
|
556
|
+
sourceFile,
|
|
557
|
+
members.get('key_ops'),
|
|
558
|
+
diagnostics,
|
|
559
|
+
{
|
|
560
|
+
label: 'RS256 public JWK key_ops',
|
|
561
|
+
category: 'jwk-key-ops',
|
|
562
|
+
maximum: 1,
|
|
563
|
+
code: jwtContracts.JWT_DIAGNOSTIC_CODES.JWK_INVALID,
|
|
564
|
+
limitCode: jwtContracts.JWT_DIAGNOSTIC_CODES.JWK_INVALID
|
|
565
|
+
}
|
|
566
|
+
);
|
|
567
|
+
if (operations.length !== 1 || operations[0] !== 'verify') diagnostics.push(makeDiagnostic(
|
|
568
|
+
sourceFile,
|
|
569
|
+
members.get('key_ops'),
|
|
570
|
+
jwtContracts.JWT_DIAGNOSTIC_CODES.JWK_INVALID,
|
|
571
|
+
'RS256 public JWK key_ops must be absent or exactly ["verify"].',
|
|
572
|
+
'Remove key_ops or declare only verify.'
|
|
573
|
+
));
|
|
574
|
+
output.key_ops = operations;
|
|
575
|
+
}
|
|
576
|
+
if (output.kid !== undefined && Buffer.byteLength(output.kid) > 256) diagnostics.push(makeDiagnostic(sourceFile, members.get('kid'), jwtContracts.JWT_DIAGNOSTIC_CODES.JWK_INVALID, 'RSA kid exceeds 256 UTF-8 bytes.', 'Use a bounded kid.'));
|
|
577
|
+
return deepFreeze(output);
|
|
578
|
+
}
|
|
579
|
+
|
|
580
|
+
function parseEs256Jwks(ts, sourceFile, node, diagnostics, algorithm = 'ES256') {
|
|
487
581
|
const current = unwrapExpression(ts, node);
|
|
488
582
|
if (!current || !ts.isArrayLiteralExpression(current)) {
|
|
489
583
|
diagnostics.push(makeDiagnostic(
|
|
@@ -514,7 +608,7 @@ function parseEs256Jwks(ts, sourceFile, node, diagnostics) {
|
|
|
514
608
|
}
|
|
515
609
|
));
|
|
516
610
|
const keys = current.elements.slice(0, jwtContracts.JWT_RESOURCE_LIMITS.jwksEntries)
|
|
517
|
-
.map((entry) => parseEs256Jwk(ts, sourceFile, entry, diagnostics));
|
|
611
|
+
.map((entry) => (algorithm === 'RS256' ? parseRs256Jwk : parseEs256Jwk)(ts, sourceFile, entry, diagnostics));
|
|
518
612
|
const kids = keys.map((entry) => entry.kid).filter(Boolean);
|
|
519
613
|
if (new Set(kids).size !== kids.length) diagnostics.push(makeDiagnostic(
|
|
520
614
|
sourceFile,
|
|
@@ -527,13 +621,14 @@ function parseEs256Jwks(ts, sourceFile, node, diagnostics) {
|
|
|
527
621
|
}
|
|
528
622
|
|
|
529
623
|
function keyArtifact(type, keys) {
|
|
624
|
+
const algorithm = keys[0]?.kty === 'RSA' ? 'rs256' : 'es256';
|
|
530
625
|
const material = type === 'jwk'
|
|
531
626
|
? { type, key: keys[0] }
|
|
532
627
|
: { type, keys };
|
|
533
628
|
const materialHash = crypto.createHash('sha256')
|
|
534
629
|
.update(stableStringify(material))
|
|
535
630
|
.digest('hex');
|
|
536
|
-
const id = `pulse.jwt
|
|
631
|
+
const id = `pulse.jwt.${algorithm}-key.${materialHash.slice(0, 24)}`;
|
|
537
632
|
return Object.freeze({
|
|
538
633
|
descriptor: Object.freeze({
|
|
539
634
|
type,
|
|
@@ -547,14 +642,14 @@ function keyArtifact(type, keys) {
|
|
|
547
642
|
secretBinding: null
|
|
548
643
|
}),
|
|
549
644
|
realizationArtifact: deepFreeze({
|
|
550
|
-
version:
|
|
645
|
+
version: `pulse.jwt-${algorithm}-key-artifact.v1`,
|
|
551
646
|
id,
|
|
552
647
|
contractId: jwtContracts.JWT_CONTRACT_ID,
|
|
553
648
|
package: jwtContracts.JWT_PACKAGE_NAME,
|
|
554
|
-
kind:
|
|
555
|
-
mediaType:
|
|
649
|
+
kind: `jwt-${algorithm}-static-public-key`,
|
|
650
|
+
mediaType: `application/vnd.pulse.jwt-${algorithm}-key+json`,
|
|
556
651
|
materialHash,
|
|
557
|
-
redaction: ['x', 'y'],
|
|
652
|
+
redaction: algorithm === 'rs256' ? ['n', 'e'] : ['x', 'y'],
|
|
558
653
|
data: material
|
|
559
654
|
})
|
|
560
655
|
});
|
|
@@ -620,7 +715,7 @@ function parseKey(ts, sourceFile, node, algorithms, diagnostics) {
|
|
|
620
715
|
});
|
|
621
716
|
}
|
|
622
717
|
|
|
623
|
-
if (algorithms.length !== 1 || algorithms[0]
|
|
718
|
+
if (algorithms.length !== 1 || !['ES256', 'RS256'].includes(algorithms[0])) {
|
|
624
719
|
diagnostics.push(makeDiagnostic(
|
|
625
720
|
sourceFile,
|
|
626
721
|
node,
|
|
@@ -649,7 +744,7 @@ function parseKey(ts, sourceFile, node, algorithms, diagnostics) {
|
|
|
649
744
|
));
|
|
650
745
|
return keyArtifact(
|
|
651
746
|
type,
|
|
652
|
-
[parseEs256Jwk(ts, sourceFile, members.get('key'), diagnostics)]
|
|
747
|
+
[(algorithms[0] === 'RS256' ? parseRs256Jwk : parseEs256Jwk)(ts, sourceFile, members.get('key'), diagnostics)]
|
|
653
748
|
);
|
|
654
749
|
}
|
|
655
750
|
if (members.size !== 2 || !members.has('keys')) diagnostics.push(makeDiagnostic(
|
|
@@ -661,7 +756,7 @@ function parseKey(ts, sourceFile, node, algorithms, diagnostics) {
|
|
|
661
756
|
));
|
|
662
757
|
return keyArtifact(
|
|
663
758
|
type,
|
|
664
|
-
parseEs256Jwks(ts, sourceFile, members.get('keys'), diagnostics)
|
|
759
|
+
parseEs256Jwks(ts, sourceFile, members.get('keys'), diagnostics, algorithms[0])
|
|
665
760
|
);
|
|
666
761
|
}
|
|
667
762
|
|
|
@@ -797,6 +892,7 @@ function collectFacadeBindings(ts, sourceFile, manifest) {
|
|
|
797
892
|
const bindings = {
|
|
798
893
|
namespaces: new Set(),
|
|
799
894
|
verify: new Set(),
|
|
895
|
+
sign: new Set(),
|
|
800
896
|
bearer: new Set(),
|
|
801
897
|
imports: []
|
|
802
898
|
};
|
|
@@ -821,6 +917,7 @@ function collectFacadeBindings(ts, sourceFile, manifest) {
|
|
|
821
917
|
const local = specifier.name.text;
|
|
822
918
|
if (imported === 'jwt' || imported === 'default') bindings.namespaces.add(local);
|
|
823
919
|
if (imported === 'verify') bindings.verify.add(local);
|
|
920
|
+
if (imported === 'sign') bindings.sign.add(local);
|
|
824
921
|
if (imported === 'bearer') bindings.bearer.add(local);
|
|
825
922
|
bindings.imports.push({ imported, local, loc: locFor(sourceFile, specifier.name) });
|
|
826
923
|
}
|
|
@@ -834,13 +931,14 @@ function resolveFacadeCall(ts, call, bindings) {
|
|
|
834
931
|
if (expression && ts.isPropertyAccessExpression(expression)) {
|
|
835
932
|
const object = unwrapExpression(ts, expression.expression);
|
|
836
933
|
if (object && ts.isIdentifier(object) && bindings.namespaces.has(object.text)) {
|
|
837
|
-
if (
|
|
934
|
+
if (['verify', 'bearer', 'sign'].includes(expression.name.text)) {
|
|
838
935
|
return { method: expression.name.text, local: object.text };
|
|
839
936
|
}
|
|
840
937
|
}
|
|
841
938
|
}
|
|
842
939
|
if (expression && ts.isIdentifier(expression)) {
|
|
843
940
|
if (bindings.verify.has(expression.text)) return { method: 'verify', local: expression.text };
|
|
941
|
+
if (bindings.sign.has(expression.text)) return { method: 'sign', local: expression.text };
|
|
844
942
|
if (bindings.bearer.has(expression.text)) return { method: 'bearer', local: expression.text };
|
|
845
943
|
}
|
|
846
944
|
return undefined;
|
|
@@ -1066,6 +1164,45 @@ function buildJwtLoweringPlan(inputs = {}) {
|
|
|
1066
1164
|
function visit(node) {
|
|
1067
1165
|
if (ts.isCallExpression(node)) {
|
|
1068
1166
|
const resolved = resolveFacadeCall(ts, node, bindings);
|
|
1167
|
+
if (resolved && resolved.method === 'sign') {
|
|
1168
|
+
const contextName = enclosingContextName(ts, node);
|
|
1169
|
+
const placement = placementForCall(ts, node, contextName);
|
|
1170
|
+
const error = (code, message) => diagnostics.push(makeDiagnostic(sourceFile, node, code, message,
|
|
1171
|
+
'Await jwt.sign(ctx, claims, { algorithm: "HS256", key: { type: "secret", binding: "NAME" }, expiresInSeconds: 45 }) into a local or in ctx.parallel.'));
|
|
1172
|
+
if (node.arguments.length !== 3 || !currentContextArgument(ts, node.arguments[0], contextName)) {
|
|
1173
|
+
error(jwtContracts.JWT_DIAGNOSTIC_CODES.ARGUMENT_SHAPE_UNSUPPORTED, 'Signing requires the current handler context, claims, and static options.');
|
|
1174
|
+
return;
|
|
1175
|
+
}
|
|
1176
|
+
if (!(placement.kind === 'variable' && placement.awaited || placement.parallel && ts.isAwaitExpression(placement.parallel.parent))) {
|
|
1177
|
+
error(jwtContracts.JWT_DIAGNOSTIC_CODES.PLACEMENT_UNSUPPORTED, 'Signing must be directly awaited or grouped.');
|
|
1178
|
+
}
|
|
1179
|
+
const members = objectMembers(ts, sourceFile, node.arguments[2], new Set(['algorithm', 'key', 'expiresInSeconds', 'kid']), diagnostics,
|
|
1180
|
+
jwtContracts.JWT_DIAGNOSTIC_CODES.OPTIONS_LITERAL_REQUIRED, 'JWT signing options');
|
|
1181
|
+
const algorithm = literalString(ts, members.get('algorithm'));
|
|
1182
|
+
const expiresInSeconds = literalNumber(ts, members.get('expiresInSeconds'));
|
|
1183
|
+
if (!['HS256', 'ES256', 'RS256'].includes(algorithm)) error(jwtContracts.JWT_DIAGNOSTIC_CODES.ALGORITHM_UNSUPPORTED, 'Signing supports HS256, ES256 and RS256.');
|
|
1184
|
+
const kid = members.has('kid') ? literalString(ts, members.get('kid')) : undefined;
|
|
1185
|
+
if (members.has('kid') && (typeof kid !== 'string' || !kid.length || kid.includes('\0') || Buffer.byteLength(kid) > 256)) {
|
|
1186
|
+
error(jwtContracts.JWT_DIAGNOSTIC_CODES.KEY_TYPE_UNSUPPORTED, 'Signing kid must be a nonempty literal of at most 256 UTF-8 bytes.');
|
|
1187
|
+
}
|
|
1188
|
+
if (!Number.isSafeInteger(expiresInSeconds) || expiresInSeconds < 1) {
|
|
1189
|
+
error(jwtContracts.JWT_DIAGNOSTIC_CODES.POLICY_LIMIT_EXCEEDED, 'Signing requires a literal positive safe-integer lifetime in seconds.');
|
|
1190
|
+
}
|
|
1191
|
+
const key = parseKey(ts, sourceFile, members.get('key') || node, ['HS256'], diagnostics);
|
|
1192
|
+
if (key.descriptor.type !== 'secret' || !key.resource.secretBinding || key.resource.secretBinding.length > 256
|
|
1193
|
+
|| key.resource.secretBinding.includes('\0') || !key.resource.secretBinding.trim()) {
|
|
1194
|
+
error(jwtContracts.JWT_DIAGNOSTIC_CODES.KEY_TYPE_UNSUPPORTED, 'Signing requires a bounded named secret binding.');
|
|
1195
|
+
}
|
|
1196
|
+
const effect = Object.freeze({ ...jwtContracts.JWT_SIGN_OPERATION, placement: placement.kind,
|
|
1197
|
+
range: rangeFor(sourceFile, node), loc: locFor(sourceFile, node), resource: key.resource,
|
|
1198
|
+
payload: { algorithm, expiresInSeconds: expiresInSeconds || 0, ...(kid === undefined ? {} : { kid }) },
|
|
1199
|
+
runtimeInputs: [{ name: 'claims', argumentIndex: 1, source: 'package-call-argument' }],
|
|
1200
|
+
providerRequirements: jwtContracts.JWT_SIGN_CONTRACT.providerRequirements,
|
|
1201
|
+
schemaReferences: [], redaction: ['claims', 'token', 'signature', 'key-material', 'secret-value'] });
|
|
1202
|
+
entries.push(Object.freeze({ kind: 'jwt-sign', symbol: 'jwt.sign', placement: placement.kind,
|
|
1203
|
+
awaited: placement.awaited, algorithms: [], signingAlgorithm: algorithm, key: key.descriptor,
|
|
1204
|
+
providerRequirements: effect.providerRequirements, range: effect.range, loc: effect.loc, canonicalEffect: effect }));
|
|
1205
|
+
}
|
|
1069
1206
|
if (resolved && resolved.method === 'bearer') bearerCalls.push(node);
|
|
1070
1207
|
if (resolved && resolved.method === 'verify') {
|
|
1071
1208
|
const contextName = enclosingContextName(ts, node);
|
|
@@ -1141,14 +1278,18 @@ function buildJwtLoweringPlan(inputs = {}) {
|
|
|
1141
1278
|
'Pass jwt.bearer(ctx.req) directly as the second jwt.verify argument.'
|
|
1142
1279
|
));
|
|
1143
1280
|
}
|
|
1144
|
-
for (const entry of entries) validateResultAccess(ts, sourceFile, entry, diagnostics);
|
|
1281
|
+
for (const entry of entries.filter(entry => entry.kind === 'jwt-verify')) validateResultAccess(ts, sourceFile, entry, diagnostics);
|
|
1145
1282
|
|
|
1146
1283
|
const canonicalEffects = entries.map((entry) => entry.canonicalEffect);
|
|
1147
1284
|
const canonicalIntrinsics = entries.map((entry) => entry.bearerIntrinsic).filter(Boolean);
|
|
1148
1285
|
const schemaReferences = entries.map((entry) => entry.schemaReference).filter(Boolean);
|
|
1149
|
-
const cryptoRequirements = jwtContracts.jwtCryptoRequirements(
|
|
1286
|
+
const cryptoRequirements = Object.freeze([...jwtContracts.jwtCryptoRequirements(
|
|
1150
1287
|
entries.flatMap((entry) => entry.algorithms)
|
|
1151
|
-
)
|
|
1288
|
+
), ...[...new Set(entries.filter(entry => entry.kind === 'jwt-sign').map(entry =>
|
|
1289
|
+
entry.signingAlgorithm === 'HS256' ? 'HMAC-SHA256' : entry.signingAlgorithm))].map(algorithm => ({
|
|
1290
|
+
version: packageContracts.PACKAGE_CRYPTO_REQUIREMENT_VERSION, requestedBy: jwtContracts.JWT_PACKAGE_NAME,
|
|
1291
|
+
semanticOwner: '@pulse-compute/crypto', reachable: true, algorithms: [algorithm]
|
|
1292
|
+
}))]);
|
|
1152
1293
|
const keyArtifacts = entries
|
|
1153
1294
|
.filter((entry) => entry.key.keyArtifactId)
|
|
1154
1295
|
.map((entry) => Object.freeze({
|
|
@@ -1161,7 +1302,7 @@ function buildJwtLoweringPlan(inputs = {}) {
|
|
|
1161
1302
|
const uniqueKeyArtifacts = [...new Map(keyArtifacts.map((entry) => [entry.id, entry])).values()];
|
|
1162
1303
|
const realizationArtifacts = [...new Map(privateRealizationArtifacts
|
|
1163
1304
|
.map((entry) => [entry.id, entry])).values()];
|
|
1164
|
-
const guestUnits = entries.some((entry) => entry.algorithms.includes('ES256'))
|
|
1305
|
+
const guestUnits = entries.some((entry) => entry.algorithms.some(algorithm => ['ES256', 'RS256'].includes(algorithm)) || ['ES256', 'RS256'].includes(entry.signingAlgorithm))
|
|
1165
1306
|
? Object.freeze([loadCryptoGuestContribution()])
|
|
1166
1307
|
: Object.freeze([]);
|
|
1167
1308
|
const artifact = deepFreeze(normalizeArtifact({
|
package/pulsewasm.manifest.cjs
CHANGED
|
@@ -9,7 +9,7 @@ const pulseWasmManifest = Object.freeze({
|
|
|
9
9
|
facade: Object.freeze({
|
|
10
10
|
namespace: 'jwt',
|
|
11
11
|
import: '@pulse-compute/jwt',
|
|
12
|
-
symbols: Object.freeze(['bearer', 'verify'])
|
|
12
|
+
symbols: Object.freeze(['bearer', 'verify', 'sign'])
|
|
13
13
|
}),
|
|
14
14
|
publicApi: Object.freeze({
|
|
15
15
|
package: '@pulse-compute/jwt',
|
|
@@ -18,12 +18,13 @@ const pulseWasmManifest = Object.freeze({
|
|
|
18
18
|
'JwtError',
|
|
19
19
|
'bearer',
|
|
20
20
|
'jwt',
|
|
21
|
-
'verify'
|
|
21
|
+
'verify',
|
|
22
|
+
'sign'
|
|
22
23
|
]),
|
|
23
24
|
compatibilitySubpaths: Object.freeze([]),
|
|
24
25
|
loweringIntentionallyNarrow: true
|
|
25
26
|
}),
|
|
26
|
-
description: 'Package-owned Native lowering contract for bounded, provider-neutral JWT verification.',
|
|
27
|
+
description: 'Package-owned Native lowering contract for bounded, provider-neutral JWT verification and HS256 signing.',
|
|
27
28
|
compiler: Object.freeze({
|
|
28
29
|
version: 'pulsewasm.lowerable-compiler-builder.v1',
|
|
29
30
|
entry: './pulsewasm.compiler.cjs',
|
|
@@ -63,6 +64,7 @@ const pulseWasmManifest = Object.freeze({
|
|
|
63
64
|
])
|
|
64
65
|
}),
|
|
65
66
|
lowerings: Object.freeze([
|
|
67
|
+
Object.freeze({ tsSymbol: 'jwt.sign', asSymbol: 'pulse_jwt_sign', callShape: 'claims-static-sign-policy', hostCapabilities: Object.freeze(['clock', 'result']) }),
|
|
66
68
|
Object.freeze({
|
|
67
69
|
tsSymbol: 'jwt.verify',
|
|
68
70
|
asSymbol: 'pulse_jwt_verify',
|
|
@@ -85,11 +87,11 @@ const pulseWasmManifest = Object.freeze({
|
|
|
85
87
|
realization: 'guest-source:pulse-hmac-as',
|
|
86
88
|
implementation: 'pulse-hmac-as.v1',
|
|
87
89
|
packageSource: 'pulse-jwt-as',
|
|
88
|
-
algorithms: Object.freeze(['HS256', 'ES256']),
|
|
90
|
+
algorithms: Object.freeze(['HS256', 'ES256', 'RS256']),
|
|
89
91
|
keyTypes: Object.freeze(['secret', 'jwk', 'jwks']),
|
|
90
92
|
guestUnitRequired: false,
|
|
91
93
|
guestUnits: Object.freeze({
|
|
92
|
-
ES256: 'pulse.crypto.es256.rustcrypto-p256.v1'
|
|
94
|
+
ES256: 'pulse.crypto.es256.rustcrypto-p256.v1', RS256: 'pulse.crypto.es256.rustcrypto-p256.v1'
|
|
93
95
|
}),
|
|
94
96
|
portable: true,
|
|
95
97
|
automaticFallback: false
|
package/pulsewasm.native.cjs
CHANGED
|
@@ -13,8 +13,8 @@ function sha256(value) {
|
|
|
13
13
|
return crypto.createHash('sha256').update(value).digest('hex');
|
|
14
14
|
}
|
|
15
15
|
|
|
16
|
-
function pulseJwtAssemblyScriptSource() {
|
|
17
|
-
const source = fs.readFileSync(sourceFile, 'utf8');
|
|
16
|
+
function pulseJwtAssemblyScriptSource(options = {}) {
|
|
17
|
+
const source = fs.readFileSync(sourceFile, 'utf8') + (options.includeSigning === true ? '\n' + fs.readFileSync(path.join(__dirname, 'as/sign.as.ts'), 'utf8') : '');
|
|
18
18
|
return Object.freeze({
|
|
19
19
|
version: JWT_NATIVE_SOURCE_CONTRACT_VERSION,
|
|
20
20
|
id: 'pulse-jwt-as',
|
|
@@ -26,13 +26,16 @@ function pulseJwtAssemblyScriptSource() {
|
|
|
26
26
|
origin: 'package-source',
|
|
27
27
|
sourceIncluded: true,
|
|
28
28
|
sourceFile: sourceRelativeFile,
|
|
29
|
+
sourceFiles: Object.freeze([sourceRelativeFile, ...(options.includeSigning === true ? ['as/sign.as.ts'] : [])]),
|
|
29
30
|
source,
|
|
30
31
|
sourceBytes: Buffer.byteLength(source),
|
|
31
32
|
sourceSha256: sha256(source),
|
|
32
33
|
imports: Object.freeze([]),
|
|
33
34
|
exports: Object.freeze([
|
|
34
35
|
'pulse_jwt_verify',
|
|
35
|
-
'pulse_jwt_fastly_verify'
|
|
36
|
+
'pulse_jwt_fastly_verify',
|
|
37
|
+
'pulse_jwt_sign',
|
|
38
|
+
...(options.includeSigning === true ? ['pulse_jwt_fastly_sign'] : [])
|
|
36
39
|
]),
|
|
37
40
|
semanticOwnership: Object.freeze([
|
|
38
41
|
'compact-jws-parsing',
|