@twin.org/web 0.0.1-next.9 → 0.0.2-next.3
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 +298 -94
- package/dist/esm/index.mjs +299 -96
- package/dist/types/index.d.ts +3 -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/jwkCryptoKey.d.ts +4 -0
- package/dist/types/models/mimeTypes.d.ts +8 -0
- package/dist/types/utils/fetchHelper.d.ts +7 -0
- package/dist/types/utils/jwk.d.ts +41 -0
- package/dist/types/utils/jws.d.ts +22 -0
- package/dist/types/utils/jwt.d.ts +67 -29
- package/docs/changelog.md +480 -1
- package/docs/reference/classes/FetchError.md +16 -8
- package/docs/reference/classes/FetchHelper.md +104 -28
- package/docs/reference/classes/Jwk.md +129 -0
- package/docs/reference/classes/Jws.md +81 -0
- package/docs/reference/classes/Jwt.md +261 -105
- package/docs/reference/classes/MimeTypeHelper.md +9 -5
- package/docs/reference/index.md +3 -2
- package/docs/reference/interfaces/IHttpHeaders.md +1 -1
- package/docs/reference/interfaces/IJwk.md +2 -106
- package/docs/reference/interfaces/IJwtHeader.md +5 -23
- package/docs/reference/interfaces/IJwtPayload.md +5 -55
- package/docs/reference/type-aliases/HeaderTypes.md +1 -1
- package/docs/reference/type-aliases/HttpMethod.md +1 -1
- package/docs/reference/type-aliases/HttpStatusCode.md +1 -1
- package/docs/reference/type-aliases/JwkCryptoKey.md +5 -0
- package/docs/reference/type-aliases/MimeTypes.md +1 -1
- package/docs/reference/variables/MimeTypes.md +12 -0
- package/locales/en.json +11 -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
|
@@ -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.
|
|
@@ -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
|
*/
|
|
@@ -555,7 +547,7 @@ class FetchHelper {
|
|
|
555
547
|
if (isErr && core.Is.stringValue(err.message) && err.message.includes("Failed to fetch")) {
|
|
556
548
|
lastError = new FetchError(source, `${FetchHelper._CLASS_NAME_CAMEL_CASE}.connectivity`, HttpStatusCode.serviceUnavailable, {
|
|
557
549
|
url
|
|
558
|
-
});
|
|
550
|
+
}, err);
|
|
559
551
|
}
|
|
560
552
|
else {
|
|
561
553
|
const isAbort = isErr && err.name === "AbortError";
|
|
@@ -570,7 +562,7 @@ class FetchHelper {
|
|
|
570
562
|
if (isErr && "statusText" in err) {
|
|
571
563
|
props.statusText = err.statusText;
|
|
572
564
|
}
|
|
573
|
-
lastError = new FetchError(source, `${FetchHelper._CLASS_NAME_CAMEL_CASE}.${isAbort ? "timeout" : "general"}`, httpStatus, props);
|
|
565
|
+
lastError = new FetchError(source, `${FetchHelper._CLASS_NAME_CAMEL_CASE}.${isAbort ? "timeout" : "general"}`, httpStatus, props, err);
|
|
574
566
|
}
|
|
575
567
|
}
|
|
576
568
|
finally {
|
|
@@ -627,12 +619,15 @@ class FetchHelper {
|
|
|
627
619
|
}
|
|
628
620
|
}
|
|
629
621
|
const errorResponseData = await response.json();
|
|
622
|
+
const errorResponse = core.BaseError.fromError(errorResponseData);
|
|
623
|
+
const isErrorEmpty = core.BaseError.isEmpty(errorResponse);
|
|
630
624
|
// False positive as FetchError is derived from Error
|
|
631
625
|
// eslint-disable-next-line @typescript-eslint/only-throw-error
|
|
632
626
|
throw new FetchError(source, `${FetchHelper._CLASS_NAME_CAMEL_CASE}.failureStatusText`, response.status, {
|
|
633
627
|
statusText: response.statusText,
|
|
634
|
-
url
|
|
635
|
-
|
|
628
|
+
url,
|
|
629
|
+
data: isErrorEmpty ? errorResponseData : undefined
|
|
630
|
+
}, isErrorEmpty ? undefined : errorResponse);
|
|
636
631
|
}
|
|
637
632
|
/**
|
|
638
633
|
* Perform a request for binary data.
|
|
@@ -677,12 +672,15 @@ class FetchHelper {
|
|
|
677
672
|
}
|
|
678
673
|
}
|
|
679
674
|
const errorResponseData = await response.json();
|
|
675
|
+
const errorResponse = core.BaseError.fromError(errorResponseData);
|
|
676
|
+
const isErrorEmpty = core.BaseError.isEmpty(errorResponse);
|
|
680
677
|
// False positive as FetchError is derived from Error
|
|
681
678
|
// eslint-disable-next-line @typescript-eslint/only-throw-error
|
|
682
679
|
throw new FetchError(source, `${FetchHelper._CLASS_NAME_CAMEL_CASE}.failureStatusText`, response.status, {
|
|
683
680
|
statusText: response.statusText,
|
|
684
|
-
url
|
|
685
|
-
|
|
681
|
+
url,
|
|
682
|
+
data: isErrorEmpty ? errorResponseData : undefined
|
|
683
|
+
}, isErrorEmpty ? undefined : errorResponse);
|
|
686
684
|
}
|
|
687
685
|
/**
|
|
688
686
|
* Clears the cache.
|
|
@@ -698,6 +696,15 @@ class FetchHelper {
|
|
|
698
696
|
static async getCacheEntry(url) {
|
|
699
697
|
return core.AsyncCache.get(`${FetchHelper._CACHE_PREFIX}${url}`);
|
|
700
698
|
}
|
|
699
|
+
/**
|
|
700
|
+
* Set a cache entry.
|
|
701
|
+
* @param url The url for the request.
|
|
702
|
+
* @param value The value to cache.
|
|
703
|
+
* @returns The cache entry if it exists.
|
|
704
|
+
*/
|
|
705
|
+
static async setCacheEntry(url, value) {
|
|
706
|
+
core.AsyncCache.set(`${FetchHelper._CACHE_PREFIX}${url}`, value);
|
|
707
|
+
}
|
|
701
708
|
/**
|
|
702
709
|
* Remove a cache entry.
|
|
703
710
|
* @param url The url for the request.
|
|
@@ -710,7 +717,158 @@ class FetchHelper {
|
|
|
710
717
|
// Copyright 2024 IOTA Stiftung.
|
|
711
718
|
// SPDX-License-Identifier: Apache-2.0.
|
|
712
719
|
/**
|
|
713
|
-
* Class to
|
|
720
|
+
* Class to handle JSON Web Keys.
|
|
721
|
+
*/
|
|
722
|
+
class Jwk {
|
|
723
|
+
/**
|
|
724
|
+
* Runtime name for the class.
|
|
725
|
+
* @internal
|
|
726
|
+
*/
|
|
727
|
+
static _CLASS_NAME = "Jwk";
|
|
728
|
+
/**
|
|
729
|
+
* Convert the JWK to a crypto key.
|
|
730
|
+
* @param jwk The JWK to convert.
|
|
731
|
+
* @param alg The alg to be used, defaults to jwk.alg.
|
|
732
|
+
* @returns The crypto key.
|
|
733
|
+
*/
|
|
734
|
+
static async toCryptoKey(jwk, alg) {
|
|
735
|
+
core.Guards.object(Jwk._CLASS_NAME, "jwk", jwk);
|
|
736
|
+
try {
|
|
737
|
+
return jose.importJWK(jwk, alg);
|
|
738
|
+
}
|
|
739
|
+
catch (err) {
|
|
740
|
+
throw new core.GeneralError(Jwk._CLASS_NAME, "jwkImportFailed", undefined, err);
|
|
741
|
+
}
|
|
742
|
+
}
|
|
743
|
+
/**
|
|
744
|
+
* Convert the Ed25519 private key to a crypto key.
|
|
745
|
+
* @param privateKey The private key to use.
|
|
746
|
+
* @returns The crypto key.
|
|
747
|
+
*/
|
|
748
|
+
static async fromEd25519Private(privateKey) {
|
|
749
|
+
core.Guards.uint8Array(Jwk._CLASS_NAME, "privateKey", privateKey);
|
|
750
|
+
const publicKey = crypto.Ed25519.publicKeyFromPrivateKey(privateKey);
|
|
751
|
+
const jwk = {
|
|
752
|
+
kty: "OKP",
|
|
753
|
+
use: "enc",
|
|
754
|
+
alg: "EdDSA",
|
|
755
|
+
crv: "Ed25519",
|
|
756
|
+
x: core.Converter.bytesToBase64Url(publicKey),
|
|
757
|
+
d: core.Converter.bytesToBase64Url(privateKey)
|
|
758
|
+
};
|
|
759
|
+
return jwk;
|
|
760
|
+
}
|
|
761
|
+
/**
|
|
762
|
+
* Convert the Ed25519 public key to a crypto key.
|
|
763
|
+
* @param publicKey The private key to use.
|
|
764
|
+
* @returns The crypto key.
|
|
765
|
+
*/
|
|
766
|
+
static async fromEd25519Public(publicKey) {
|
|
767
|
+
core.Guards.uint8Array(Jwk._CLASS_NAME, "publicKey", publicKey);
|
|
768
|
+
const jwk = {
|
|
769
|
+
kty: "OKP",
|
|
770
|
+
use: "sig",
|
|
771
|
+
alg: "EdDSA",
|
|
772
|
+
crv: "Ed25519",
|
|
773
|
+
x: core.Converter.bytesToBase64Url(publicKey)
|
|
774
|
+
};
|
|
775
|
+
return jwk;
|
|
776
|
+
}
|
|
777
|
+
/**
|
|
778
|
+
* Convert the JWK to raw keys.
|
|
779
|
+
* @param jwk The JWK to convert to raw.
|
|
780
|
+
* @returns The crypto key.
|
|
781
|
+
*/
|
|
782
|
+
static async toRaw(jwk) {
|
|
783
|
+
core.Guards.object(Jwk._CLASS_NAME, "jwk", jwk);
|
|
784
|
+
let publicKey;
|
|
785
|
+
let privateKey;
|
|
786
|
+
if (core.Is.stringBase64Url(jwk.x)) {
|
|
787
|
+
publicKey = core.Converter.base64UrlToBytes(jwk.x);
|
|
788
|
+
}
|
|
789
|
+
if (core.Is.stringBase64Url(jwk.d)) {
|
|
790
|
+
privateKey = core.Converter.base64UrlToBytes(jwk.d);
|
|
791
|
+
}
|
|
792
|
+
return {
|
|
793
|
+
publicKey,
|
|
794
|
+
privateKey
|
|
795
|
+
};
|
|
796
|
+
}
|
|
797
|
+
/**
|
|
798
|
+
* Generate a KID for the JWK.
|
|
799
|
+
* @param jwk The JWK to generate a KID for.
|
|
800
|
+
* @returns The KID.
|
|
801
|
+
*/
|
|
802
|
+
static async generateKid(jwk) {
|
|
803
|
+
core.Guards.object(Jwk._CLASS_NAME, "jwk", jwk);
|
|
804
|
+
const kidProps = core.ObjectHelper.pick(jwk, ["crv", "kty", "x"]);
|
|
805
|
+
const canonicalJson = core.JsonHelper.canonicalize(kidProps);
|
|
806
|
+
const hash = crypto.Sha256.sum256(core.Converter.utf8ToBytes(canonicalJson));
|
|
807
|
+
return core.Converter.bytesToBase64Url(hash);
|
|
808
|
+
}
|
|
809
|
+
}
|
|
810
|
+
|
|
811
|
+
// Copyright 2024 IOTA Stiftung.
|
|
812
|
+
// SPDX-License-Identifier: Apache-2.0.
|
|
813
|
+
/**
|
|
814
|
+
* Class to handle JSON Web Signatures.
|
|
815
|
+
*/
|
|
816
|
+
class Jws {
|
|
817
|
+
/**
|
|
818
|
+
* Runtime name for the class.
|
|
819
|
+
* @internal
|
|
820
|
+
*/
|
|
821
|
+
static _CLASS_NAME = "Jws";
|
|
822
|
+
/**
|
|
823
|
+
* Create a signature.
|
|
824
|
+
* @param privateKey The private key to use.
|
|
825
|
+
* @param hash The hash to sign.
|
|
826
|
+
* @param algOverride An optional algorithm override.
|
|
827
|
+
* @returns The signature.
|
|
828
|
+
*/
|
|
829
|
+
static async create(privateKey, hash, algOverride) {
|
|
830
|
+
core.Guards.defined(Jws._CLASS_NAME, "privateKey", privateKey);
|
|
831
|
+
core.Guards.uint8Array(Jws._CLASS_NAME, "hash", hash);
|
|
832
|
+
try {
|
|
833
|
+
const jws = await new jose.CompactSign(hash)
|
|
834
|
+
.setProtectedHeader({
|
|
835
|
+
alg: algOverride ?? (core.Is.uint8Array(privateKey) ? "EdDSA" : privateKey.algorithm.name),
|
|
836
|
+
b64: false,
|
|
837
|
+
crit: ["b64"]
|
|
838
|
+
})
|
|
839
|
+
.sign(privateKey);
|
|
840
|
+
return jws;
|
|
841
|
+
}
|
|
842
|
+
catch (err) {
|
|
843
|
+
throw new core.GeneralError(Jws._CLASS_NAME, "createFailed", undefined, err);
|
|
844
|
+
}
|
|
845
|
+
}
|
|
846
|
+
/**
|
|
847
|
+
* Verify a signature.
|
|
848
|
+
* @param jws The signature to verify.
|
|
849
|
+
* @param publicKey The public key to verify the signature with.
|
|
850
|
+
* @param hash The hash to verify.
|
|
851
|
+
* @returns True if the signature was verified.
|
|
852
|
+
*/
|
|
853
|
+
static async verify(jws, publicKey, hash) {
|
|
854
|
+
core.Guards.stringValue(Jws._CLASS_NAME, "jws", jws);
|
|
855
|
+
core.Guards.defined(Jws._CLASS_NAME, "publicKey", publicKey);
|
|
856
|
+
core.Guards.uint8Array(Jws._CLASS_NAME, "hash", hash);
|
|
857
|
+
try {
|
|
858
|
+
const jwsParts = jws.split(".");
|
|
859
|
+
await jose.flattenedVerify({ protected: jwsParts[0], payload: hash, signature: jwsParts[2] }, publicKey);
|
|
860
|
+
return true;
|
|
861
|
+
}
|
|
862
|
+
catch (err) {
|
|
863
|
+
throw new core.GeneralError(Jws._CLASS_NAME, "verifyFailed", undefined, err);
|
|
864
|
+
}
|
|
865
|
+
}
|
|
866
|
+
}
|
|
867
|
+
|
|
868
|
+
// Copyright 2024 IOTA Stiftung.
|
|
869
|
+
// SPDX-License-Identifier: Apache-2.0.
|
|
870
|
+
/**
|
|
871
|
+
* Class to handle JSON Web Tokens.
|
|
714
872
|
*/
|
|
715
873
|
class Jwt {
|
|
716
874
|
/**
|
|
@@ -727,9 +885,8 @@ class Jwt {
|
|
|
727
885
|
*/
|
|
728
886
|
static async encode(header, payload, key) {
|
|
729
887
|
core.Guards.object(Jwt._CLASS_NAME, "header", header);
|
|
730
|
-
core.Guards.arrayOneOf(Jwt._CLASS_NAME, "header.alg", header.alg, Object.values(JwtAlgorithms));
|
|
731
888
|
core.Guards.object(Jwt._CLASS_NAME, "payload", payload);
|
|
732
|
-
core.Guards.
|
|
889
|
+
core.Guards.defined(Jwt._CLASS_NAME, "key", key);
|
|
733
890
|
return Jwt.internalEncode(header, payload, key);
|
|
734
891
|
}
|
|
735
892
|
/**
|
|
@@ -741,7 +898,7 @@ class Jwt {
|
|
|
741
898
|
*/
|
|
742
899
|
static async encodeWithSigner(header, payload, signer) {
|
|
743
900
|
core.Guards.object(Jwt._CLASS_NAME, "header", header);
|
|
744
|
-
core.Guards.
|
|
901
|
+
core.Guards.stringValue(Jwt._CLASS_NAME, "header.alg", header.alg);
|
|
745
902
|
core.Guards.object(Jwt._CLASS_NAME, "payload", payload);
|
|
746
903
|
core.Guards.function(Jwt._CLASS_NAME, "signer", signer);
|
|
747
904
|
return Jwt.internalEncode(header, payload, undefined, signer);
|
|
@@ -788,13 +945,8 @@ class Jwt {
|
|
|
788
945
|
*/
|
|
789
946
|
static async verify(token, key) {
|
|
790
947
|
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
|
-
};
|
|
948
|
+
core.Guards.defined(Jwt._CLASS_NAME, "key", key);
|
|
949
|
+
return Jwt.verifySignature(token, key);
|
|
798
950
|
}
|
|
799
951
|
/**
|
|
800
952
|
* Verify a token.
|
|
@@ -805,79 +957,131 @@ class Jwt {
|
|
|
805
957
|
static async verifyWithVerifier(token, verifier) {
|
|
806
958
|
core.Guards.stringValue(Jwt._CLASS_NAME, "token", token);
|
|
807
959
|
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
|
-
};
|
|
960
|
+
return Jwt.verifySignature(token, undefined, verifier);
|
|
815
961
|
}
|
|
816
962
|
/**
|
|
817
963
|
* Verify a token by parts.
|
|
818
|
-
* @param
|
|
819
|
-
* @param payload The payload to verify.
|
|
820
|
-
* @param signature The signature to verify.
|
|
964
|
+
* @param token The token to verify.
|
|
821
965
|
* @param key The key for verifying the token, if not provided no verification occurs.
|
|
822
966
|
* @param verifier Custom verification method.
|
|
823
967
|
* @returns True if the parts are verified.
|
|
824
968
|
*/
|
|
825
|
-
static async verifySignature(
|
|
969
|
+
static async verifySignature(token, key, verifier) {
|
|
970
|
+
core.Guards.stringValue(Jwt._CLASS_NAME, "token", token);
|
|
826
971
|
const hasKey = core.Is.notEmpty(key);
|
|
827
972
|
const hasVerifier = core.Is.notEmpty(verifier);
|
|
828
973
|
if (!hasKey && !hasVerifier) {
|
|
829
974
|
throw new core.GeneralError(Jwt._CLASS_NAME, "noKeyOrVerifier");
|
|
830
975
|
}
|
|
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;
|
|
976
|
+
verifier ??= async (t, k) => Jwt.defaultVerifier(t, k);
|
|
977
|
+
return verifier(token, key);
|
|
846
978
|
}
|
|
847
979
|
/**
|
|
848
980
|
* The default signer for the JWT.
|
|
849
|
-
* @param
|
|
850
|
-
* @param key The key to sign with.
|
|
981
|
+
* @param header The header to sign.
|
|
851
982
|
* @param payload The payload to sign.
|
|
983
|
+
* @param key The optional key to sign with.
|
|
852
984
|
* @returns The signature.
|
|
853
985
|
*/
|
|
854
|
-
static async defaultSigner(
|
|
855
|
-
core.Guards.
|
|
856
|
-
core.Guards.
|
|
857
|
-
|
|
858
|
-
|
|
859
|
-
|
|
986
|
+
static async defaultSigner(header, payload, key) {
|
|
987
|
+
core.Guards.object(Jwt._CLASS_NAME, "header", header);
|
|
988
|
+
core.Guards.object(Jwt._CLASS_NAME, "payload", payload);
|
|
989
|
+
core.Guards.defined(Jwt._CLASS_NAME, "key", key);
|
|
990
|
+
const signer = new jose.SignJWT(payload);
|
|
991
|
+
signer.setProtectedHeader(header);
|
|
992
|
+
let finalKey = key;
|
|
993
|
+
if (header.alg === "EdDSA" && core.Is.uint8Array(key)) {
|
|
994
|
+
// Jose does not support Ed25519 keys in raw format, so we need to convert it to PKCS8.
|
|
995
|
+
finalKey = await crypto.Ed25519.privateKeyToPkcs8(key);
|
|
860
996
|
}
|
|
861
|
-
return
|
|
997
|
+
return signer.sign(finalKey);
|
|
862
998
|
}
|
|
863
999
|
/**
|
|
864
1000
|
* The default verifier for the JWT.
|
|
865
|
-
* @param
|
|
1001
|
+
* @param token The token to verify.
|
|
866
1002
|
* @param key The key to verify with.
|
|
867
|
-
* @
|
|
868
|
-
|
|
869
|
-
|
|
1003
|
+
* @returns The header and payload if verification successful.
|
|
1004
|
+
*/
|
|
1005
|
+
static async defaultVerifier(token, key) {
|
|
1006
|
+
core.Guards.stringValue(Jwt._CLASS_NAME, "token", token);
|
|
1007
|
+
core.Guards.defined(Jwt._CLASS_NAME, "key", key);
|
|
1008
|
+
try {
|
|
1009
|
+
const result = await jose.jwtVerify(token, key);
|
|
1010
|
+
return {
|
|
1011
|
+
header: result.protectedHeader,
|
|
1012
|
+
payload: result.payload
|
|
1013
|
+
};
|
|
1014
|
+
}
|
|
1015
|
+
catch (err) {
|
|
1016
|
+
throw new core.GeneralError(Jwt._CLASS_NAME, "verifyFailed", undefined, err);
|
|
1017
|
+
}
|
|
1018
|
+
}
|
|
1019
|
+
/**
|
|
1020
|
+
* Create bytes for signing from header and payload.
|
|
1021
|
+
* @param header The header.
|
|
1022
|
+
* @param payload The payload.
|
|
1023
|
+
* @returns The bytes to sign.
|
|
870
1024
|
*/
|
|
871
|
-
static
|
|
872
|
-
core.Guards.
|
|
873
|
-
core.Guards.
|
|
1025
|
+
static toSigningBytes(header, payload) {
|
|
1026
|
+
core.Guards.object(Jwt._CLASS_NAME, "header", header);
|
|
1027
|
+
core.Guards.object(Jwt._CLASS_NAME, "payload", payload);
|
|
1028
|
+
const segments = [];
|
|
1029
|
+
const headerBytes = core.Converter.utf8ToBytes(JSON.stringify(header));
|
|
1030
|
+
segments.push(core.Converter.bytesToBase64Url(headerBytes));
|
|
1031
|
+
const payloadBytes = core.Converter.utf8ToBytes(JSON.stringify(payload));
|
|
1032
|
+
segments.push(core.Converter.bytesToBase64Url(payloadBytes));
|
|
1033
|
+
return core.Converter.utf8ToBytes(segments.join("."));
|
|
1034
|
+
}
|
|
1035
|
+
/**
|
|
1036
|
+
* Create header and payload from signing bytes.
|
|
1037
|
+
* @param signingBytes The signing bytes from a token.
|
|
1038
|
+
* @returns The header and payload.
|
|
1039
|
+
* @throws If the signing bytes are invalid
|
|
1040
|
+
*/
|
|
1041
|
+
static fromSigningBytes(signingBytes) {
|
|
1042
|
+
core.Guards.uint8Array(Jwt._CLASS_NAME, "signingBytes", signingBytes);
|
|
1043
|
+
const segments = core.Converter.bytesToUtf8(signingBytes).split(".");
|
|
1044
|
+
if (segments.length !== 2) {
|
|
1045
|
+
throw new core.GeneralError(Jwt._CLASS_NAME, "invalidSigningBytes");
|
|
1046
|
+
}
|
|
1047
|
+
const headerBytes = core.Converter.base64UrlToBytes(segments[0]);
|
|
1048
|
+
const payloadBytes = core.Converter.base64UrlToBytes(segments[1]);
|
|
1049
|
+
return {
|
|
1050
|
+
header: core.ObjectHelper.fromBytes(headerBytes),
|
|
1051
|
+
payload: core.ObjectHelper.fromBytes(payloadBytes)
|
|
1052
|
+
};
|
|
1053
|
+
}
|
|
1054
|
+
/**
|
|
1055
|
+
* Convert signed bytes and signature bytes to token.
|
|
1056
|
+
* @param signingBytes The signed bytes.
|
|
1057
|
+
* @param signature The signature.
|
|
1058
|
+
* @returns The token.
|
|
1059
|
+
*/
|
|
1060
|
+
static tokenFromBytes(signingBytes, signature) {
|
|
1061
|
+
core.Guards.uint8Array(Jwt._CLASS_NAME, "signingBytes", signingBytes);
|
|
874
1062
|
core.Guards.uint8Array(Jwt._CLASS_NAME, "signature", signature);
|
|
875
|
-
|
|
876
|
-
|
|
877
|
-
|
|
878
|
-
|
|
1063
|
+
const signedBytesUtf8 = core.Converter.bytesToUtf8(signingBytes);
|
|
1064
|
+
const signatureBase64 = core.Converter.bytesToBase64Url(signature);
|
|
1065
|
+
return `${signedBytesUtf8}.${signatureBase64}`;
|
|
1066
|
+
}
|
|
1067
|
+
/**
|
|
1068
|
+
* Convert the token to signing bytes and signature bytes.
|
|
1069
|
+
* @param token The token to convert to bytes.
|
|
1070
|
+
* @returns The decoded bytes.
|
|
1071
|
+
* @throws If the token is invalid.
|
|
1072
|
+
*/
|
|
1073
|
+
static tokenToBytes(token) {
|
|
1074
|
+
core.Guards.stringValue(Jwt._CLASS_NAME, "token", token);
|
|
1075
|
+
const segments = token.split(".");
|
|
1076
|
+
if (segments.length !== 3) {
|
|
1077
|
+
throw new core.GeneralError(Jwt._CLASS_NAME, "invalidTokenParts");
|
|
879
1078
|
}
|
|
880
|
-
|
|
1079
|
+
const signingBytes = core.Converter.utf8ToBytes(`${segments[0]}.${segments[1]}`);
|
|
1080
|
+
const signature = core.Converter.base64UrlToBytes(segments[2]);
|
|
1081
|
+
return {
|
|
1082
|
+
signingBytes,
|
|
1083
|
+
signature
|
|
1084
|
+
};
|
|
881
1085
|
}
|
|
882
1086
|
/**
|
|
883
1087
|
* Encode a token.
|
|
@@ -894,19 +1098,11 @@ class Jwt {
|
|
|
894
1098
|
if (!hasKey && !hasSigner) {
|
|
895
1099
|
throw new core.GeneralError(Jwt._CLASS_NAME, "noKeyOrSigner");
|
|
896
1100
|
}
|
|
897
|
-
signer ??= async (
|
|
1101
|
+
signer ??= async (h, p, k) => Jwt.defaultSigner(h, p, k);
|
|
898
1102
|
if (core.Is.undefined(header.typ)) {
|
|
899
1103
|
header.typ = "JWT";
|
|
900
1104
|
}
|
|
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(".");
|
|
1105
|
+
return signer(header, payload, key);
|
|
910
1106
|
}
|
|
911
1107
|
}
|
|
912
1108
|
|
|
@@ -922,7 +1118,7 @@ class MimeTypeHelper {
|
|
|
922
1118
|
* @returns The mime type if detected.
|
|
923
1119
|
*/
|
|
924
1120
|
static async detect(data) {
|
|
925
|
-
if (!core.Is.uint8Array(data)) {
|
|
1121
|
+
if (!core.Is.uint8Array(data) || data.length === 0) {
|
|
926
1122
|
return undefined;
|
|
927
1123
|
}
|
|
928
1124
|
// Image
|
|
@@ -946,6 +1142,11 @@ class MimeTypeHelper {
|
|
|
946
1142
|
if (MimeTypeHelper.checkBytes(data, [0x1f, 0x8b, 0x8])) {
|
|
947
1143
|
return MimeTypes.Gzip;
|
|
948
1144
|
}
|
|
1145
|
+
if (MimeTypeHelper.checkBytes(data, [0x78, 0x01]) ||
|
|
1146
|
+
MimeTypeHelper.checkBytes(data, [0x78, 0x9c]) ||
|
|
1147
|
+
MimeTypeHelper.checkBytes(data, [0x78, 0xda])) {
|
|
1148
|
+
return MimeTypes.Zlib;
|
|
1149
|
+
}
|
|
949
1150
|
if (MimeTypeHelper.checkBytes(data, [0x42, 0x5a, 0x68])) {
|
|
950
1151
|
return MimeTypes.Bzip2;
|
|
951
1152
|
}
|
|
@@ -994,12 +1195,14 @@ class MimeTypeHelper {
|
|
|
994
1195
|
[MimeTypes.Javascript]: "js",
|
|
995
1196
|
[MimeTypes.Json]: "json",
|
|
996
1197
|
[MimeTypes.JsonLd]: "jsonld",
|
|
1198
|
+
[MimeTypes.Jwt]: "jwt",
|
|
997
1199
|
[MimeTypes.Xml]: "xml",
|
|
998
1200
|
[MimeTypes.OctetStream]: "bin",
|
|
999
1201
|
[MimeTypes.Gzip]: "gzip",
|
|
1202
|
+
[MimeTypes.Zlib]: "zlib",
|
|
1000
1203
|
[MimeTypes.Bzip2]: "bz2",
|
|
1001
1204
|
[MimeTypes.Zip]: "zip",
|
|
1002
|
-
[MimeTypes.Pdf]: "
|
|
1205
|
+
[MimeTypes.Pdf]: "pdf",
|
|
1003
1206
|
[MimeTypes.Gif]: "gif",
|
|
1004
1207
|
[MimeTypes.Bmp]: "bmp",
|
|
1005
1208
|
[MimeTypes.Jpeg]: "jpeg",
|
|
@@ -1050,7 +1253,8 @@ exports.FetchHelper = FetchHelper;
|
|
|
1050
1253
|
exports.HeaderTypes = HeaderTypes;
|
|
1051
1254
|
exports.HttpMethod = HttpMethod;
|
|
1052
1255
|
exports.HttpStatusCode = HttpStatusCode;
|
|
1256
|
+
exports.Jwk = Jwk;
|
|
1257
|
+
exports.Jws = Jws;
|
|
1053
1258
|
exports.Jwt = Jwt;
|
|
1054
|
-
exports.JwtAlgorithms = JwtAlgorithms;
|
|
1055
1259
|
exports.MimeTypeHelper = MimeTypeHelper;
|
|
1056
1260
|
exports.MimeTypes = MimeTypes;
|