@twin.org/web 0.0.1-next.9 → 0.0.2-next.10

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.
Files changed (39) hide show
  1. package/dist/cjs/index.cjs +311 -98
  2. package/dist/esm/index.mjs +312 -100
  3. package/dist/types/errors/fetchError.d.ts +2 -2
  4. package/dist/types/index.d.ts +3 -1
  5. package/dist/types/models/IJwk.d.ts +2 -58
  6. package/dist/types/models/IJwtHeader.d.ts +2 -18
  7. package/dist/types/models/IJwtPayload.d.ts +2 -33
  8. package/dist/types/models/jwkCryptoKey.d.ts +4 -0
  9. package/dist/types/models/mimeTypes.d.ts +8 -0
  10. package/dist/types/utils/fetchHelper.d.ts +7 -0
  11. package/dist/types/utils/jwk.d.ts +41 -0
  12. package/dist/types/utils/jws.d.ts +22 -0
  13. package/dist/types/utils/jwt.d.ts +67 -29
  14. package/docs/changelog.md +613 -1
  15. package/docs/reference/classes/FetchError.md +17 -9
  16. package/docs/reference/classes/FetchHelper.md +104 -28
  17. package/docs/reference/classes/Jwk.md +129 -0
  18. package/docs/reference/classes/Jws.md +81 -0
  19. package/docs/reference/classes/Jwt.md +261 -105
  20. package/docs/reference/classes/MimeTypeHelper.md +9 -5
  21. package/docs/reference/index.md +3 -2
  22. package/docs/reference/interfaces/IHttpHeaders.md +1 -1
  23. package/docs/reference/interfaces/IJwk.md +2 -106
  24. package/docs/reference/interfaces/IJwtHeader.md +5 -23
  25. package/docs/reference/interfaces/IJwtPayload.md +5 -55
  26. package/docs/reference/type-aliases/HeaderTypes.md +1 -1
  27. package/docs/reference/type-aliases/HttpMethod.md +1 -1
  28. package/docs/reference/type-aliases/HttpStatusCode.md +1 -1
  29. package/docs/reference/type-aliases/JwkCryptoKey.md +5 -0
  30. package/docs/reference/type-aliases/MimeTypes.md +1 -1
  31. package/docs/reference/variables/HeaderTypes.md +1 -1
  32. package/docs/reference/variables/HttpMethod.md +1 -1
  33. package/docs/reference/variables/HttpStatusCode.md +1 -1
  34. package/docs/reference/variables/MimeTypes.md +13 -1
  35. package/locales/en.json +11 -1
  36. package/package.json +7 -6
  37. package/dist/types/models/jwtAlgorithms.d.ts +0 -17
  38. package/docs/reference/type-aliases/JwtAlgorithms.md +0 -5
  39. package/docs/reference/variables/JwtAlgorithms.md +0 -19
@@ -2,6 +2,7 @@
2
2
 
3
3
  var core = require('@twin.org/core');
4
4
  var crypto = require('@twin.org/crypto');
5
+ var jose = require('jose');
5
6
 
6
7
  // Copyright 2024 IOTA Stiftung.
7
8
  // SPDX-License-Identifier: Apache-2.0.
@@ -19,13 +20,13 @@ class FetchError extends core.BaseError {
19
20
  * @param message The message as a code.
20
21
  * @param httpStatus The http status code.
21
22
  * @param properties Any additional information for the error.
22
- * @param inner The inner error if we have wrapped another error.
23
+ * @param cause The cause of the error if we have wrapped another error.
23
24
  */
24
- constructor(source, message, httpStatus, properties, inner) {
25
+ constructor(source, message, httpStatus, properties, cause) {
25
26
  super(FetchError.CLASS_NAME, source, message, {
26
27
  httpStatus,
27
28
  ...properties
28
- }, inner);
29
+ }, cause);
29
30
  }
30
31
  }
31
32
 
@@ -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
  */
@@ -401,6 +389,10 @@ const MimeTypes = {
401
389
  * Application GZIP - application/gzip
402
390
  */
403
391
  Gzip: "application/gzip",
392
+ /**
393
+ * Application deflate - application/zlib
394
+ */
395
+ Zlib: "application/zlib",
404
396
  /**
405
397
  * Application BZIP2 - application/x-bzip2
406
398
  */
@@ -529,11 +521,20 @@ class FetchHelper {
529
521
  }
530
522
  }, options?.timeoutMs);
531
523
  }
524
+ let finalBody;
525
+ if (method === HttpMethod.POST || method === HttpMethod.PUT) {
526
+ if (core.Is.string(body)) {
527
+ finalBody = body;
528
+ }
529
+ else if (core.Is.uint8Array(body)) {
530
+ finalBody = new Uint8Array(body);
531
+ }
532
+ }
532
533
  try {
533
534
  const requestOptions = {
534
535
  method,
535
536
  headers: options?.headers,
536
- body: method === HttpMethod.POST || method === HttpMethod.PUT ? body : undefined,
537
+ body: finalBody,
537
538
  signal: controller ? controller.signal : undefined
538
539
  };
539
540
  if (core.Is.boolean(options?.includeCredentials)) {
@@ -555,7 +556,7 @@ class FetchHelper {
555
556
  if (isErr && core.Is.stringValue(err.message) && err.message.includes("Failed to fetch")) {
556
557
  lastError = new FetchError(source, `${FetchHelper._CLASS_NAME_CAMEL_CASE}.connectivity`, HttpStatusCode.serviceUnavailable, {
557
558
  url
558
- });
559
+ }, err);
559
560
  }
560
561
  else {
561
562
  const isAbort = isErr && err.name === "AbortError";
@@ -570,7 +571,7 @@ class FetchHelper {
570
571
  if (isErr && "statusText" in err) {
571
572
  props.statusText = err.statusText;
572
573
  }
573
- lastError = new FetchError(source, `${FetchHelper._CLASS_NAME_CAMEL_CASE}.${isAbort ? "timeout" : "general"}`, httpStatus, props);
574
+ lastError = new FetchError(source, `${FetchHelper._CLASS_NAME_CAMEL_CASE}.${isAbort ? "timeout" : "general"}`, httpStatus, props, err);
574
575
  }
575
576
  }
576
577
  finally {
@@ -627,12 +628,15 @@ class FetchHelper {
627
628
  }
628
629
  }
629
630
  const errorResponseData = await response.json();
631
+ const errorResponse = core.BaseError.fromError(errorResponseData);
632
+ const isErrorEmpty = core.BaseError.isEmpty(errorResponse);
630
633
  // False positive as FetchError is derived from Error
631
634
  // eslint-disable-next-line @typescript-eslint/only-throw-error
632
635
  throw new FetchError(source, `${FetchHelper._CLASS_NAME_CAMEL_CASE}.failureStatusText`, response.status, {
633
636
  statusText: response.statusText,
634
- url
635
- }, errorResponseData);
637
+ url,
638
+ data: isErrorEmpty ? errorResponseData : undefined
639
+ }, isErrorEmpty ? undefined : errorResponse);
636
640
  }
637
641
  /**
638
642
  * Perform a request for binary data.
@@ -677,12 +681,15 @@ class FetchHelper {
677
681
  }
678
682
  }
679
683
  const errorResponseData = await response.json();
684
+ const errorResponse = core.BaseError.fromError(errorResponseData);
685
+ const isErrorEmpty = core.BaseError.isEmpty(errorResponse);
680
686
  // False positive as FetchError is derived from Error
681
687
  // eslint-disable-next-line @typescript-eslint/only-throw-error
682
688
  throw new FetchError(source, `${FetchHelper._CLASS_NAME_CAMEL_CASE}.failureStatusText`, response.status, {
683
689
  statusText: response.statusText,
684
- url
685
- }, errorResponseData);
690
+ url,
691
+ data: isErrorEmpty ? errorResponseData : undefined
692
+ }, isErrorEmpty ? undefined : errorResponse);
686
693
  }
687
694
  /**
688
695
  * Clears the cache.
@@ -698,6 +705,15 @@ class FetchHelper {
698
705
  static async getCacheEntry(url) {
699
706
  return core.AsyncCache.get(`${FetchHelper._CACHE_PREFIX}${url}`);
700
707
  }
708
+ /**
709
+ * Set a cache entry.
710
+ * @param url The url for the request.
711
+ * @param value The value to cache.
712
+ * @returns The cache entry if it exists.
713
+ */
714
+ static async setCacheEntry(url, value) {
715
+ core.AsyncCache.set(`${FetchHelper._CACHE_PREFIX}${url}`, value);
716
+ }
701
717
  /**
702
718
  * Remove a cache entry.
703
719
  * @param url The url for the request.
@@ -710,7 +726,158 @@ class FetchHelper {
710
726
  // Copyright 2024 IOTA Stiftung.
711
727
  // SPDX-License-Identifier: Apache-2.0.
712
728
  /**
713
- * Class to encode and decode JSON Web Tokens.
729
+ * Class to handle JSON Web Keys.
730
+ */
731
+ class Jwk {
732
+ /**
733
+ * Runtime name for the class.
734
+ * @internal
735
+ */
736
+ static _CLASS_NAME = "Jwk";
737
+ /**
738
+ * Convert the JWK to a crypto key.
739
+ * @param jwk The JWK to convert.
740
+ * @param alg The alg to be used, defaults to jwk.alg.
741
+ * @returns The crypto key.
742
+ */
743
+ static async toCryptoKey(jwk, alg) {
744
+ core.Guards.object(Jwk._CLASS_NAME, "jwk", jwk);
745
+ try {
746
+ return jose.importJWK(jwk, alg);
747
+ }
748
+ catch (err) {
749
+ throw new core.GeneralError(Jwk._CLASS_NAME, "jwkImportFailed", undefined, err);
750
+ }
751
+ }
752
+ /**
753
+ * Convert the Ed25519 private key to a crypto key.
754
+ * @param privateKey The private key to use.
755
+ * @returns The crypto key.
756
+ */
757
+ static async fromEd25519Private(privateKey) {
758
+ core.Guards.uint8Array(Jwk._CLASS_NAME, "privateKey", privateKey);
759
+ const publicKey = crypto.Ed25519.publicKeyFromPrivateKey(privateKey);
760
+ const jwk = {
761
+ kty: "OKP",
762
+ use: "enc",
763
+ alg: "EdDSA",
764
+ crv: "Ed25519",
765
+ x: core.Converter.bytesToBase64Url(publicKey),
766
+ d: core.Converter.bytesToBase64Url(privateKey)
767
+ };
768
+ return jwk;
769
+ }
770
+ /**
771
+ * Convert the Ed25519 public key to a crypto key.
772
+ * @param publicKey The private key to use.
773
+ * @returns The crypto key.
774
+ */
775
+ static async fromEd25519Public(publicKey) {
776
+ core.Guards.uint8Array(Jwk._CLASS_NAME, "publicKey", publicKey);
777
+ const jwk = {
778
+ kty: "OKP",
779
+ use: "sig",
780
+ alg: "EdDSA",
781
+ crv: "Ed25519",
782
+ x: core.Converter.bytesToBase64Url(publicKey)
783
+ };
784
+ return jwk;
785
+ }
786
+ /**
787
+ * Convert the JWK to raw keys.
788
+ * @param jwk The JWK to convert to raw.
789
+ * @returns The crypto key.
790
+ */
791
+ static async toRaw(jwk) {
792
+ core.Guards.object(Jwk._CLASS_NAME, "jwk", jwk);
793
+ let publicKey;
794
+ let privateKey;
795
+ if (core.Is.stringBase64Url(jwk.x)) {
796
+ publicKey = core.Converter.base64UrlToBytes(jwk.x);
797
+ }
798
+ if (core.Is.stringBase64Url(jwk.d)) {
799
+ privateKey = core.Converter.base64UrlToBytes(jwk.d);
800
+ }
801
+ return {
802
+ publicKey,
803
+ privateKey
804
+ };
805
+ }
806
+ /**
807
+ * Generate a KID for the JWK.
808
+ * @param jwk The JWK to generate a KID for.
809
+ * @returns The KID.
810
+ */
811
+ static async generateKid(jwk) {
812
+ core.Guards.object(Jwk._CLASS_NAME, "jwk", jwk);
813
+ const kidProps = core.ObjectHelper.pick(jwk, ["crv", "kty", "x"]);
814
+ const canonicalJson = core.JsonHelper.canonicalize(kidProps);
815
+ const hash = crypto.Sha256.sum256(core.Converter.utf8ToBytes(canonicalJson));
816
+ return core.Converter.bytesToBase64Url(hash);
817
+ }
818
+ }
819
+
820
+ // Copyright 2024 IOTA Stiftung.
821
+ // SPDX-License-Identifier: Apache-2.0.
822
+ /**
823
+ * Class to handle JSON Web Signatures.
824
+ */
825
+ class Jws {
826
+ /**
827
+ * Runtime name for the class.
828
+ * @internal
829
+ */
830
+ static _CLASS_NAME = "Jws";
831
+ /**
832
+ * Create a signature.
833
+ * @param privateKey The private key to use.
834
+ * @param hash The hash to sign.
835
+ * @param algOverride An optional algorithm override.
836
+ * @returns The signature.
837
+ */
838
+ static async create(privateKey, hash, algOverride) {
839
+ core.Guards.defined(Jws._CLASS_NAME, "privateKey", privateKey);
840
+ core.Guards.uint8Array(Jws._CLASS_NAME, "hash", hash);
841
+ try {
842
+ const jws = await new jose.CompactSign(hash)
843
+ .setProtectedHeader({
844
+ alg: algOverride ?? (core.Is.uint8Array(privateKey) ? "EdDSA" : privateKey.algorithm.name),
845
+ b64: false,
846
+ crit: ["b64"]
847
+ })
848
+ .sign(privateKey);
849
+ return jws;
850
+ }
851
+ catch (err) {
852
+ throw new core.GeneralError(Jws._CLASS_NAME, "createFailed", undefined, err);
853
+ }
854
+ }
855
+ /**
856
+ * Verify a signature.
857
+ * @param jws The signature to verify.
858
+ * @param publicKey The public key to verify the signature with.
859
+ * @param hash The hash to verify.
860
+ * @returns True if the signature was verified.
861
+ */
862
+ static async verify(jws, publicKey, hash) {
863
+ core.Guards.stringValue(Jws._CLASS_NAME, "jws", jws);
864
+ core.Guards.defined(Jws._CLASS_NAME, "publicKey", publicKey);
865
+ core.Guards.uint8Array(Jws._CLASS_NAME, "hash", hash);
866
+ try {
867
+ const jwsParts = jws.split(".");
868
+ await jose.flattenedVerify({ protected: jwsParts[0], payload: hash, signature: jwsParts[2] }, publicKey);
869
+ return true;
870
+ }
871
+ catch (err) {
872
+ throw new core.GeneralError(Jws._CLASS_NAME, "verifyFailed", undefined, err);
873
+ }
874
+ }
875
+ }
876
+
877
+ // Copyright 2024 IOTA Stiftung.
878
+ // SPDX-License-Identifier: Apache-2.0.
879
+ /**
880
+ * Class to handle JSON Web Tokens.
714
881
  */
715
882
  class Jwt {
716
883
  /**
@@ -727,9 +894,8 @@ class Jwt {
727
894
  */
728
895
  static async encode(header, payload, key) {
729
896
  core.Guards.object(Jwt._CLASS_NAME, "header", header);
730
- core.Guards.arrayOneOf(Jwt._CLASS_NAME, "header.alg", header.alg, Object.values(JwtAlgorithms));
731
897
  core.Guards.object(Jwt._CLASS_NAME, "payload", payload);
732
- core.Guards.uint8Array(Jwt._CLASS_NAME, "key", key);
898
+ core.Guards.defined(Jwt._CLASS_NAME, "key", key);
733
899
  return Jwt.internalEncode(header, payload, key);
734
900
  }
735
901
  /**
@@ -741,7 +907,7 @@ class Jwt {
741
907
  */
742
908
  static async encodeWithSigner(header, payload, signer) {
743
909
  core.Guards.object(Jwt._CLASS_NAME, "header", header);
744
- core.Guards.arrayOneOf(Jwt._CLASS_NAME, "header.alg", header.alg, Object.values(JwtAlgorithms));
910
+ core.Guards.stringValue(Jwt._CLASS_NAME, "header.alg", header.alg);
745
911
  core.Guards.object(Jwt._CLASS_NAME, "payload", payload);
746
912
  core.Guards.function(Jwt._CLASS_NAME, "signer", signer);
747
913
  return Jwt.internalEncode(header, payload, undefined, signer);
@@ -788,13 +954,8 @@ class Jwt {
788
954
  */
789
955
  static async verify(token, key) {
790
956
  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
- };
957
+ core.Guards.defined(Jwt._CLASS_NAME, "key", key);
958
+ return Jwt.verifySignature(token, key);
798
959
  }
799
960
  /**
800
961
  * Verify a token.
@@ -805,79 +966,131 @@ class Jwt {
805
966
  static async verifyWithVerifier(token, verifier) {
806
967
  core.Guards.stringValue(Jwt._CLASS_NAME, "token", token);
807
968
  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
- };
969
+ return Jwt.verifySignature(token, undefined, verifier);
815
970
  }
816
971
  /**
817
972
  * 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.
973
+ * @param token The token to verify.
821
974
  * @param key The key for verifying the token, if not provided no verification occurs.
822
975
  * @param verifier Custom verification method.
823
976
  * @returns True if the parts are verified.
824
977
  */
825
- static async verifySignature(header, payload, signature, key, verifier) {
978
+ static async verifySignature(token, key, verifier) {
979
+ core.Guards.stringValue(Jwt._CLASS_NAME, "token", token);
826
980
  const hasKey = core.Is.notEmpty(key);
827
981
  const hasVerifier = core.Is.notEmpty(verifier);
828
982
  if (!hasKey && !hasVerifier) {
829
983
  throw new core.GeneralError(Jwt._CLASS_NAME, "noKeyOrVerifier");
830
984
  }
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;
985
+ verifier ??= async (t, k) => Jwt.defaultVerifier(t, k);
986
+ return verifier(token, key);
846
987
  }
847
988
  /**
848
989
  * The default signer for the JWT.
849
- * @param alg The algorithm to use.
850
- * @param key The key to sign with.
990
+ * @param header The header to sign.
851
991
  * @param payload The payload to sign.
992
+ * @param key The optional key to sign with.
852
993
  * @returns The signature.
853
994
  */
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();
995
+ static async defaultSigner(header, payload, key) {
996
+ core.Guards.object(Jwt._CLASS_NAME, "header", header);
997
+ core.Guards.object(Jwt._CLASS_NAME, "payload", payload);
998
+ core.Guards.defined(Jwt._CLASS_NAME, "key", key);
999
+ const signer = new jose.SignJWT(payload);
1000
+ signer.setProtectedHeader(header);
1001
+ let finalKey = key;
1002
+ if (header.alg === "EdDSA" && core.Is.uint8Array(key)) {
1003
+ // Jose does not support Ed25519 keys in raw format, so we need to convert it to PKCS8.
1004
+ finalKey = await crypto.Ed25519.privateKeyToPkcs8(key);
860
1005
  }
861
- return crypto.Ed25519.sign(key, payload);
1006
+ return signer.sign(finalKey);
862
1007
  }
863
1008
  /**
864
1009
  * The default verifier for the JWT.
865
- * @param alg The algorithm to use.
1010
+ * @param token The token to verify.
866
1011
  * @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.
1012
+ * @returns The header and payload if verification successful.
1013
+ */
1014
+ static async defaultVerifier(token, key) {
1015
+ core.Guards.stringValue(Jwt._CLASS_NAME, "token", token);
1016
+ core.Guards.defined(Jwt._CLASS_NAME, "key", key);
1017
+ try {
1018
+ const result = await jose.jwtVerify(token, key);
1019
+ return {
1020
+ header: result.protectedHeader,
1021
+ payload: result.payload
1022
+ };
1023
+ }
1024
+ catch (err) {
1025
+ throw new core.GeneralError(Jwt._CLASS_NAME, "verifyFailed", undefined, err);
1026
+ }
1027
+ }
1028
+ /**
1029
+ * Create bytes for signing from header and payload.
1030
+ * @param header The header.
1031
+ * @param payload The payload.
1032
+ * @returns The bytes to sign.
1033
+ */
1034
+ static toSigningBytes(header, payload) {
1035
+ core.Guards.object(Jwt._CLASS_NAME, "header", header);
1036
+ core.Guards.object(Jwt._CLASS_NAME, "payload", payload);
1037
+ const segments = [];
1038
+ const headerBytes = core.Converter.utf8ToBytes(JSON.stringify(header));
1039
+ segments.push(core.Converter.bytesToBase64Url(headerBytes));
1040
+ const payloadBytes = core.Converter.utf8ToBytes(JSON.stringify(payload));
1041
+ segments.push(core.Converter.bytesToBase64Url(payloadBytes));
1042
+ return core.Converter.utf8ToBytes(segments.join("."));
1043
+ }
1044
+ /**
1045
+ * Create header and payload from signing bytes.
1046
+ * @param signingBytes The signing bytes from a token.
1047
+ * @returns The header and payload.
1048
+ * @throws If the signing bytes are invalid
870
1049
  */
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);
1050
+ static fromSigningBytes(signingBytes) {
1051
+ core.Guards.uint8Array(Jwt._CLASS_NAME, "signingBytes", signingBytes);
1052
+ const segments = core.Converter.bytesToUtf8(signingBytes).split(".");
1053
+ if (segments.length !== 2) {
1054
+ throw new core.GeneralError(Jwt._CLASS_NAME, "invalidSigningBytes");
1055
+ }
1056
+ const headerBytes = core.Converter.base64UrlToBytes(segments[0]);
1057
+ const payloadBytes = core.Converter.base64UrlToBytes(segments[1]);
1058
+ return {
1059
+ header: core.ObjectHelper.fromBytes(headerBytes),
1060
+ payload: core.ObjectHelper.fromBytes(payloadBytes)
1061
+ };
1062
+ }
1063
+ /**
1064
+ * Convert signed bytes and signature bytes to token.
1065
+ * @param signingBytes The signed bytes.
1066
+ * @param signature The signature.
1067
+ * @returns The token.
1068
+ */
1069
+ static tokenFromBytes(signingBytes, signature) {
1070
+ core.Guards.uint8Array(Jwt._CLASS_NAME, "signingBytes", signingBytes);
874
1071
  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);
1072
+ const signedBytesUtf8 = core.Converter.bytesToUtf8(signingBytes);
1073
+ const signatureBase64 = core.Converter.bytesToBase64Url(signature);
1074
+ return `${signedBytesUtf8}.${signatureBase64}`;
1075
+ }
1076
+ /**
1077
+ * Convert the token to signing bytes and signature bytes.
1078
+ * @param token The token to convert to bytes.
1079
+ * @returns The decoded bytes.
1080
+ * @throws If the token is invalid.
1081
+ */
1082
+ static tokenToBytes(token) {
1083
+ core.Guards.stringValue(Jwt._CLASS_NAME, "token", token);
1084
+ const segments = token.split(".");
1085
+ if (segments.length !== 3) {
1086
+ throw new core.GeneralError(Jwt._CLASS_NAME, "invalidTokenParts");
879
1087
  }
880
- return crypto.Ed25519.verify(key, payload, signature);
1088
+ const signingBytes = core.Converter.utf8ToBytes(`${segments[0]}.${segments[1]}`);
1089
+ const signature = core.Converter.base64UrlToBytes(segments[2]);
1090
+ return {
1091
+ signingBytes,
1092
+ signature
1093
+ };
881
1094
  }
882
1095
  /**
883
1096
  * Encode a token.
@@ -894,19 +1107,11 @@ class Jwt {
894
1107
  if (!hasKey && !hasSigner) {
895
1108
  throw new core.GeneralError(Jwt._CLASS_NAME, "noKeyOrSigner");
896
1109
  }
897
- signer ??= async (alg, k, p) => Jwt.defaultSigner(alg, k, p);
1110
+ signer ??= async (h, p, k) => Jwt.defaultSigner(h, p, k);
898
1111
  if (core.Is.undefined(header.typ)) {
899
1112
  header.typ = "JWT";
900
1113
  }
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(".");
1114
+ return signer(header, payload, key);
910
1115
  }
911
1116
  }
912
1117
 
@@ -922,7 +1127,7 @@ class MimeTypeHelper {
922
1127
  * @returns The mime type if detected.
923
1128
  */
924
1129
  static async detect(data) {
925
- if (!core.Is.uint8Array(data)) {
1130
+ if (!core.Is.uint8Array(data) || data.length === 0) {
926
1131
  return undefined;
927
1132
  }
928
1133
  // Image
@@ -946,6 +1151,11 @@ class MimeTypeHelper {
946
1151
  if (MimeTypeHelper.checkBytes(data, [0x1f, 0x8b, 0x8])) {
947
1152
  return MimeTypes.Gzip;
948
1153
  }
1154
+ if (MimeTypeHelper.checkBytes(data, [0x78, 0x01]) ||
1155
+ MimeTypeHelper.checkBytes(data, [0x78, 0x9c]) ||
1156
+ MimeTypeHelper.checkBytes(data, [0x78, 0xda])) {
1157
+ return MimeTypes.Zlib;
1158
+ }
949
1159
  if (MimeTypeHelper.checkBytes(data, [0x42, 0x5a, 0x68])) {
950
1160
  return MimeTypes.Bzip2;
951
1161
  }
@@ -994,12 +1204,14 @@ class MimeTypeHelper {
994
1204
  [MimeTypes.Javascript]: "js",
995
1205
  [MimeTypes.Json]: "json",
996
1206
  [MimeTypes.JsonLd]: "jsonld",
1207
+ [MimeTypes.Jwt]: "jwt",
997
1208
  [MimeTypes.Xml]: "xml",
998
1209
  [MimeTypes.OctetStream]: "bin",
999
1210
  [MimeTypes.Gzip]: "gzip",
1211
+ [MimeTypes.Zlib]: "zlib",
1000
1212
  [MimeTypes.Bzip2]: "bz2",
1001
1213
  [MimeTypes.Zip]: "zip",
1002
- [MimeTypes.Pdf]: "pfd",
1214
+ [MimeTypes.Pdf]: "pdf",
1003
1215
  [MimeTypes.Gif]: "gif",
1004
1216
  [MimeTypes.Bmp]: "bmp",
1005
1217
  [MimeTypes.Jpeg]: "jpeg",
@@ -1050,7 +1262,8 @@ exports.FetchHelper = FetchHelper;
1050
1262
  exports.HeaderTypes = HeaderTypes;
1051
1263
  exports.HttpMethod = HttpMethod;
1052
1264
  exports.HttpStatusCode = HttpStatusCode;
1265
+ exports.Jwk = Jwk;
1266
+ exports.Jws = Jws;
1053
1267
  exports.Jwt = Jwt;
1054
- exports.JwtAlgorithms = JwtAlgorithms;
1055
1268
  exports.MimeTypeHelper = MimeTypeHelper;
1056
1269
  exports.MimeTypes = MimeTypes;