@twin.org/web 0.0.1-next.4 → 0.0.1-next.40
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 +151 -96
- package/dist/esm/index.mjs +153 -98
- package/dist/types/index.d.ts +2 -1
- package/dist/types/models/IJwk.d.ts +2 -58
- package/dist/types/models/IJwtHeader.d.ts +2 -18
- package/dist/types/models/IJwtPayload.d.ts +2 -33
- package/dist/types/models/headerTypes.d.ts +8 -8
- package/dist/types/models/jwkCryptoKey.d.ts +4 -0
- package/dist/types/models/mimeTypes.d.ts +4 -0
- package/dist/types/utils/jwk.d.ts +13 -0
- package/dist/types/utils/jwt.d.ts +67 -29
- package/docs/changelog.md +1 -1
- package/docs/reference/classes/FetchError.md +13 -5
- package/docs/reference/classes/FetchHelper.md +51 -17
- package/docs/reference/classes/Jwk.md +35 -0
- package/docs/reference/classes/Jwt.md +222 -102
- package/docs/reference/classes/MimeTypeHelper.md +6 -2
- package/docs/reference/index.md +2 -2
- package/docs/reference/interfaces/IHttpHeaders.md +1 -1
- package/docs/reference/interfaces/IJwk.md +2 -106
- package/docs/reference/interfaces/IJwtHeader.md +4 -24
- package/docs/reference/interfaces/IJwtPayload.md +4 -56
- package/docs/reference/type-aliases/JwkCryptoKey.md +5 -0
- package/docs/reference/variables/HeaderTypes.md +8 -8
- package/docs/reference/variables/MimeTypes.md +6 -0
- package/locales/en.json +7 -1
- package/package.json +7 -6
- package/dist/types/models/jwtAlgorithms.d.ts +0 -17
- package/docs/reference/type-aliases/JwtAlgorithms.md +0 -5
- package/docs/reference/variables/JwtAlgorithms.md +0 -19
package/dist/cjs/index.cjs
CHANGED
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
'use strict';
|
|
2
2
|
|
|
3
3
|
var core = require('@twin.org/core');
|
|
4
|
+
var jose = require('jose');
|
|
4
5
|
var crypto = require('@twin.org/crypto');
|
|
5
6
|
|
|
6
7
|
// Copyright 2024 IOTA Stiftung.
|
|
@@ -39,35 +40,35 @@ const HeaderTypes = {
|
|
|
39
40
|
/**
|
|
40
41
|
* Content Type.
|
|
41
42
|
*/
|
|
42
|
-
ContentType: "
|
|
43
|
+
ContentType: "content-type",
|
|
43
44
|
/**
|
|
44
45
|
* Content Length.
|
|
45
46
|
*/
|
|
46
|
-
ContentLength: "
|
|
47
|
+
ContentLength: "content-length",
|
|
47
48
|
/**
|
|
48
49
|
* Content Disposition.
|
|
49
50
|
*/
|
|
50
|
-
ContentDisposition: "
|
|
51
|
+
ContentDisposition: "content-disposition",
|
|
51
52
|
/**
|
|
52
53
|
* Accept.
|
|
53
54
|
*/
|
|
54
|
-
Accept: "
|
|
55
|
+
Accept: "accept",
|
|
55
56
|
/**
|
|
56
57
|
* Authorization.
|
|
57
58
|
*/
|
|
58
|
-
Authorization: "
|
|
59
|
+
Authorization: "authorization",
|
|
59
60
|
/**
|
|
60
61
|
* Cookie.
|
|
61
62
|
*/
|
|
62
|
-
Cookie: "
|
|
63
|
+
Cookie: "cookie",
|
|
63
64
|
/**
|
|
64
65
|
* Set Cookie.
|
|
65
66
|
*/
|
|
66
|
-
SetCookie: "
|
|
67
|
+
SetCookie: "set-cookie",
|
|
67
68
|
/**
|
|
68
69
|
* Location
|
|
69
70
|
*/
|
|
70
|
-
Location: "
|
|
71
|
+
Location: "location"
|
|
71
72
|
};
|
|
72
73
|
|
|
73
74
|
// Copyright 2024 IOTA Stiftung.
|
|
@@ -345,23 +346,6 @@ const HttpStatusCode = {
|
|
|
345
346
|
networkAuthenticationRequired: 511
|
|
346
347
|
};
|
|
347
348
|
|
|
348
|
-
// Copyright 2024 IOTA Stiftung.
|
|
349
|
-
// SPDX-License-Identifier: Apache-2.0.
|
|
350
|
-
/**
|
|
351
|
-
* The cryptographic algorithms supported for JSON Web Tokens and JSON Web Keys.
|
|
352
|
-
*/
|
|
353
|
-
// eslint-disable-next-line @typescript-eslint/naming-convention
|
|
354
|
-
const JwtAlgorithms = {
|
|
355
|
-
/**
|
|
356
|
-
* HMAC using SHA-256.
|
|
357
|
-
*/
|
|
358
|
-
HS256: "HS256",
|
|
359
|
-
/**
|
|
360
|
-
* EdDSA using Ed25519.
|
|
361
|
-
*/
|
|
362
|
-
EdDSA: "EdDSA"
|
|
363
|
-
};
|
|
364
|
-
|
|
365
349
|
// Copyright 2024 IOTA Stiftung.
|
|
366
350
|
// SPDX-License-Identifier: Apache-2.0.
|
|
367
351
|
/**
|
|
@@ -389,6 +373,10 @@ const MimeTypes = {
|
|
|
389
373
|
* JSON-LD - application/ld+json
|
|
390
374
|
*/
|
|
391
375
|
JsonLd: "application/ld+json",
|
|
376
|
+
/**
|
|
377
|
+
* JWT - application/jwt
|
|
378
|
+
*/
|
|
379
|
+
Jwt: "application/jwt",
|
|
392
380
|
/**
|
|
393
381
|
* XML - application/xml
|
|
394
382
|
*/
|
|
@@ -710,7 +698,34 @@ class FetchHelper {
|
|
|
710
698
|
// Copyright 2024 IOTA Stiftung.
|
|
711
699
|
// SPDX-License-Identifier: Apache-2.0.
|
|
712
700
|
/**
|
|
713
|
-
* Class to
|
|
701
|
+
* Class to handle JSON Web Keys.
|
|
702
|
+
*/
|
|
703
|
+
class Jwk {
|
|
704
|
+
/**
|
|
705
|
+
* Runtime name for the class.
|
|
706
|
+
* @internal
|
|
707
|
+
*/
|
|
708
|
+
static _CLASS_NAME = "Jwk";
|
|
709
|
+
/**
|
|
710
|
+
* Convert the JWK to a crypto key.
|
|
711
|
+
* @param jwk The JWK to convert.
|
|
712
|
+
* @returns The crypto key.
|
|
713
|
+
*/
|
|
714
|
+
static async toCryptoKey(jwk) {
|
|
715
|
+
core.Guards.object(Jwk._CLASS_NAME, "jwk", jwk);
|
|
716
|
+
try {
|
|
717
|
+
return jose.importJWK(jwk);
|
|
718
|
+
}
|
|
719
|
+
catch (err) {
|
|
720
|
+
throw new core.GeneralError(Jwk._CLASS_NAME, "jwkImportFailed", undefined, err);
|
|
721
|
+
}
|
|
722
|
+
}
|
|
723
|
+
}
|
|
724
|
+
|
|
725
|
+
// Copyright 2024 IOTA Stiftung.
|
|
726
|
+
// SPDX-License-Identifier: Apache-2.0.
|
|
727
|
+
/**
|
|
728
|
+
* Class to handle JSON Web Tokens.
|
|
714
729
|
*/
|
|
715
730
|
class Jwt {
|
|
716
731
|
/**
|
|
@@ -727,9 +742,9 @@ class Jwt {
|
|
|
727
742
|
*/
|
|
728
743
|
static async encode(header, payload, key) {
|
|
729
744
|
core.Guards.object(Jwt._CLASS_NAME, "header", header);
|
|
730
|
-
core.Guards.
|
|
745
|
+
core.Guards.stringValue(Jwt._CLASS_NAME, "header.alg", header.alg);
|
|
731
746
|
core.Guards.object(Jwt._CLASS_NAME, "payload", payload);
|
|
732
|
-
core.Guards.
|
|
747
|
+
core.Guards.defined(Jwt._CLASS_NAME, "key", key);
|
|
733
748
|
return Jwt.internalEncode(header, payload, key);
|
|
734
749
|
}
|
|
735
750
|
/**
|
|
@@ -741,7 +756,7 @@ class Jwt {
|
|
|
741
756
|
*/
|
|
742
757
|
static async encodeWithSigner(header, payload, signer) {
|
|
743
758
|
core.Guards.object(Jwt._CLASS_NAME, "header", header);
|
|
744
|
-
core.Guards.
|
|
759
|
+
core.Guards.stringValue(Jwt._CLASS_NAME, "header.alg", header.alg);
|
|
745
760
|
core.Guards.object(Jwt._CLASS_NAME, "payload", payload);
|
|
746
761
|
core.Guards.function(Jwt._CLASS_NAME, "signer", signer);
|
|
747
762
|
return Jwt.internalEncode(header, payload, undefined, signer);
|
|
@@ -788,13 +803,8 @@ class Jwt {
|
|
|
788
803
|
*/
|
|
789
804
|
static async verify(token, key) {
|
|
790
805
|
core.Guards.stringValue(Jwt._CLASS_NAME, "token", token);
|
|
791
|
-
core.Guards.
|
|
792
|
-
|
|
793
|
-
const verified = await Jwt.verifySignature(decoded.header, decoded.payload, decoded.signature, key);
|
|
794
|
-
return {
|
|
795
|
-
verified,
|
|
796
|
-
...decoded
|
|
797
|
-
};
|
|
806
|
+
core.Guards.defined(Jwt._CLASS_NAME, "key", key);
|
|
807
|
+
return Jwt.verifySignature(token, key);
|
|
798
808
|
}
|
|
799
809
|
/**
|
|
800
810
|
* Verify a token.
|
|
@@ -805,79 +815,131 @@ class Jwt {
|
|
|
805
815
|
static async verifyWithVerifier(token, verifier) {
|
|
806
816
|
core.Guards.stringValue(Jwt._CLASS_NAME, "token", token);
|
|
807
817
|
core.Guards.function(Jwt._CLASS_NAME, "verifier", verifier);
|
|
808
|
-
|
|
809
|
-
const decoded = await Jwt.decode(token);
|
|
810
|
-
const verified = await Jwt.verifySignature(decoded.header, decoded.payload, decoded.signature, undefined, verifier);
|
|
811
|
-
return {
|
|
812
|
-
verified,
|
|
813
|
-
...decoded
|
|
814
|
-
};
|
|
818
|
+
return Jwt.verifySignature(token, undefined, verifier);
|
|
815
819
|
}
|
|
816
820
|
/**
|
|
817
821
|
* Verify a token by parts.
|
|
818
|
-
* @param
|
|
819
|
-
* @param payload The payload to verify.
|
|
820
|
-
* @param signature The signature to verify.
|
|
822
|
+
* @param token The token to verify.
|
|
821
823
|
* @param key The key for verifying the token, if not provided no verification occurs.
|
|
822
824
|
* @param verifier Custom verification method.
|
|
823
825
|
* @returns True if the parts are verified.
|
|
824
826
|
*/
|
|
825
|
-
static async verifySignature(
|
|
827
|
+
static async verifySignature(token, key, verifier) {
|
|
828
|
+
core.Guards.stringValue(Jwt._CLASS_NAME, "token", token);
|
|
826
829
|
const hasKey = core.Is.notEmpty(key);
|
|
827
830
|
const hasVerifier = core.Is.notEmpty(verifier);
|
|
828
831
|
if (!hasKey && !hasVerifier) {
|
|
829
832
|
throw new core.GeneralError(Jwt._CLASS_NAME, "noKeyOrVerifier");
|
|
830
833
|
}
|
|
831
|
-
|
|
832
|
-
|
|
833
|
-
core.Is.object(payload) &&
|
|
834
|
-
core.Is.uint8Array(signature) &&
|
|
835
|
-
core.Is.arrayOneOf(header.alg, Object.values(JwtAlgorithms))) {
|
|
836
|
-
const segments = [];
|
|
837
|
-
const headerBytes = core.Converter.utf8ToBytes(JSON.stringify(header));
|
|
838
|
-
segments.push(core.Converter.bytesToBase64Url(headerBytes));
|
|
839
|
-
const payloadBytes = core.Converter.utf8ToBytes(JSON.stringify(payload));
|
|
840
|
-
segments.push(core.Converter.bytesToBase64Url(payloadBytes));
|
|
841
|
-
const jwtHeaderAndPayload = core.Converter.utf8ToBytes(segments.join("."));
|
|
842
|
-
verifier ??= async (alg, k, p, s) => Jwt.defaultVerifier(alg, k, p, s);
|
|
843
|
-
verified = await verifier(header.alg, key, jwtHeaderAndPayload, signature);
|
|
844
|
-
}
|
|
845
|
-
return verified;
|
|
834
|
+
verifier ??= async (t, k) => Jwt.defaultVerifier(t, k);
|
|
835
|
+
return verifier(token, key);
|
|
846
836
|
}
|
|
847
837
|
/**
|
|
848
838
|
* The default signer for the JWT.
|
|
849
|
-
* @param
|
|
850
|
-
* @param key The key to sign with.
|
|
839
|
+
* @param header The header to sign.
|
|
851
840
|
* @param payload The payload to sign.
|
|
841
|
+
* @param key The optional key to sign with.
|
|
852
842
|
* @returns The signature.
|
|
853
843
|
*/
|
|
854
|
-
static async defaultSigner(
|
|
855
|
-
core.Guards.
|
|
856
|
-
core.Guards.
|
|
857
|
-
|
|
858
|
-
|
|
859
|
-
|
|
844
|
+
static async defaultSigner(header, payload, key) {
|
|
845
|
+
core.Guards.object(Jwt._CLASS_NAME, "header", header);
|
|
846
|
+
core.Guards.object(Jwt._CLASS_NAME, "payload", payload);
|
|
847
|
+
core.Guards.defined(Jwt._CLASS_NAME, "key", key);
|
|
848
|
+
const signer = new jose.SignJWT(payload);
|
|
849
|
+
signer.setProtectedHeader(header);
|
|
850
|
+
let finalKey = key;
|
|
851
|
+
if (header.alg === "EdDSA" && core.Is.uint8Array(key)) {
|
|
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);
|
|
860
854
|
}
|
|
861
|
-
return
|
|
855
|
+
return signer.sign(finalKey);
|
|
862
856
|
}
|
|
863
857
|
/**
|
|
864
858
|
* The default verifier for the JWT.
|
|
865
|
-
* @param
|
|
859
|
+
* @param token The token to verify.
|
|
866
860
|
* @param key The key to verify with.
|
|
867
|
-
* @
|
|
868
|
-
|
|
869
|
-
|
|
861
|
+
* @returns The header and payload if verification successful.
|
|
862
|
+
*/
|
|
863
|
+
static async defaultVerifier(token, key) {
|
|
864
|
+
core.Guards.stringValue(Jwt._CLASS_NAME, "token", token);
|
|
865
|
+
core.Guards.defined(Jwt._CLASS_NAME, "key", key);
|
|
866
|
+
try {
|
|
867
|
+
const result = await jose.jwtVerify(token, key);
|
|
868
|
+
return {
|
|
869
|
+
header: result.protectedHeader,
|
|
870
|
+
payload: result.payload
|
|
871
|
+
};
|
|
872
|
+
}
|
|
873
|
+
catch (err) {
|
|
874
|
+
throw new core.GeneralError(Jwt._CLASS_NAME, "verifyFailed", undefined, err);
|
|
875
|
+
}
|
|
876
|
+
}
|
|
877
|
+
/**
|
|
878
|
+
* Create bytes for signing from header and payload.
|
|
879
|
+
* @param header The header.
|
|
880
|
+
* @param payload The payload.
|
|
881
|
+
* @returns The bytes to sign.
|
|
870
882
|
*/
|
|
871
|
-
static
|
|
872
|
-
core.Guards.
|
|
873
|
-
core.Guards.
|
|
883
|
+
static toSigningBytes(header, payload) {
|
|
884
|
+
core.Guards.object(Jwt._CLASS_NAME, "header", header);
|
|
885
|
+
core.Guards.object(Jwt._CLASS_NAME, "payload", payload);
|
|
886
|
+
const segments = [];
|
|
887
|
+
const headerBytes = core.Converter.utf8ToBytes(JSON.stringify(header));
|
|
888
|
+
segments.push(core.Converter.bytesToBase64Url(headerBytes));
|
|
889
|
+
const payloadBytes = core.Converter.utf8ToBytes(JSON.stringify(payload));
|
|
890
|
+
segments.push(core.Converter.bytesToBase64Url(payloadBytes));
|
|
891
|
+
return core.Converter.utf8ToBytes(segments.join("."));
|
|
892
|
+
}
|
|
893
|
+
/**
|
|
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.
|
|
915
|
+
* @param signature The signature.
|
|
916
|
+
* @returns The token.
|
|
917
|
+
*/
|
|
918
|
+
static tokenFromBytes(signingBytes, signature) {
|
|
919
|
+
core.Guards.uint8Array(Jwt._CLASS_NAME, "signingBytes", signingBytes);
|
|
874
920
|
core.Guards.uint8Array(Jwt._CLASS_NAME, "signature", signature);
|
|
875
|
-
|
|
876
|
-
|
|
877
|
-
|
|
878
|
-
|
|
921
|
+
const signedBytesUtf8 = core.Converter.bytesToUtf8(signingBytes);
|
|
922
|
+
const signatureBase64 = core.Converter.bytesToBase64Url(signature);
|
|
923
|
+
return `${signedBytesUtf8}.${signatureBase64}`;
|
|
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");
|
|
879
936
|
}
|
|
880
|
-
|
|
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
|
+
};
|
|
881
943
|
}
|
|
882
944
|
/**
|
|
883
945
|
* Encode a token.
|
|
@@ -894,19 +956,11 @@ class Jwt {
|
|
|
894
956
|
if (!hasKey && !hasSigner) {
|
|
895
957
|
throw new core.GeneralError(Jwt._CLASS_NAME, "noKeyOrSigner");
|
|
896
958
|
}
|
|
897
|
-
signer ??= async (
|
|
959
|
+
signer ??= async (h, p, k) => Jwt.defaultSigner(h, p, k);
|
|
898
960
|
if (core.Is.undefined(header.typ)) {
|
|
899
961
|
header.typ = "JWT";
|
|
900
962
|
}
|
|
901
|
-
|
|
902
|
-
const headerBytes = core.Converter.utf8ToBytes(JSON.stringify(header));
|
|
903
|
-
segments.push(core.Converter.bytesToBase64Url(headerBytes));
|
|
904
|
-
const payloadBytes = core.Converter.utf8ToBytes(JSON.stringify(payload));
|
|
905
|
-
segments.push(core.Converter.bytesToBase64Url(payloadBytes));
|
|
906
|
-
const jwtHeaderAndPayload = core.Converter.utf8ToBytes(segments.join("."));
|
|
907
|
-
const sigBytes = await signer(header.alg, key, jwtHeaderAndPayload);
|
|
908
|
-
segments.push(core.Converter.bytesToBase64Url(sigBytes));
|
|
909
|
-
return segments.join(".");
|
|
963
|
+
return signer(header, payload, key);
|
|
910
964
|
}
|
|
911
965
|
}
|
|
912
966
|
|
|
@@ -922,7 +976,7 @@ class MimeTypeHelper {
|
|
|
922
976
|
* @returns The mime type if detected.
|
|
923
977
|
*/
|
|
924
978
|
static async detect(data) {
|
|
925
|
-
if (!core.Is.uint8Array(data)) {
|
|
979
|
+
if (!core.Is.uint8Array(data) || data.length === 0) {
|
|
926
980
|
return undefined;
|
|
927
981
|
}
|
|
928
982
|
// Image
|
|
@@ -994,12 +1048,13 @@ class MimeTypeHelper {
|
|
|
994
1048
|
[MimeTypes.Javascript]: "js",
|
|
995
1049
|
[MimeTypes.Json]: "json",
|
|
996
1050
|
[MimeTypes.JsonLd]: "jsonld",
|
|
1051
|
+
[MimeTypes.Jwt]: "jwt",
|
|
997
1052
|
[MimeTypes.Xml]: "xml",
|
|
998
1053
|
[MimeTypes.OctetStream]: "bin",
|
|
999
1054
|
[MimeTypes.Gzip]: "gzip",
|
|
1000
1055
|
[MimeTypes.Bzip2]: "bz2",
|
|
1001
1056
|
[MimeTypes.Zip]: "zip",
|
|
1002
|
-
[MimeTypes.Pdf]: "
|
|
1057
|
+
[MimeTypes.Pdf]: "pdf",
|
|
1003
1058
|
[MimeTypes.Gif]: "gif",
|
|
1004
1059
|
[MimeTypes.Bmp]: "bmp",
|
|
1005
1060
|
[MimeTypes.Jpeg]: "jpeg",
|
|
@@ -1050,7 +1105,7 @@ exports.FetchHelper = FetchHelper;
|
|
|
1050
1105
|
exports.HeaderTypes = HeaderTypes;
|
|
1051
1106
|
exports.HttpMethod = HttpMethod;
|
|
1052
1107
|
exports.HttpStatusCode = HttpStatusCode;
|
|
1108
|
+
exports.Jwk = Jwk;
|
|
1053
1109
|
exports.Jwt = Jwt;
|
|
1054
|
-
exports.JwtAlgorithms = JwtAlgorithms;
|
|
1055
1110
|
exports.MimeTypeHelper = MimeTypeHelper;
|
|
1056
1111
|
exports.MimeTypes = MimeTypes;
|