@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.
@@ -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: "Content-Type",
43
+ ContentType: "content-type",
43
44
  /**
44
45
  * Content Length.
45
46
  */
46
- ContentLength: "Content-Length",
47
+ ContentLength: "content-length",
47
48
  /**
48
49
  * Content Disposition.
49
50
  */
50
- ContentDisposition: "Content-Disposition",
51
+ ContentDisposition: "content-disposition",
51
52
  /**
52
53
  * Accept.
53
54
  */
54
- Accept: "Accept",
55
+ Accept: "accept",
55
56
  /**
56
57
  * Authorization.
57
58
  */
58
- Authorization: "Authorization",
59
+ Authorization: "authorization",
59
60
  /**
60
61
  * Cookie.
61
62
  */
62
- Cookie: "Cookie",
63
+ Cookie: "cookie",
63
64
  /**
64
65
  * Set Cookie.
65
66
  */
66
- SetCookie: "Set-Cookie",
67
+ SetCookie: "set-cookie",
67
68
  /**
68
69
  * Location
69
70
  */
70
- Location: "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 encode and decode JSON Web Tokens.
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.arrayOneOf(Jwt._CLASS_NAME, "header.alg", header.alg, Object.values(JwtAlgorithms));
745
+ core.Guards.stringValue(Jwt._CLASS_NAME, "header.alg", header.alg);
731
746
  core.Guards.object(Jwt._CLASS_NAME, "payload", payload);
732
- core.Guards.uint8Array(Jwt._CLASS_NAME, "key", key);
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.arrayOneOf(Jwt._CLASS_NAME, "header.alg", header.alg, Object.values(JwtAlgorithms));
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.uint8Array(Jwt._CLASS_NAME, "key", key);
792
- const decoded = await Jwt.decode(token);
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
- core.Guards.stringValue(Jwt._CLASS_NAME, "token", token);
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 header The header to verify.
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(header, payload, signature, key, verifier) {
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
- let verified = false;
832
- if (core.Is.object(header) &&
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 alg The algorithm to use.
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(alg, key, payload) {
855
- core.Guards.uint8Array(Jwt._CLASS_NAME, "key", key);
856
- core.Guards.uint8Array(Jwt._CLASS_NAME, "payload", payload);
857
- if (alg === "HS256") {
858
- const algo = new crypto.HmacSha256(key);
859
- return algo.update(payload).digest();
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 crypto.Ed25519.sign(key, payload);
855
+ return signer.sign(finalKey);
862
856
  }
863
857
  /**
864
858
  * The default verifier for the JWT.
865
- * @param alg The algorithm to use.
859
+ * @param token The token to verify.
866
860
  * @param key The key to verify with.
867
- * @param payload The payload to verify.
868
- * @param signature The signature to verify.
869
- * @returns True if the signature was verified.
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 async defaultVerifier(alg, key, payload, signature) {
872
- core.Guards.uint8Array(Jwt._CLASS_NAME, "key", key);
873
- core.Guards.uint8Array(Jwt._CLASS_NAME, "payload", payload);
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
- if (alg === "HS256") {
876
- const algo = new crypto.HmacSha256(key);
877
- const sigBytes = algo.update(payload).digest();
878
- return core.ArrayHelper.matches(sigBytes, signature);
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
- return crypto.Ed25519.verify(key, payload, signature);
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 (alg, k, p) => Jwt.defaultSigner(alg, k, p);
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
- const segments = [];
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]: "pfd",
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;