@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.
- package/dist/cjs/index.cjs +311 -98
- package/dist/esm/index.mjs +312 -100
- package/dist/types/errors/fetchError.d.ts +2 -2
- 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 +613 -1
- package/docs/reference/classes/FetchError.md +17 -9
- 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/HeaderTypes.md +1 -1
- package/docs/reference/variables/HttpMethod.md +1 -1
- package/docs/reference/variables/HttpStatusCode.md +1 -1
- package/docs/reference/variables/MimeTypes.md +13 -1
- 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.
|
|
@@ -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
|
|
23
|
+
* @param cause The cause of the error if we have wrapped another error.
|
|
23
24
|
*/
|
|
24
|
-
constructor(source, message, httpStatus, properties,
|
|
25
|
+
constructor(source, message, httpStatus, properties, cause) {
|
|
25
26
|
super(FetchError.CLASS_NAME, source, message, {
|
|
26
27
|
httpStatus,
|
|
27
28
|
...properties
|
|
28
|
-
},
|
|
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:
|
|
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
|
-
|
|
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
|
-
|
|
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
|
|
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.
|
|
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.
|
|
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.
|
|
792
|
-
|
|
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
|
-
|
|
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
|
|
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(
|
|
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
|
-
|
|
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;
|
|
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
|
|
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(
|
|
855
|
-
core.Guards.
|
|
856
|
-
core.Guards.
|
|
857
|
-
|
|
858
|
-
|
|
859
|
-
|
|
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
|
|
1006
|
+
return signer.sign(finalKey);
|
|
862
1007
|
}
|
|
863
1008
|
/**
|
|
864
1009
|
* The default verifier for the JWT.
|
|
865
|
-
* @param
|
|
1010
|
+
* @param token The token to verify.
|
|
866
1011
|
* @param key The key to verify with.
|
|
867
|
-
* @
|
|
868
|
-
|
|
869
|
-
|
|
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
|
|
872
|
-
core.Guards.uint8Array(Jwt._CLASS_NAME, "
|
|
873
|
-
core.
|
|
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
|
-
|
|
876
|
-
|
|
877
|
-
|
|
878
|
-
|
|
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
|
-
|
|
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 (
|
|
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
|
-
|
|
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]: "
|
|
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;
|