@sd-jwt/core 0.13.0 → 0.14.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/CHANGELOG.md +11 -0
- package/dist/index.d.mts +12 -2
- package/dist/index.d.ts +12 -2
- package/dist/index.js +19 -10
- package/dist/index.mjs +19 -10
- package/package.json +7 -7
- package/src/index.ts +15 -16
- package/src/jwt.ts +11 -0
- package/src/kbjwt.ts +9 -1
- package/src/test/kbjwt.spec.ts +17 -3
package/CHANGELOG.md
CHANGED
|
@@ -3,6 +3,17 @@
|
|
|
3
3
|
All notable changes to this project will be documented in this file.
|
|
4
4
|
See [Conventional Commits](https://conventionalcommits.org) for commit guidelines.
|
|
5
5
|
|
|
6
|
+
# [0.14.0](https://github.com/openwallet-foundation-labs/sd-jwt-js/compare/v0.13.0...v0.14.0) (2025-06-30)
|
|
7
|
+
|
|
8
|
+
|
|
9
|
+
### Features
|
|
10
|
+
|
|
11
|
+
* Add Verifier options ([#297](https://github.com/openwallet-foundation-labs/sd-jwt-js/issues/297)) ([2a6a367](https://github.com/openwallet-foundation-labs/sd-jwt-js/commit/2a6a3674f94742f48feaf660056226b1a54145e7))
|
|
12
|
+
|
|
13
|
+
|
|
14
|
+
|
|
15
|
+
|
|
16
|
+
|
|
6
17
|
# [0.13.0](https://github.com/openwallet-foundation-labs/sd-jwt-js/compare/v0.12.0...v0.13.0) (2025-06-25)
|
|
7
18
|
|
|
8
19
|
**Note:** Version bump only for package @sd-jwt/core
|
package/dist/index.d.mts
CHANGED
|
@@ -20,6 +20,15 @@ type VerifierOptions = {
|
|
|
20
20
|
* allowed skew for the current time in seconds. Positive value that will lower the iat and nbf checks, and increase the exp check.
|
|
21
21
|
*/
|
|
22
22
|
skewSeconds?: number;
|
|
23
|
+
/**
|
|
24
|
+
* required claim keys for the payload.
|
|
25
|
+
* If the payload does not contain these keys, the verification will fail.
|
|
26
|
+
*/
|
|
27
|
+
requiredClaimKeys?: string[];
|
|
28
|
+
/**
|
|
29
|
+
* nonce used to verify the key binding jwt to prevent replay attacks.
|
|
30
|
+
*/
|
|
31
|
+
keyBindingNonce?: string;
|
|
23
32
|
};
|
|
24
33
|
declare class Jwt<Header extends Record<string, unknown> = Record<string, unknown>, Payload extends Record<string, unknown> = Record<string, unknown>> {
|
|
25
34
|
header?: Header;
|
|
@@ -55,6 +64,7 @@ declare class KBJwt<Header extends kbHeader = kbHeader, Payload extends kbPayloa
|
|
|
55
64
|
verifyKB(values: {
|
|
56
65
|
verifier: KbVerifier;
|
|
57
66
|
payload: JwtPayload;
|
|
67
|
+
nonce: string;
|
|
58
68
|
}): Promise<{
|
|
59
69
|
payload: Payload;
|
|
60
70
|
header: Header;
|
|
@@ -204,7 +214,7 @@ declare class SDJwtInstance<ExtendedPayload extends SdJwtPayload> {
|
|
|
204
214
|
present<T extends Record<string, unknown>>(encodedSDJwt: string, presentationFrame?: PresentationFrame<T>, options?: {
|
|
205
215
|
kb?: KBOptions;
|
|
206
216
|
}): Promise<SDJWTCompact>;
|
|
207
|
-
verify(encodedSDJwt: string,
|
|
217
|
+
verify(encodedSDJwt: string, options?: VerifierOptions): Promise<{
|
|
208
218
|
payload: unknown;
|
|
209
219
|
header: Record<string, unknown> | undefined;
|
|
210
220
|
kb?: undefined;
|
|
@@ -261,7 +271,7 @@ declare class SDJwtGeneralJSONInstance<ExtendedPayload extends SdJwtPayload> {
|
|
|
261
271
|
present<T extends Record<string, unknown>>(generalJSON: GeneralJSON, presentationFrame?: PresentationFrame<T>, options?: {
|
|
262
272
|
kb?: KBOptions;
|
|
263
273
|
}): Promise<GeneralJSON>;
|
|
264
|
-
verify(generalJSON: GeneralJSON,
|
|
274
|
+
verify(generalJSON: GeneralJSON, options?: VerifierOptions): Promise<{
|
|
265
275
|
payload: unknown;
|
|
266
276
|
headers: any[];
|
|
267
277
|
kb?: undefined;
|
package/dist/index.d.ts
CHANGED
|
@@ -20,6 +20,15 @@ type VerifierOptions = {
|
|
|
20
20
|
* allowed skew for the current time in seconds. Positive value that will lower the iat and nbf checks, and increase the exp check.
|
|
21
21
|
*/
|
|
22
22
|
skewSeconds?: number;
|
|
23
|
+
/**
|
|
24
|
+
* required claim keys for the payload.
|
|
25
|
+
* If the payload does not contain these keys, the verification will fail.
|
|
26
|
+
*/
|
|
27
|
+
requiredClaimKeys?: string[];
|
|
28
|
+
/**
|
|
29
|
+
* nonce used to verify the key binding jwt to prevent replay attacks.
|
|
30
|
+
*/
|
|
31
|
+
keyBindingNonce?: string;
|
|
23
32
|
};
|
|
24
33
|
declare class Jwt<Header extends Record<string, unknown> = Record<string, unknown>, Payload extends Record<string, unknown> = Record<string, unknown>> {
|
|
25
34
|
header?: Header;
|
|
@@ -55,6 +64,7 @@ declare class KBJwt<Header extends kbHeader = kbHeader, Payload extends kbPayloa
|
|
|
55
64
|
verifyKB(values: {
|
|
56
65
|
verifier: KbVerifier;
|
|
57
66
|
payload: JwtPayload;
|
|
67
|
+
nonce: string;
|
|
58
68
|
}): Promise<{
|
|
59
69
|
payload: Payload;
|
|
60
70
|
header: Header;
|
|
@@ -204,7 +214,7 @@ declare class SDJwtInstance<ExtendedPayload extends SdJwtPayload> {
|
|
|
204
214
|
present<T extends Record<string, unknown>>(encodedSDJwt: string, presentationFrame?: PresentationFrame<T>, options?: {
|
|
205
215
|
kb?: KBOptions;
|
|
206
216
|
}): Promise<SDJWTCompact>;
|
|
207
|
-
verify(encodedSDJwt: string,
|
|
217
|
+
verify(encodedSDJwt: string, options?: VerifierOptions): Promise<{
|
|
208
218
|
payload: unknown;
|
|
209
219
|
header: Record<string, unknown> | undefined;
|
|
210
220
|
kb?: undefined;
|
|
@@ -261,7 +271,7 @@ declare class SDJwtGeneralJSONInstance<ExtendedPayload extends SdJwtPayload> {
|
|
|
261
271
|
present<T extends Record<string, unknown>>(generalJSON: GeneralJSON, presentationFrame?: PresentationFrame<T>, options?: {
|
|
262
272
|
kb?: KBOptions;
|
|
263
273
|
}): Promise<GeneralJSON>;
|
|
264
|
-
verify(generalJSON: GeneralJSON,
|
|
274
|
+
verify(generalJSON: GeneralJSON, options?: VerifierOptions): Promise<{
|
|
265
275
|
payload: unknown;
|
|
266
276
|
headers: any[];
|
|
267
277
|
kb?: undefined;
|
package/dist/index.js
CHANGED
|
@@ -202,6 +202,9 @@ var KBJwt = class _KBJwt extends Jwt {
|
|
|
202
202
|
if (!verified) {
|
|
203
203
|
throw new import_utils2.SDJWTException("Verify Error: Invalid JWT Signature");
|
|
204
204
|
}
|
|
205
|
+
if (this.payload.nonce !== values.nonce) {
|
|
206
|
+
throw new import_utils2.SDJWTException("Verify Error: Invalid Nonce");
|
|
207
|
+
}
|
|
205
208
|
return { payload: this.payload, header: this.header };
|
|
206
209
|
});
|
|
207
210
|
}
|
|
@@ -759,7 +762,7 @@ var _SDJwtInstance = class _SDJwtInstance {
|
|
|
759
762
|
// This function is for verifying the SD JWT
|
|
760
763
|
// If requiredClaimKeys is provided, it will check if the required claim keys are presentation in the SD JWT
|
|
761
764
|
// If requireKeyBindings is true, it will check if the key binding JWT is presentation and verify it
|
|
762
|
-
verify(encodedSDJwt,
|
|
765
|
+
verify(encodedSDJwt, options) {
|
|
763
766
|
return __async(this, null, function* () {
|
|
764
767
|
if (!this.userConfig.hasher) {
|
|
765
768
|
throw new import_utils7.SDJWTException("Hasher not found");
|
|
@@ -770,16 +773,18 @@ var _SDJwtInstance = class _SDJwtInstance {
|
|
|
770
773
|
throw new import_utils7.SDJWTException("Invalid SD JWT");
|
|
771
774
|
}
|
|
772
775
|
const { payload, header } = yield this.validate(encodedSDJwt);
|
|
773
|
-
if (requiredClaimKeys) {
|
|
776
|
+
if (options == null ? void 0 : options.requiredClaimKeys) {
|
|
774
777
|
const keys = yield sdjwt.keys(hasher);
|
|
775
|
-
const missingKeys = requiredClaimKeys.filter(
|
|
778
|
+
const missingKeys = options.requiredClaimKeys.filter(
|
|
779
|
+
(k) => !keys.includes(k)
|
|
780
|
+
);
|
|
776
781
|
if (missingKeys.length > 0) {
|
|
777
782
|
throw new import_utils7.SDJWTException(
|
|
778
783
|
`Missing required claim keys: ${missingKeys.join(", ")}`
|
|
779
784
|
);
|
|
780
785
|
}
|
|
781
786
|
}
|
|
782
|
-
if (!
|
|
787
|
+
if (!(options == null ? void 0 : options.keyBindingNonce)) {
|
|
783
788
|
return { payload, header };
|
|
784
789
|
}
|
|
785
790
|
if (!sdjwt.kbJwt) {
|
|
@@ -790,7 +795,8 @@ var _SDJwtInstance = class _SDJwtInstance {
|
|
|
790
795
|
}
|
|
791
796
|
const kb = yield sdjwt.kbJwt.verifyKB({
|
|
792
797
|
verifier: this.userConfig.kbVerifier,
|
|
793
|
-
payload
|
|
798
|
+
payload,
|
|
799
|
+
nonce: options.keyBindingNonce
|
|
794
800
|
});
|
|
795
801
|
if (!kb) {
|
|
796
802
|
throw new Error("signature is not valid");
|
|
@@ -1024,7 +1030,7 @@ var SDJwtGeneralJSONInstance = class {
|
|
|
1024
1030
|
// This function is for verifying the SD JWT
|
|
1025
1031
|
// If requiredClaimKeys is provided, it will check if the required claim keys are presentation in the SD JWT
|
|
1026
1032
|
// If requireKeyBindings is true, it will check if the key binding JWT is presentation and verify it
|
|
1027
|
-
verify(generalJSON,
|
|
1033
|
+
verify(generalJSON, options) {
|
|
1028
1034
|
return __async(this, null, function* () {
|
|
1029
1035
|
if (!this.userConfig.hasher) {
|
|
1030
1036
|
throw new import_utils7.SDJWTException("Hasher not found");
|
|
@@ -1036,16 +1042,18 @@ var SDJwtGeneralJSONInstance = class {
|
|
|
1036
1042
|
if (!sdjwt.jwt || !sdjwt.jwt.payload) {
|
|
1037
1043
|
throw new import_utils7.SDJWTException("Invalid SD JWT");
|
|
1038
1044
|
}
|
|
1039
|
-
if (requiredClaimKeys) {
|
|
1045
|
+
if (options == null ? void 0 : options.requiredClaimKeys) {
|
|
1040
1046
|
const keys = yield sdjwt.keys(hasher);
|
|
1041
|
-
const missingKeys = requiredClaimKeys.filter(
|
|
1047
|
+
const missingKeys = options == null ? void 0 : options.requiredClaimKeys.filter(
|
|
1048
|
+
(k) => !keys.includes(k)
|
|
1049
|
+
);
|
|
1042
1050
|
if (missingKeys.length > 0) {
|
|
1043
1051
|
throw new import_utils7.SDJWTException(
|
|
1044
1052
|
`Missing required claim keys: ${missingKeys.join(", ")}`
|
|
1045
1053
|
);
|
|
1046
1054
|
}
|
|
1047
1055
|
}
|
|
1048
|
-
if (!
|
|
1056
|
+
if (!(options == null ? void 0 : options.keyBindingNonce)) {
|
|
1049
1057
|
return { payload, headers };
|
|
1050
1058
|
}
|
|
1051
1059
|
if (!sdjwt.kbJwt) {
|
|
@@ -1056,7 +1064,8 @@ var SDJwtGeneralJSONInstance = class {
|
|
|
1056
1064
|
}
|
|
1057
1065
|
const kb = yield sdjwt.kbJwt.verifyKB({
|
|
1058
1066
|
verifier: this.userConfig.kbVerifier,
|
|
1059
|
-
payload
|
|
1067
|
+
payload,
|
|
1068
|
+
nonce: options.keyBindingNonce
|
|
1060
1069
|
});
|
|
1061
1070
|
if (!kb) {
|
|
1062
1071
|
throw new Error("signature is not valid");
|
package/dist/index.mjs
CHANGED
|
@@ -179,6 +179,9 @@ var KBJwt = class _KBJwt extends Jwt {
|
|
|
179
179
|
if (!verified) {
|
|
180
180
|
throw new SDJWTException2("Verify Error: Invalid JWT Signature");
|
|
181
181
|
}
|
|
182
|
+
if (this.payload.nonce !== values.nonce) {
|
|
183
|
+
throw new SDJWTException2("Verify Error: Invalid Nonce");
|
|
184
|
+
}
|
|
182
185
|
return { payload: this.payload, header: this.header };
|
|
183
186
|
});
|
|
184
187
|
}
|
|
@@ -744,7 +747,7 @@ var _SDJwtInstance = class _SDJwtInstance {
|
|
|
744
747
|
// This function is for verifying the SD JWT
|
|
745
748
|
// If requiredClaimKeys is provided, it will check if the required claim keys are presentation in the SD JWT
|
|
746
749
|
// If requireKeyBindings is true, it will check if the key binding JWT is presentation and verify it
|
|
747
|
-
verify(encodedSDJwt,
|
|
750
|
+
verify(encodedSDJwt, options) {
|
|
748
751
|
return __async(this, null, function* () {
|
|
749
752
|
if (!this.userConfig.hasher) {
|
|
750
753
|
throw new SDJWTException6("Hasher not found");
|
|
@@ -755,16 +758,18 @@ var _SDJwtInstance = class _SDJwtInstance {
|
|
|
755
758
|
throw new SDJWTException6("Invalid SD JWT");
|
|
756
759
|
}
|
|
757
760
|
const { payload, header } = yield this.validate(encodedSDJwt);
|
|
758
|
-
if (requiredClaimKeys) {
|
|
761
|
+
if (options == null ? void 0 : options.requiredClaimKeys) {
|
|
759
762
|
const keys = yield sdjwt.keys(hasher);
|
|
760
|
-
const missingKeys = requiredClaimKeys.filter(
|
|
763
|
+
const missingKeys = options.requiredClaimKeys.filter(
|
|
764
|
+
(k) => !keys.includes(k)
|
|
765
|
+
);
|
|
761
766
|
if (missingKeys.length > 0) {
|
|
762
767
|
throw new SDJWTException6(
|
|
763
768
|
`Missing required claim keys: ${missingKeys.join(", ")}`
|
|
764
769
|
);
|
|
765
770
|
}
|
|
766
771
|
}
|
|
767
|
-
if (!
|
|
772
|
+
if (!(options == null ? void 0 : options.keyBindingNonce)) {
|
|
768
773
|
return { payload, header };
|
|
769
774
|
}
|
|
770
775
|
if (!sdjwt.kbJwt) {
|
|
@@ -775,7 +780,8 @@ var _SDJwtInstance = class _SDJwtInstance {
|
|
|
775
780
|
}
|
|
776
781
|
const kb = yield sdjwt.kbJwt.verifyKB({
|
|
777
782
|
verifier: this.userConfig.kbVerifier,
|
|
778
|
-
payload
|
|
783
|
+
payload,
|
|
784
|
+
nonce: options.keyBindingNonce
|
|
779
785
|
});
|
|
780
786
|
if (!kb) {
|
|
781
787
|
throw new Error("signature is not valid");
|
|
@@ -1009,7 +1015,7 @@ var SDJwtGeneralJSONInstance = class {
|
|
|
1009
1015
|
// This function is for verifying the SD JWT
|
|
1010
1016
|
// If requiredClaimKeys is provided, it will check if the required claim keys are presentation in the SD JWT
|
|
1011
1017
|
// If requireKeyBindings is true, it will check if the key binding JWT is presentation and verify it
|
|
1012
|
-
verify(generalJSON,
|
|
1018
|
+
verify(generalJSON, options) {
|
|
1013
1019
|
return __async(this, null, function* () {
|
|
1014
1020
|
if (!this.userConfig.hasher) {
|
|
1015
1021
|
throw new SDJWTException6("Hasher not found");
|
|
@@ -1021,16 +1027,18 @@ var SDJwtGeneralJSONInstance = class {
|
|
|
1021
1027
|
if (!sdjwt.jwt || !sdjwt.jwt.payload) {
|
|
1022
1028
|
throw new SDJWTException6("Invalid SD JWT");
|
|
1023
1029
|
}
|
|
1024
|
-
if (requiredClaimKeys) {
|
|
1030
|
+
if (options == null ? void 0 : options.requiredClaimKeys) {
|
|
1025
1031
|
const keys = yield sdjwt.keys(hasher);
|
|
1026
|
-
const missingKeys = requiredClaimKeys.filter(
|
|
1032
|
+
const missingKeys = options == null ? void 0 : options.requiredClaimKeys.filter(
|
|
1033
|
+
(k) => !keys.includes(k)
|
|
1034
|
+
);
|
|
1027
1035
|
if (missingKeys.length > 0) {
|
|
1028
1036
|
throw new SDJWTException6(
|
|
1029
1037
|
`Missing required claim keys: ${missingKeys.join(", ")}`
|
|
1030
1038
|
);
|
|
1031
1039
|
}
|
|
1032
1040
|
}
|
|
1033
|
-
if (!
|
|
1041
|
+
if (!(options == null ? void 0 : options.keyBindingNonce)) {
|
|
1034
1042
|
return { payload, headers };
|
|
1035
1043
|
}
|
|
1036
1044
|
if (!sdjwt.kbJwt) {
|
|
@@ -1041,7 +1049,8 @@ var SDJwtGeneralJSONInstance = class {
|
|
|
1041
1049
|
}
|
|
1042
1050
|
const kb = yield sdjwt.kbJwt.verifyKB({
|
|
1043
1051
|
verifier: this.userConfig.kbVerifier,
|
|
1044
|
-
payload
|
|
1052
|
+
payload,
|
|
1053
|
+
nonce: options.keyBindingNonce
|
|
1045
1054
|
});
|
|
1046
1055
|
if (!kb) {
|
|
1047
1056
|
throw new Error("signature is not valid");
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@sd-jwt/core",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.14.0",
|
|
4
4
|
"description": "sd-jwt draft 7 implementation in typescript",
|
|
5
5
|
"main": "dist/index.js",
|
|
6
6
|
"module": "dist/index.mjs",
|
|
@@ -38,13 +38,13 @@
|
|
|
38
38
|
},
|
|
39
39
|
"license": "Apache-2.0",
|
|
40
40
|
"devDependencies": {
|
|
41
|
-
"@sd-jwt/crypto-nodejs": "0.
|
|
41
|
+
"@sd-jwt/crypto-nodejs": "0.14.0"
|
|
42
42
|
},
|
|
43
43
|
"dependencies": {
|
|
44
|
-
"@sd-jwt/decode": "0.
|
|
45
|
-
"@sd-jwt/present": "0.
|
|
46
|
-
"@sd-jwt/types": "0.
|
|
47
|
-
"@sd-jwt/utils": "0.
|
|
44
|
+
"@sd-jwt/decode": "0.14.0",
|
|
45
|
+
"@sd-jwt/present": "0.14.0",
|
|
46
|
+
"@sd-jwt/types": "0.14.0",
|
|
47
|
+
"@sd-jwt/utils": "0.14.0"
|
|
48
48
|
},
|
|
49
49
|
"publishConfig": {
|
|
50
50
|
"access": "public"
|
|
@@ -62,5 +62,5 @@
|
|
|
62
62
|
"esm"
|
|
63
63
|
]
|
|
64
64
|
},
|
|
65
|
-
"gitHead": "
|
|
65
|
+
"gitHead": "121c6d9d3a9df09e9bbaf9a20b7759aa79ec091d"
|
|
66
66
|
}
|
package/src/index.ts
CHANGED
|
@@ -196,11 +196,7 @@ export class SDJwtInstance<ExtendedPayload extends SdJwtPayload> {
|
|
|
196
196
|
// This function is for verifying the SD JWT
|
|
197
197
|
// If requiredClaimKeys is provided, it will check if the required claim keys are presentation in the SD JWT
|
|
198
198
|
// If requireKeyBindings is true, it will check if the key binding JWT is presentation and verify it
|
|
199
|
-
public async verify(
|
|
200
|
-
encodedSDJwt: string,
|
|
201
|
-
requiredClaimKeys?: string[],
|
|
202
|
-
requireKeyBindings?: boolean,
|
|
203
|
-
) {
|
|
199
|
+
public async verify(encodedSDJwt: string, options?: VerifierOptions) {
|
|
204
200
|
if (!this.userConfig.hasher) {
|
|
205
201
|
throw new SDJWTException('Hasher not found');
|
|
206
202
|
}
|
|
@@ -212,9 +208,11 @@ export class SDJwtInstance<ExtendedPayload extends SdJwtPayload> {
|
|
|
212
208
|
}
|
|
213
209
|
const { payload, header } = await this.validate(encodedSDJwt);
|
|
214
210
|
|
|
215
|
-
if (requiredClaimKeys) {
|
|
211
|
+
if (options?.requiredClaimKeys) {
|
|
216
212
|
const keys = await sdjwt.keys(hasher);
|
|
217
|
-
const missingKeys = requiredClaimKeys.filter(
|
|
213
|
+
const missingKeys = options.requiredClaimKeys.filter(
|
|
214
|
+
(k) => !keys.includes(k),
|
|
215
|
+
);
|
|
218
216
|
if (missingKeys.length > 0) {
|
|
219
217
|
throw new SDJWTException(
|
|
220
218
|
`Missing required claim keys: ${missingKeys.join(', ')}`,
|
|
@@ -222,7 +220,7 @@ export class SDJwtInstance<ExtendedPayload extends SdJwtPayload> {
|
|
|
222
220
|
}
|
|
223
221
|
}
|
|
224
222
|
|
|
225
|
-
if (!
|
|
223
|
+
if (!options?.keyBindingNonce) {
|
|
226
224
|
return { payload, header };
|
|
227
225
|
}
|
|
228
226
|
|
|
@@ -235,10 +233,12 @@ export class SDJwtInstance<ExtendedPayload extends SdJwtPayload> {
|
|
|
235
233
|
const kb = await sdjwt.kbJwt.verifyKB({
|
|
236
234
|
verifier: this.userConfig.kbVerifier,
|
|
237
235
|
payload: payload as JwtPayload,
|
|
236
|
+
nonce: options.keyBindingNonce,
|
|
238
237
|
});
|
|
239
238
|
if (!kb) {
|
|
240
239
|
throw new Error('signature is not valid');
|
|
241
240
|
}
|
|
241
|
+
|
|
242
242
|
const sdHashfromKb = kb.payload.sd_hash;
|
|
243
243
|
const sdjwtWithoutKb = new SDJwt({
|
|
244
244
|
jwt: sdjwt.jwt,
|
|
@@ -521,11 +521,7 @@ export class SDJwtGeneralJSONInstance<ExtendedPayload extends SdJwtPayload> {
|
|
|
521
521
|
// This function is for verifying the SD JWT
|
|
522
522
|
// If requiredClaimKeys is provided, it will check if the required claim keys are presentation in the SD JWT
|
|
523
523
|
// If requireKeyBindings is true, it will check if the key binding JWT is presentation and verify it
|
|
524
|
-
public async verify(
|
|
525
|
-
generalJSON: GeneralJSON,
|
|
526
|
-
requiredClaimKeys?: string[],
|
|
527
|
-
requireKeyBindings?: boolean,
|
|
528
|
-
) {
|
|
524
|
+
public async verify(generalJSON: GeneralJSON, options?: VerifierOptions) {
|
|
529
525
|
if (!this.userConfig.hasher) {
|
|
530
526
|
throw new SDJWTException('Hasher not found');
|
|
531
527
|
}
|
|
@@ -539,9 +535,11 @@ export class SDJwtGeneralJSONInstance<ExtendedPayload extends SdJwtPayload> {
|
|
|
539
535
|
throw new SDJWTException('Invalid SD JWT');
|
|
540
536
|
}
|
|
541
537
|
|
|
542
|
-
if (requiredClaimKeys) {
|
|
538
|
+
if (options?.requiredClaimKeys) {
|
|
543
539
|
const keys = await sdjwt.keys(hasher);
|
|
544
|
-
const missingKeys = requiredClaimKeys.filter(
|
|
540
|
+
const missingKeys = options?.requiredClaimKeys.filter(
|
|
541
|
+
(k) => !keys.includes(k),
|
|
542
|
+
);
|
|
545
543
|
if (missingKeys.length > 0) {
|
|
546
544
|
throw new SDJWTException(
|
|
547
545
|
`Missing required claim keys: ${missingKeys.join(', ')}`,
|
|
@@ -549,7 +547,7 @@ export class SDJwtGeneralJSONInstance<ExtendedPayload extends SdJwtPayload> {
|
|
|
549
547
|
}
|
|
550
548
|
}
|
|
551
549
|
|
|
552
|
-
if (!
|
|
550
|
+
if (!options?.keyBindingNonce) {
|
|
553
551
|
return { payload, headers };
|
|
554
552
|
}
|
|
555
553
|
|
|
@@ -562,6 +560,7 @@ export class SDJwtGeneralJSONInstance<ExtendedPayload extends SdJwtPayload> {
|
|
|
562
560
|
const kb = await sdjwt.kbJwt.verifyKB({
|
|
563
561
|
verifier: this.userConfig.kbVerifier,
|
|
564
562
|
payload: payload as JwtPayload,
|
|
563
|
+
nonce: options.keyBindingNonce as string,
|
|
565
564
|
});
|
|
566
565
|
if (!kb) {
|
|
567
566
|
throw new Error('signature is not valid');
|
package/src/jwt.ts
CHANGED
|
@@ -25,6 +25,17 @@ export type VerifierOptions = {
|
|
|
25
25
|
* allowed skew for the current time in seconds. Positive value that will lower the iat and nbf checks, and increase the exp check.
|
|
26
26
|
*/
|
|
27
27
|
skewSeconds?: number;
|
|
28
|
+
|
|
29
|
+
/**
|
|
30
|
+
* required claim keys for the payload.
|
|
31
|
+
* If the payload does not contain these keys, the verification will fail.
|
|
32
|
+
*/
|
|
33
|
+
requiredClaimKeys?: string[];
|
|
34
|
+
|
|
35
|
+
/**
|
|
36
|
+
* nonce used to verify the key binding jwt to prevent replay attacks.
|
|
37
|
+
*/
|
|
38
|
+
keyBindingNonce?: string;
|
|
28
39
|
};
|
|
29
40
|
|
|
30
41
|
// This class is used to create and verify JWT
|
package/src/kbjwt.ts
CHANGED
|
@@ -14,7 +14,11 @@ export class KBJwt<
|
|
|
14
14
|
> extends Jwt<Header, Payload> {
|
|
15
15
|
// Checking the validity of the key binding jwt
|
|
16
16
|
// the type unknown is not good, but we don't know at this point how to get the public key of the signer, this is defined in the kbVerifier
|
|
17
|
-
public async verifyKB(values: {
|
|
17
|
+
public async verifyKB(values: {
|
|
18
|
+
verifier: KbVerifier;
|
|
19
|
+
payload: JwtPayload;
|
|
20
|
+
nonce: string;
|
|
21
|
+
}) {
|
|
18
22
|
if (!this.header || !this.payload || !this.signature) {
|
|
19
23
|
throw new SDJWTException('Verify Error: Invalid JWT');
|
|
20
24
|
}
|
|
@@ -45,6 +49,10 @@ export class KBJwt<
|
|
|
45
49
|
if (!verified) {
|
|
46
50
|
throw new SDJWTException('Verify Error: Invalid JWT Signature');
|
|
47
51
|
}
|
|
52
|
+
if (this.payload.nonce !== values.nonce) {
|
|
53
|
+
throw new SDJWTException('Verify Error: Invalid Nonce');
|
|
54
|
+
}
|
|
55
|
+
|
|
48
56
|
return { payload: this.payload, header: this.header };
|
|
49
57
|
}
|
|
50
58
|
|
package/src/test/kbjwt.spec.ts
CHANGED
|
@@ -117,6 +117,7 @@ describe('KB JWT', () => {
|
|
|
117
117
|
const verified = await decoded.verifyKB({
|
|
118
118
|
verifier: testVerifier,
|
|
119
119
|
payload,
|
|
120
|
+
nonce: 'nonce',
|
|
120
121
|
});
|
|
121
122
|
expect(verified).toStrictEqual({
|
|
122
123
|
header: {
|
|
@@ -177,7 +178,11 @@ describe('KB JWT', () => {
|
|
|
177
178
|
const encodedKbJwt = await kbJwt.sign(testSigner);
|
|
178
179
|
const decoded = KBJwt.fromKBEncode(encodedKbJwt);
|
|
179
180
|
try {
|
|
180
|
-
await decoded.verifyKB({
|
|
181
|
+
await decoded.verifyKB({
|
|
182
|
+
verifier: testVerifier,
|
|
183
|
+
payload,
|
|
184
|
+
nonce: 'nonce',
|
|
185
|
+
});
|
|
181
186
|
} catch (e: unknown) {
|
|
182
187
|
const error = e as SDJWTException;
|
|
183
188
|
expect(error.message).toBe('Invalid Key Binding Jwt');
|
|
@@ -222,7 +227,11 @@ describe('KB JWT', () => {
|
|
|
222
227
|
const encodedKbJwt = await kbJwt.sign(testSigner);
|
|
223
228
|
const decoded = KBJwt.fromKBEncode(encodedKbJwt);
|
|
224
229
|
try {
|
|
225
|
-
await decoded.verifyKB({
|
|
230
|
+
await decoded.verifyKB({
|
|
231
|
+
verifier: testVerifier,
|
|
232
|
+
payload,
|
|
233
|
+
nonce: 'nonce',
|
|
234
|
+
});
|
|
226
235
|
} catch (e: unknown) {
|
|
227
236
|
const error = e as SDJWTException;
|
|
228
237
|
expect(error.message).toBe('Verify Error: Invalid JWT Signature');
|
|
@@ -268,7 +277,11 @@ describe('KB JWT', () => {
|
|
|
268
277
|
const decoded = KBJwt.fromKBEncode(encodedKbJwt);
|
|
269
278
|
decoded.signature = undefined;
|
|
270
279
|
try {
|
|
271
|
-
await decoded.verifyKB({
|
|
280
|
+
await decoded.verifyKB({
|
|
281
|
+
verifier: testVerifier,
|
|
282
|
+
payload,
|
|
283
|
+
nonce: 'nonce',
|
|
284
|
+
});
|
|
272
285
|
} catch (e: unknown) {
|
|
273
286
|
const error = e as SDJWTException;
|
|
274
287
|
expect(error.message).toBe('Verify Error: Invalid JWT');
|
|
@@ -324,6 +337,7 @@ describe('KB JWT', () => {
|
|
|
324
337
|
const verified = await decoded.verifyKB({
|
|
325
338
|
verifier: testVerifier,
|
|
326
339
|
payload,
|
|
340
|
+
nonce: 'nonce',
|
|
327
341
|
});
|
|
328
342
|
expect(verified).toStrictEqual({
|
|
329
343
|
header: {
|