@twin.org/web 0.0.1-next.38 → 0.0.1-next.39
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/dist/cjs/index.cjs +52 -18
- package/dist/esm/index.mjs +53 -19
- package/dist/types/utils/jwt.d.ts +24 -4
- package/docs/changelog.md +1 -1
- package/docs/reference/classes/Jwt.md +80 -6
- package/locales/en.json +3 -1
- package/package.json +3 -3
package/dist/cjs/index.cjs
CHANGED
|
@@ -2,6 +2,7 @@
|
|
|
2
2
|
|
|
3
3
|
var core = require('@twin.org/core');
|
|
4
4
|
var jose = require('jose');
|
|
5
|
+
var crypto = require('@twin.org/crypto');
|
|
5
6
|
|
|
6
7
|
// Copyright 2024 IOTA Stiftung.
|
|
7
8
|
// SPDX-License-Identifier: Apache-2.0.
|
|
@@ -846,21 +847,12 @@ class Jwt {
|
|
|
846
847
|
core.Guards.defined(Jwt._CLASS_NAME, "key", key);
|
|
847
848
|
const signer = new jose.SignJWT(payload);
|
|
848
849
|
signer.setProtectedHeader(header);
|
|
850
|
+
let finalKey = key;
|
|
849
851
|
if (header.alg === "EdDSA" && core.Is.uint8Array(key)) {
|
|
850
|
-
//
|
|
851
|
-
|
|
852
|
-
// The PKCS8 format is the raw key prefixed with the ASN.1 sequence for an Ed25519 private key.
|
|
853
|
-
// The ASN.1 sequence is 48 46 02 01 00 30 05 06 03 2b 65 70 04 20 04 20
|
|
854
|
-
const pkcs8Prefix = new Uint8Array([
|
|
855
|
-
48, 46, 2, 1, 0, 48, 5, 6, 3, 43, 101, 112, 4, 34, 4, 32
|
|
856
|
-
]); // 0x302e020100300506032b657004220420
|
|
857
|
-
const pkcs8PrivateKey = core.Uint8ArrayHelper.concat([pkcs8Prefix, key]);
|
|
858
|
-
const imported = await crypto.subtle.importKey("pkcs8", pkcs8PrivateKey, "Ed25519", false, [
|
|
859
|
-
"sign"
|
|
860
|
-
]);
|
|
861
|
-
return signer.sign(imported);
|
|
852
|
+
// Jose does not support Ed25519 keys in raw format, so we need to convert it to PKCS8.
|
|
853
|
+
finalKey = await crypto.Ed25519.privateKeyToPKCS8(key);
|
|
862
854
|
}
|
|
863
|
-
return signer.sign(
|
|
855
|
+
return signer.sign(finalKey);
|
|
864
856
|
}
|
|
865
857
|
/**
|
|
866
858
|
* The default verifier for the JWT.
|
|
@@ -888,7 +880,9 @@ class Jwt {
|
|
|
888
880
|
* @param payload The payload.
|
|
889
881
|
* @returns The bytes to sign.
|
|
890
882
|
*/
|
|
891
|
-
static
|
|
883
|
+
static toSigningBytes(header, payload) {
|
|
884
|
+
core.Guards.object(Jwt._CLASS_NAME, "header", header);
|
|
885
|
+
core.Guards.object(Jwt._CLASS_NAME, "payload", payload);
|
|
892
886
|
const segments = [];
|
|
893
887
|
const headerBytes = core.Converter.utf8ToBytes(JSON.stringify(header));
|
|
894
888
|
segments.push(core.Converter.bytesToBase64Url(headerBytes));
|
|
@@ -897,16 +891,56 @@ class Jwt {
|
|
|
897
891
|
return core.Converter.utf8ToBytes(segments.join("."));
|
|
898
892
|
}
|
|
899
893
|
/**
|
|
900
|
-
* Create
|
|
901
|
-
* @param
|
|
894
|
+
* Create header and payload from signing bytes.
|
|
895
|
+
* @param signingBytes The signing bytes from a token.
|
|
896
|
+
* @returns The header and payload.
|
|
897
|
+
* @throws If the signing bytes are invalid
|
|
898
|
+
*/
|
|
899
|
+
static fromSigningBytes(signingBytes) {
|
|
900
|
+
core.Guards.uint8Array(Jwt._CLASS_NAME, "signingBytes", signingBytes);
|
|
901
|
+
const segments = core.Converter.bytesToUtf8(signingBytes).split(".");
|
|
902
|
+
if (segments.length !== 2) {
|
|
903
|
+
throw new core.GeneralError(Jwt._CLASS_NAME, "invalidSigningBytes");
|
|
904
|
+
}
|
|
905
|
+
const headerBytes = core.Converter.base64UrlToBytes(segments[0]);
|
|
906
|
+
const payloadBytes = core.Converter.base64UrlToBytes(segments[1]);
|
|
907
|
+
return {
|
|
908
|
+
header: core.ObjectHelper.fromBytes(headerBytes),
|
|
909
|
+
payload: core.ObjectHelper.fromBytes(payloadBytes)
|
|
910
|
+
};
|
|
911
|
+
}
|
|
912
|
+
/**
|
|
913
|
+
* Convert signed bytes and signature bytes to token.
|
|
914
|
+
* @param signingBytes The signed bytes.
|
|
902
915
|
* @param signature The signature.
|
|
903
916
|
* @returns The token.
|
|
904
917
|
*/
|
|
905
|
-
static
|
|
906
|
-
|
|
918
|
+
static tokenFromBytes(signingBytes, signature) {
|
|
919
|
+
core.Guards.uint8Array(Jwt._CLASS_NAME, "signingBytes", signingBytes);
|
|
920
|
+
core.Guards.uint8Array(Jwt._CLASS_NAME, "signature", signature);
|
|
921
|
+
const signedBytesUtf8 = core.Converter.bytesToUtf8(signingBytes);
|
|
907
922
|
const signatureBase64 = core.Converter.bytesToBase64Url(signature);
|
|
908
923
|
return `${signedBytesUtf8}.${signatureBase64}`;
|
|
909
924
|
}
|
|
925
|
+
/**
|
|
926
|
+
* Convert the token to signing bytes and signature bytes.
|
|
927
|
+
* @param token The token to convert to bytes.
|
|
928
|
+
* @returns The decoded bytes.
|
|
929
|
+
* @throws If the token is invalid.
|
|
930
|
+
*/
|
|
931
|
+
static tokenToBytes(token) {
|
|
932
|
+
core.Guards.stringValue(Jwt._CLASS_NAME, "token", token);
|
|
933
|
+
const segments = token.split(".");
|
|
934
|
+
if (segments.length !== 3) {
|
|
935
|
+
throw new core.GeneralError(Jwt._CLASS_NAME, "invalidTokenParts");
|
|
936
|
+
}
|
|
937
|
+
const signingBytes = core.Converter.utf8ToBytes(`${segments[0]}.${segments[1]}`);
|
|
938
|
+
const signature = core.Converter.base64UrlToBytes(segments[2]);
|
|
939
|
+
return {
|
|
940
|
+
signingBytes,
|
|
941
|
+
signature
|
|
942
|
+
};
|
|
943
|
+
}
|
|
910
944
|
/**
|
|
911
945
|
* Encode a token.
|
|
912
946
|
* @param header The header to encode.
|
package/dist/esm/index.mjs
CHANGED
|
@@ -1,5 +1,6 @@
|
|
|
1
|
-
import { BaseError, StringHelper, Guards, Is, AsyncCache, ObjectHelper, GeneralError, Converter
|
|
1
|
+
import { BaseError, StringHelper, Guards, Is, AsyncCache, ObjectHelper, GeneralError, Converter } from '@twin.org/core';
|
|
2
2
|
import { importJWK, SignJWT, jwtVerify } from 'jose';
|
|
3
|
+
import { Ed25519 } from '@twin.org/crypto';
|
|
3
4
|
|
|
4
5
|
// Copyright 2024 IOTA Stiftung.
|
|
5
6
|
// SPDX-License-Identifier: Apache-2.0.
|
|
@@ -844,21 +845,12 @@ class Jwt {
|
|
|
844
845
|
Guards.defined(Jwt._CLASS_NAME, "key", key);
|
|
845
846
|
const signer = new SignJWT(payload);
|
|
846
847
|
signer.setProtectedHeader(header);
|
|
848
|
+
let finalKey = key;
|
|
847
849
|
if (header.alg === "EdDSA" && Is.uint8Array(key)) {
|
|
848
|
-
//
|
|
849
|
-
|
|
850
|
-
// The PKCS8 format is the raw key prefixed with the ASN.1 sequence for an Ed25519 private key.
|
|
851
|
-
// The ASN.1 sequence is 48 46 02 01 00 30 05 06 03 2b 65 70 04 20 04 20
|
|
852
|
-
const pkcs8Prefix = new Uint8Array([
|
|
853
|
-
48, 46, 2, 1, 0, 48, 5, 6, 3, 43, 101, 112, 4, 34, 4, 32
|
|
854
|
-
]); // 0x302e020100300506032b657004220420
|
|
855
|
-
const pkcs8PrivateKey = Uint8ArrayHelper.concat([pkcs8Prefix, key]);
|
|
856
|
-
const imported = await crypto.subtle.importKey("pkcs8", pkcs8PrivateKey, "Ed25519", false, [
|
|
857
|
-
"sign"
|
|
858
|
-
]);
|
|
859
|
-
return signer.sign(imported);
|
|
850
|
+
// Jose does not support Ed25519 keys in raw format, so we need to convert it to PKCS8.
|
|
851
|
+
finalKey = await Ed25519.privateKeyToPKCS8(key);
|
|
860
852
|
}
|
|
861
|
-
return signer.sign(
|
|
853
|
+
return signer.sign(finalKey);
|
|
862
854
|
}
|
|
863
855
|
/**
|
|
864
856
|
* The default verifier for the JWT.
|
|
@@ -886,7 +878,9 @@ class Jwt {
|
|
|
886
878
|
* @param payload The payload.
|
|
887
879
|
* @returns The bytes to sign.
|
|
888
880
|
*/
|
|
889
|
-
static
|
|
881
|
+
static toSigningBytes(header, payload) {
|
|
882
|
+
Guards.object(Jwt._CLASS_NAME, "header", header);
|
|
883
|
+
Guards.object(Jwt._CLASS_NAME, "payload", payload);
|
|
890
884
|
const segments = [];
|
|
891
885
|
const headerBytes = Converter.utf8ToBytes(JSON.stringify(header));
|
|
892
886
|
segments.push(Converter.bytesToBase64Url(headerBytes));
|
|
@@ -895,16 +889,56 @@ class Jwt {
|
|
|
895
889
|
return Converter.utf8ToBytes(segments.join("."));
|
|
896
890
|
}
|
|
897
891
|
/**
|
|
898
|
-
* Create
|
|
899
|
-
* @param
|
|
892
|
+
* Create header and payload from signing bytes.
|
|
893
|
+
* @param signingBytes The signing bytes from a token.
|
|
894
|
+
* @returns The header and payload.
|
|
895
|
+
* @throws If the signing bytes are invalid
|
|
896
|
+
*/
|
|
897
|
+
static fromSigningBytes(signingBytes) {
|
|
898
|
+
Guards.uint8Array(Jwt._CLASS_NAME, "signingBytes", signingBytes);
|
|
899
|
+
const segments = Converter.bytesToUtf8(signingBytes).split(".");
|
|
900
|
+
if (segments.length !== 2) {
|
|
901
|
+
throw new GeneralError(Jwt._CLASS_NAME, "invalidSigningBytes");
|
|
902
|
+
}
|
|
903
|
+
const headerBytes = Converter.base64UrlToBytes(segments[0]);
|
|
904
|
+
const payloadBytes = Converter.base64UrlToBytes(segments[1]);
|
|
905
|
+
return {
|
|
906
|
+
header: ObjectHelper.fromBytes(headerBytes),
|
|
907
|
+
payload: ObjectHelper.fromBytes(payloadBytes)
|
|
908
|
+
};
|
|
909
|
+
}
|
|
910
|
+
/**
|
|
911
|
+
* Convert signed bytes and signature bytes to token.
|
|
912
|
+
* @param signingBytes The signed bytes.
|
|
900
913
|
* @param signature The signature.
|
|
901
914
|
* @returns The token.
|
|
902
915
|
*/
|
|
903
|
-
static
|
|
904
|
-
|
|
916
|
+
static tokenFromBytes(signingBytes, signature) {
|
|
917
|
+
Guards.uint8Array(Jwt._CLASS_NAME, "signingBytes", signingBytes);
|
|
918
|
+
Guards.uint8Array(Jwt._CLASS_NAME, "signature", signature);
|
|
919
|
+
const signedBytesUtf8 = Converter.bytesToUtf8(signingBytes);
|
|
905
920
|
const signatureBase64 = Converter.bytesToBase64Url(signature);
|
|
906
921
|
return `${signedBytesUtf8}.${signatureBase64}`;
|
|
907
922
|
}
|
|
923
|
+
/**
|
|
924
|
+
* Convert the token to signing bytes and signature bytes.
|
|
925
|
+
* @param token The token to convert to bytes.
|
|
926
|
+
* @returns The decoded bytes.
|
|
927
|
+
* @throws If the token is invalid.
|
|
928
|
+
*/
|
|
929
|
+
static tokenToBytes(token) {
|
|
930
|
+
Guards.stringValue(Jwt._CLASS_NAME, "token", token);
|
|
931
|
+
const segments = token.split(".");
|
|
932
|
+
if (segments.length !== 3) {
|
|
933
|
+
throw new GeneralError(Jwt._CLASS_NAME, "invalidTokenParts");
|
|
934
|
+
}
|
|
935
|
+
const signingBytes = Converter.utf8ToBytes(`${segments[0]}.${segments[1]}`);
|
|
936
|
+
const signature = Converter.base64UrlToBytes(segments[2]);
|
|
937
|
+
return {
|
|
938
|
+
signingBytes,
|
|
939
|
+
signature
|
|
940
|
+
};
|
|
941
|
+
}
|
|
908
942
|
/**
|
|
909
943
|
* Encode a token.
|
|
910
944
|
* @param header The header to encode.
|
|
@@ -92,12 +92,32 @@ export declare class Jwt {
|
|
|
92
92
|
* @param payload The payload.
|
|
93
93
|
* @returns The bytes to sign.
|
|
94
94
|
*/
|
|
95
|
-
static
|
|
95
|
+
static toSigningBytes<T extends IJwtHeader, U extends IJwtPayload>(header: T, payload: U): Uint8Array;
|
|
96
96
|
/**
|
|
97
|
-
* Create
|
|
98
|
-
* @param
|
|
97
|
+
* Create header and payload from signing bytes.
|
|
98
|
+
* @param signingBytes The signing bytes from a token.
|
|
99
|
+
* @returns The header and payload.
|
|
100
|
+
* @throws If the signing bytes are invalid
|
|
101
|
+
*/
|
|
102
|
+
static fromSigningBytes<T extends IJwtHeader, U extends IJwtPayload>(signingBytes: Uint8Array): {
|
|
103
|
+
header: T;
|
|
104
|
+
payload: U;
|
|
105
|
+
};
|
|
106
|
+
/**
|
|
107
|
+
* Convert signed bytes and signature bytes to token.
|
|
108
|
+
* @param signingBytes The signed bytes.
|
|
99
109
|
* @param signature The signature.
|
|
100
110
|
* @returns The token.
|
|
101
111
|
*/
|
|
102
|
-
static
|
|
112
|
+
static tokenFromBytes(signingBytes: Uint8Array, signature: Uint8Array): string;
|
|
113
|
+
/**
|
|
114
|
+
* Convert the token to signing bytes and signature bytes.
|
|
115
|
+
* @param token The token to convert to bytes.
|
|
116
|
+
* @returns The decoded bytes.
|
|
117
|
+
* @throws If the token is invalid.
|
|
118
|
+
*/
|
|
119
|
+
static tokenToBytes(token: string): {
|
|
120
|
+
signingBytes: Uint8Array;
|
|
121
|
+
signature: Uint8Array;
|
|
122
|
+
};
|
|
103
123
|
}
|
package/docs/changelog.md
CHANGED
|
@@ -298,9 +298,9 @@ True if the signature was verified.
|
|
|
298
298
|
|
|
299
299
|
***
|
|
300
300
|
|
|
301
|
-
###
|
|
301
|
+
### toSigningBytes()
|
|
302
302
|
|
|
303
|
-
> `static` **
|
|
303
|
+
> `static` **toSigningBytes**\<`T`, `U`\>(`header`, `payload`): `Uint8Array`
|
|
304
304
|
|
|
305
305
|
Create bytes for signing from header and payload.
|
|
306
306
|
|
|
@@ -332,15 +332,55 @@ The bytes to sign.
|
|
|
332
332
|
|
|
333
333
|
***
|
|
334
334
|
|
|
335
|
-
###
|
|
335
|
+
### fromSigningBytes()
|
|
336
336
|
|
|
337
|
-
> `static` **
|
|
337
|
+
> `static` **fromSigningBytes**\<`T`, `U`\>(`signingBytes`): `object`
|
|
338
338
|
|
|
339
|
-
Create
|
|
339
|
+
Create header and payload from signing bytes.
|
|
340
|
+
|
|
341
|
+
#### Type Parameters
|
|
342
|
+
|
|
343
|
+
• **T** *extends* [`IJwtHeader`](../interfaces/IJwtHeader.md)
|
|
344
|
+
|
|
345
|
+
• **U** *extends* [`IJwtPayload`](../interfaces/IJwtPayload.md)
|
|
346
|
+
|
|
347
|
+
#### Parameters
|
|
348
|
+
|
|
349
|
+
##### signingBytes
|
|
350
|
+
|
|
351
|
+
`Uint8Array`
|
|
352
|
+
|
|
353
|
+
The signing bytes from a token.
|
|
354
|
+
|
|
355
|
+
#### Returns
|
|
356
|
+
|
|
357
|
+
`object`
|
|
358
|
+
|
|
359
|
+
The header and payload.
|
|
360
|
+
|
|
361
|
+
##### header
|
|
362
|
+
|
|
363
|
+
> **header**: `T`
|
|
364
|
+
|
|
365
|
+
##### payload
|
|
366
|
+
|
|
367
|
+
> **payload**: `U`
|
|
368
|
+
|
|
369
|
+
#### Throws
|
|
370
|
+
|
|
371
|
+
If the signing bytes are invalid
|
|
372
|
+
|
|
373
|
+
***
|
|
374
|
+
|
|
375
|
+
### tokenFromBytes()
|
|
376
|
+
|
|
377
|
+
> `static` **tokenFromBytes**(`signingBytes`, `signature`): `string`
|
|
378
|
+
|
|
379
|
+
Convert signed bytes and signature bytes to token.
|
|
340
380
|
|
|
341
381
|
#### Parameters
|
|
342
382
|
|
|
343
|
-
#####
|
|
383
|
+
##### signingBytes
|
|
344
384
|
|
|
345
385
|
`Uint8Array`
|
|
346
386
|
|
|
@@ -357,3 +397,37 @@ The signature.
|
|
|
357
397
|
`string`
|
|
358
398
|
|
|
359
399
|
The token.
|
|
400
|
+
|
|
401
|
+
***
|
|
402
|
+
|
|
403
|
+
### tokenToBytes()
|
|
404
|
+
|
|
405
|
+
> `static` **tokenToBytes**(`token`): `object`
|
|
406
|
+
|
|
407
|
+
Convert the token to signing bytes and signature bytes.
|
|
408
|
+
|
|
409
|
+
#### Parameters
|
|
410
|
+
|
|
411
|
+
##### token
|
|
412
|
+
|
|
413
|
+
`string`
|
|
414
|
+
|
|
415
|
+
The token to convert to bytes.
|
|
416
|
+
|
|
417
|
+
#### Returns
|
|
418
|
+
|
|
419
|
+
`object`
|
|
420
|
+
|
|
421
|
+
The decoded bytes.
|
|
422
|
+
|
|
423
|
+
##### signingBytes
|
|
424
|
+
|
|
425
|
+
> **signingBytes**: `Uint8Array`
|
|
426
|
+
|
|
427
|
+
##### signature
|
|
428
|
+
|
|
429
|
+
> **signature**: `Uint8Array`
|
|
430
|
+
|
|
431
|
+
#### Throws
|
|
432
|
+
|
|
433
|
+
If the token is invalid.
|
package/locales/en.json
CHANGED
|
@@ -10,7 +10,9 @@
|
|
|
10
10
|
"jwt": {
|
|
11
11
|
"noKeyOrSigner": "No key or signer was provided for JWT creation",
|
|
12
12
|
"noKeyOrVerifier": "No key or verifier was provided for JWT creation",
|
|
13
|
-
"verifyFailed": "Failed to verify JWT"
|
|
13
|
+
"verifyFailed": "Failed to verify JWT",
|
|
14
|
+
"invalidTokenParts": "The JSON Web Token could not be parsed, it should contain three parts separated by dots",
|
|
15
|
+
"invalidSigningBytes": "The signing bytes are invalid, it should contain two parts separated by a dot"
|
|
14
16
|
},
|
|
15
17
|
"jwk": {
|
|
16
18
|
"jwkImportFailed": "Failed to import JWK"
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@twin.org/web",
|
|
3
|
-
"version": "0.0.1-next.
|
|
3
|
+
"version": "0.0.1-next.39",
|
|
4
4
|
"description": "Contains classes for use with web operations",
|
|
5
5
|
"repository": {
|
|
6
6
|
"type": "git",
|
|
@@ -14,8 +14,8 @@
|
|
|
14
14
|
"node": ">=20.0.0"
|
|
15
15
|
},
|
|
16
16
|
"dependencies": {
|
|
17
|
-
"@twin.org/core": "0.0.1-next.
|
|
18
|
-
"@twin.org/crypto": "0.0.1-next.
|
|
17
|
+
"@twin.org/core": "0.0.1-next.39",
|
|
18
|
+
"@twin.org/crypto": "0.0.1-next.39",
|
|
19
19
|
"@twin.org/nameof": "next",
|
|
20
20
|
"jose": "6.0.8"
|
|
21
21
|
},
|