@twin.org/web 0.0.1-next.36 → 0.0.1-next.38
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 +103 -87
- package/dist/esm/index.mjs +104 -88
- package/dist/types/index.d.ts +2 -1
- package/dist/types/models/IJwk.d.ts +2 -58
- package/dist/types/models/IJwtHeader.d.ts +2 -18
- package/dist/types/models/IJwtPayload.d.ts +2 -33
- package/dist/types/models/jwkCryptoKey.d.ts +5 -0
- package/dist/types/utils/jwk.d.ts +13 -0
- package/dist/types/utils/jwt.d.ts +46 -28
- package/docs/changelog.md +1 -1
- package/docs/reference/classes/Jwk.md +35 -0
- package/docs/reference/classes/Jwt.md +112 -68
- package/docs/reference/index.md +2 -2
- package/docs/reference/interfaces/IJwk.md +2 -106
- package/docs/reference/interfaces/IJwtHeader.md +4 -24
- package/docs/reference/interfaces/IJwtPayload.md +4 -56
- package/docs/reference/type-aliases/JwkCryptoKey.md +5 -0
- package/locales/en.json +5 -1
- package/package.json +5 -4
- package/dist/types/models/jwtAlgorithms.d.ts +0 -17
- package/docs/reference/type-aliases/JwtAlgorithms.md +0 -5
- package/docs/reference/variables/JwtAlgorithms.md +0 -19
package/dist/cjs/index.cjs
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
'use strict';
|
|
2
2
|
|
|
3
3
|
var core = require('@twin.org/core');
|
|
4
|
-
var
|
|
4
|
+
var jose = require('jose');
|
|
5
5
|
|
|
6
6
|
// Copyright 2024 IOTA Stiftung.
|
|
7
7
|
// SPDX-License-Identifier: Apache-2.0.
|
|
@@ -345,23 +345,6 @@ const HttpStatusCode = {
|
|
|
345
345
|
networkAuthenticationRequired: 511
|
|
346
346
|
};
|
|
347
347
|
|
|
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
348
|
// Copyright 2024 IOTA Stiftung.
|
|
366
349
|
// SPDX-License-Identifier: Apache-2.0.
|
|
367
350
|
/**
|
|
@@ -714,7 +697,34 @@ class FetchHelper {
|
|
|
714
697
|
// Copyright 2024 IOTA Stiftung.
|
|
715
698
|
// SPDX-License-Identifier: Apache-2.0.
|
|
716
699
|
/**
|
|
717
|
-
* Class to
|
|
700
|
+
* Class to handle JSON Web Keys.
|
|
701
|
+
*/
|
|
702
|
+
class Jwk {
|
|
703
|
+
/**
|
|
704
|
+
* Runtime name for the class.
|
|
705
|
+
* @internal
|
|
706
|
+
*/
|
|
707
|
+
static _CLASS_NAME = "Jwk";
|
|
708
|
+
/**
|
|
709
|
+
* Convert the JWK to a crypto key.
|
|
710
|
+
* @param jwk The JWK to convert.
|
|
711
|
+
* @returns The crypto key.
|
|
712
|
+
*/
|
|
713
|
+
static async toCryptoKey(jwk) {
|
|
714
|
+
core.Guards.object(Jwk._CLASS_NAME, "jwk", jwk);
|
|
715
|
+
try {
|
|
716
|
+
return jose.importJWK(jwk);
|
|
717
|
+
}
|
|
718
|
+
catch (err) {
|
|
719
|
+
throw new core.GeneralError(Jwk._CLASS_NAME, "jwkImportFailed", undefined, err);
|
|
720
|
+
}
|
|
721
|
+
}
|
|
722
|
+
}
|
|
723
|
+
|
|
724
|
+
// Copyright 2024 IOTA Stiftung.
|
|
725
|
+
// SPDX-License-Identifier: Apache-2.0.
|
|
726
|
+
/**
|
|
727
|
+
* Class to handle JSON Web Tokens.
|
|
718
728
|
*/
|
|
719
729
|
class Jwt {
|
|
720
730
|
/**
|
|
@@ -731,9 +741,9 @@ class Jwt {
|
|
|
731
741
|
*/
|
|
732
742
|
static async encode(header, payload, key) {
|
|
733
743
|
core.Guards.object(Jwt._CLASS_NAME, "header", header);
|
|
734
|
-
core.Guards.
|
|
744
|
+
core.Guards.stringValue(Jwt._CLASS_NAME, "header.alg", header.alg);
|
|
735
745
|
core.Guards.object(Jwt._CLASS_NAME, "payload", payload);
|
|
736
|
-
core.Guards.
|
|
746
|
+
core.Guards.defined(Jwt._CLASS_NAME, "key", key);
|
|
737
747
|
return Jwt.internalEncode(header, payload, key);
|
|
738
748
|
}
|
|
739
749
|
/**
|
|
@@ -745,7 +755,7 @@ class Jwt {
|
|
|
745
755
|
*/
|
|
746
756
|
static async encodeWithSigner(header, payload, signer) {
|
|
747
757
|
core.Guards.object(Jwt._CLASS_NAME, "header", header);
|
|
748
|
-
core.Guards.
|
|
758
|
+
core.Guards.stringValue(Jwt._CLASS_NAME, "header.alg", header.alg);
|
|
749
759
|
core.Guards.object(Jwt._CLASS_NAME, "payload", payload);
|
|
750
760
|
core.Guards.function(Jwt._CLASS_NAME, "signer", signer);
|
|
751
761
|
return Jwt.internalEncode(header, payload, undefined, signer);
|
|
@@ -792,13 +802,8 @@ class Jwt {
|
|
|
792
802
|
*/
|
|
793
803
|
static async verify(token, key) {
|
|
794
804
|
core.Guards.stringValue(Jwt._CLASS_NAME, "token", token);
|
|
795
|
-
core.Guards.
|
|
796
|
-
|
|
797
|
-
const verified = await Jwt.verifySignature(decoded.header, decoded.payload, decoded.signature, key);
|
|
798
|
-
return {
|
|
799
|
-
verified,
|
|
800
|
-
...decoded
|
|
801
|
-
};
|
|
805
|
+
core.Guards.defined(Jwt._CLASS_NAME, "key", key);
|
|
806
|
+
return Jwt.verifySignature(token, key);
|
|
802
807
|
}
|
|
803
808
|
/**
|
|
804
809
|
* Verify a token.
|
|
@@ -809,79 +814,98 @@ class Jwt {
|
|
|
809
814
|
static async verifyWithVerifier(token, verifier) {
|
|
810
815
|
core.Guards.stringValue(Jwt._CLASS_NAME, "token", token);
|
|
811
816
|
core.Guards.function(Jwt._CLASS_NAME, "verifier", verifier);
|
|
812
|
-
|
|
813
|
-
const decoded = await Jwt.decode(token);
|
|
814
|
-
const verified = await Jwt.verifySignature(decoded.header, decoded.payload, decoded.signature, undefined, verifier);
|
|
815
|
-
return {
|
|
816
|
-
verified,
|
|
817
|
-
...decoded
|
|
818
|
-
};
|
|
817
|
+
return Jwt.verifySignature(token, undefined, verifier);
|
|
819
818
|
}
|
|
820
819
|
/**
|
|
821
820
|
* Verify a token by parts.
|
|
822
|
-
* @param
|
|
823
|
-
* @param payload The payload to verify.
|
|
824
|
-
* @param signature The signature to verify.
|
|
821
|
+
* @param token The token to verify.
|
|
825
822
|
* @param key The key for verifying the token, if not provided no verification occurs.
|
|
826
823
|
* @param verifier Custom verification method.
|
|
827
824
|
* @returns True if the parts are verified.
|
|
828
825
|
*/
|
|
829
|
-
static async verifySignature(
|
|
826
|
+
static async verifySignature(token, key, verifier) {
|
|
827
|
+
core.Guards.stringValue(Jwt._CLASS_NAME, "token", token);
|
|
830
828
|
const hasKey = core.Is.notEmpty(key);
|
|
831
829
|
const hasVerifier = core.Is.notEmpty(verifier);
|
|
832
830
|
if (!hasKey && !hasVerifier) {
|
|
833
831
|
throw new core.GeneralError(Jwt._CLASS_NAME, "noKeyOrVerifier");
|
|
834
832
|
}
|
|
835
|
-
|
|
836
|
-
|
|
837
|
-
core.Is.object(payload) &&
|
|
838
|
-
core.Is.uint8Array(signature) &&
|
|
839
|
-
core.Is.arrayOneOf(header.alg, Object.values(JwtAlgorithms))) {
|
|
840
|
-
const segments = [];
|
|
841
|
-
const headerBytes = core.Converter.utf8ToBytes(JSON.stringify(header));
|
|
842
|
-
segments.push(core.Converter.bytesToBase64Url(headerBytes));
|
|
843
|
-
const payloadBytes = core.Converter.utf8ToBytes(JSON.stringify(payload));
|
|
844
|
-
segments.push(core.Converter.bytesToBase64Url(payloadBytes));
|
|
845
|
-
const jwtHeaderAndPayload = core.Converter.utf8ToBytes(segments.join("."));
|
|
846
|
-
verifier ??= async (alg, k, p, s) => Jwt.defaultVerifier(alg, k, p, s);
|
|
847
|
-
verified = await verifier(header.alg, key, jwtHeaderAndPayload, signature);
|
|
848
|
-
}
|
|
849
|
-
return verified;
|
|
833
|
+
verifier ??= async (t, k) => Jwt.defaultVerifier(t, k);
|
|
834
|
+
return verifier(token, key);
|
|
850
835
|
}
|
|
851
836
|
/**
|
|
852
837
|
* The default signer for the JWT.
|
|
853
|
-
* @param
|
|
854
|
-
* @param key The key to sign with.
|
|
838
|
+
* @param header The header to sign.
|
|
855
839
|
* @param payload The payload to sign.
|
|
840
|
+
* @param key The optional key to sign with.
|
|
856
841
|
* @returns The signature.
|
|
857
842
|
*/
|
|
858
|
-
static async defaultSigner(
|
|
859
|
-
core.Guards.
|
|
860
|
-
core.Guards.
|
|
861
|
-
|
|
862
|
-
|
|
863
|
-
|
|
843
|
+
static async defaultSigner(header, payload, key) {
|
|
844
|
+
core.Guards.object(Jwt._CLASS_NAME, "header", header);
|
|
845
|
+
core.Guards.object(Jwt._CLASS_NAME, "payload", payload);
|
|
846
|
+
core.Guards.defined(Jwt._CLASS_NAME, "key", key);
|
|
847
|
+
const signer = new jose.SignJWT(payload);
|
|
848
|
+
signer.setProtectedHeader(header);
|
|
849
|
+
if (header.alg === "EdDSA" && core.Is.uint8Array(key)) {
|
|
850
|
+
// crypto.subtle.importKey does not support Ed25519 keys in raw format.
|
|
851
|
+
// We need to convert the key to PKCS8 format before importing.
|
|
852
|
+
// The PKCS8 format is the raw key prefixed with the ASN.1 sequence for an Ed25519 private key.
|
|
853
|
+
// The ASN.1 sequence is 48 46 02 01 00 30 05 06 03 2b 65 70 04 20 04 20
|
|
854
|
+
const pkcs8Prefix = new Uint8Array([
|
|
855
|
+
48, 46, 2, 1, 0, 48, 5, 6, 3, 43, 101, 112, 4, 34, 4, 32
|
|
856
|
+
]); // 0x302e020100300506032b657004220420
|
|
857
|
+
const pkcs8PrivateKey = core.Uint8ArrayHelper.concat([pkcs8Prefix, key]);
|
|
858
|
+
const imported = await crypto.subtle.importKey("pkcs8", pkcs8PrivateKey, "Ed25519", false, [
|
|
859
|
+
"sign"
|
|
860
|
+
]);
|
|
861
|
+
return signer.sign(imported);
|
|
864
862
|
}
|
|
865
|
-
return
|
|
863
|
+
return signer.sign(key);
|
|
866
864
|
}
|
|
867
865
|
/**
|
|
868
866
|
* The default verifier for the JWT.
|
|
869
|
-
* @param
|
|
867
|
+
* @param token The token to verify.
|
|
870
868
|
* @param key The key to verify with.
|
|
871
|
-
* @param payload The payload to verify.
|
|
872
|
-
* @param signature The signature to verify.
|
|
873
869
|
* @returns True if the signature was verified.
|
|
874
870
|
*/
|
|
875
|
-
static async defaultVerifier(
|
|
876
|
-
core.Guards.
|
|
877
|
-
core.Guards.
|
|
878
|
-
|
|
879
|
-
|
|
880
|
-
|
|
881
|
-
|
|
882
|
-
|
|
871
|
+
static async defaultVerifier(token, key) {
|
|
872
|
+
core.Guards.stringValue(Jwt._CLASS_NAME, "token", token);
|
|
873
|
+
core.Guards.defined(Jwt._CLASS_NAME, "key", key);
|
|
874
|
+
try {
|
|
875
|
+
const result = await jose.jwtVerify(token, key);
|
|
876
|
+
return {
|
|
877
|
+
header: result.protectedHeader,
|
|
878
|
+
payload: result.payload
|
|
879
|
+
};
|
|
880
|
+
}
|
|
881
|
+
catch (err) {
|
|
882
|
+
throw new core.GeneralError(Jwt._CLASS_NAME, "verifyFailed", undefined, err);
|
|
883
883
|
}
|
|
884
|
-
|
|
884
|
+
}
|
|
885
|
+
/**
|
|
886
|
+
* Create bytes for signing from header and payload.
|
|
887
|
+
* @param header The header.
|
|
888
|
+
* @param payload The payload.
|
|
889
|
+
* @returns The bytes to sign.
|
|
890
|
+
*/
|
|
891
|
+
static createSignBytes(header, payload) {
|
|
892
|
+
const segments = [];
|
|
893
|
+
const headerBytes = core.Converter.utf8ToBytes(JSON.stringify(header));
|
|
894
|
+
segments.push(core.Converter.bytesToBase64Url(headerBytes));
|
|
895
|
+
const payloadBytes = core.Converter.utf8ToBytes(JSON.stringify(payload));
|
|
896
|
+
segments.push(core.Converter.bytesToBase64Url(payloadBytes));
|
|
897
|
+
return core.Converter.utf8ToBytes(segments.join("."));
|
|
898
|
+
}
|
|
899
|
+
/**
|
|
900
|
+
* Create token from bytes and signature.
|
|
901
|
+
* @param signedBytes The signed bytes.
|
|
902
|
+
* @param signature The signature.
|
|
903
|
+
* @returns The token.
|
|
904
|
+
*/
|
|
905
|
+
static createTokenFromBytes(signedBytes, signature) {
|
|
906
|
+
const signedBytesUtf8 = core.Converter.bytesToUtf8(signedBytes);
|
|
907
|
+
const signatureBase64 = core.Converter.bytesToBase64Url(signature);
|
|
908
|
+
return `${signedBytesUtf8}.${signatureBase64}`;
|
|
885
909
|
}
|
|
886
910
|
/**
|
|
887
911
|
* Encode a token.
|
|
@@ -898,19 +922,11 @@ class Jwt {
|
|
|
898
922
|
if (!hasKey && !hasSigner) {
|
|
899
923
|
throw new core.GeneralError(Jwt._CLASS_NAME, "noKeyOrSigner");
|
|
900
924
|
}
|
|
901
|
-
signer ??= async (
|
|
925
|
+
signer ??= async (h, p, k) => Jwt.defaultSigner(h, p, k);
|
|
902
926
|
if (core.Is.undefined(header.typ)) {
|
|
903
927
|
header.typ = "JWT";
|
|
904
928
|
}
|
|
905
|
-
|
|
906
|
-
const headerBytes = core.Converter.utf8ToBytes(JSON.stringify(header));
|
|
907
|
-
segments.push(core.Converter.bytesToBase64Url(headerBytes));
|
|
908
|
-
const payloadBytes = core.Converter.utf8ToBytes(JSON.stringify(payload));
|
|
909
|
-
segments.push(core.Converter.bytesToBase64Url(payloadBytes));
|
|
910
|
-
const jwtHeaderAndPayload = core.Converter.utf8ToBytes(segments.join("."));
|
|
911
|
-
const sigBytes = await signer(header.alg, key, jwtHeaderAndPayload);
|
|
912
|
-
segments.push(core.Converter.bytesToBase64Url(sigBytes));
|
|
913
|
-
return segments.join(".");
|
|
929
|
+
return signer(header, payload, key);
|
|
914
930
|
}
|
|
915
931
|
}
|
|
916
932
|
|
|
@@ -1055,7 +1071,7 @@ exports.FetchHelper = FetchHelper;
|
|
|
1055
1071
|
exports.HeaderTypes = HeaderTypes;
|
|
1056
1072
|
exports.HttpMethod = HttpMethod;
|
|
1057
1073
|
exports.HttpStatusCode = HttpStatusCode;
|
|
1074
|
+
exports.Jwk = Jwk;
|
|
1058
1075
|
exports.Jwt = Jwt;
|
|
1059
|
-
exports.JwtAlgorithms = JwtAlgorithms;
|
|
1060
1076
|
exports.MimeTypeHelper = MimeTypeHelper;
|
|
1061
1077
|
exports.MimeTypes = MimeTypes;
|
package/dist/esm/index.mjs
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
|
-
import { BaseError, StringHelper, Guards, Is, AsyncCache, ObjectHelper,
|
|
2
|
-
import {
|
|
1
|
+
import { BaseError, StringHelper, Guards, Is, AsyncCache, ObjectHelper, GeneralError, Converter, Uint8ArrayHelper } from '@twin.org/core';
|
|
2
|
+
import { importJWK, SignJWT, jwtVerify } from 'jose';
|
|
3
3
|
|
|
4
4
|
// Copyright 2024 IOTA Stiftung.
|
|
5
5
|
// SPDX-License-Identifier: Apache-2.0.
|
|
@@ -343,23 +343,6 @@ const HttpStatusCode = {
|
|
|
343
343
|
networkAuthenticationRequired: 511
|
|
344
344
|
};
|
|
345
345
|
|
|
346
|
-
// Copyright 2024 IOTA Stiftung.
|
|
347
|
-
// SPDX-License-Identifier: Apache-2.0.
|
|
348
|
-
/**
|
|
349
|
-
* The cryptographic algorithms supported for JSON Web Tokens and JSON Web Keys.
|
|
350
|
-
*/
|
|
351
|
-
// eslint-disable-next-line @typescript-eslint/naming-convention
|
|
352
|
-
const JwtAlgorithms = {
|
|
353
|
-
/**
|
|
354
|
-
* HMAC using SHA-256.
|
|
355
|
-
*/
|
|
356
|
-
HS256: "HS256",
|
|
357
|
-
/**
|
|
358
|
-
* EdDSA using Ed25519.
|
|
359
|
-
*/
|
|
360
|
-
EdDSA: "EdDSA"
|
|
361
|
-
};
|
|
362
|
-
|
|
363
346
|
// Copyright 2024 IOTA Stiftung.
|
|
364
347
|
// SPDX-License-Identifier: Apache-2.0.
|
|
365
348
|
/**
|
|
@@ -712,7 +695,34 @@ class FetchHelper {
|
|
|
712
695
|
// Copyright 2024 IOTA Stiftung.
|
|
713
696
|
// SPDX-License-Identifier: Apache-2.0.
|
|
714
697
|
/**
|
|
715
|
-
* Class to
|
|
698
|
+
* Class to handle JSON Web Keys.
|
|
699
|
+
*/
|
|
700
|
+
class Jwk {
|
|
701
|
+
/**
|
|
702
|
+
* Runtime name for the class.
|
|
703
|
+
* @internal
|
|
704
|
+
*/
|
|
705
|
+
static _CLASS_NAME = "Jwk";
|
|
706
|
+
/**
|
|
707
|
+
* Convert the JWK to a crypto key.
|
|
708
|
+
* @param jwk The JWK to convert.
|
|
709
|
+
* @returns The crypto key.
|
|
710
|
+
*/
|
|
711
|
+
static async toCryptoKey(jwk) {
|
|
712
|
+
Guards.object(Jwk._CLASS_NAME, "jwk", jwk);
|
|
713
|
+
try {
|
|
714
|
+
return importJWK(jwk);
|
|
715
|
+
}
|
|
716
|
+
catch (err) {
|
|
717
|
+
throw new GeneralError(Jwk._CLASS_NAME, "jwkImportFailed", undefined, err);
|
|
718
|
+
}
|
|
719
|
+
}
|
|
720
|
+
}
|
|
721
|
+
|
|
722
|
+
// Copyright 2024 IOTA Stiftung.
|
|
723
|
+
// SPDX-License-Identifier: Apache-2.0.
|
|
724
|
+
/**
|
|
725
|
+
* Class to handle JSON Web Tokens.
|
|
716
726
|
*/
|
|
717
727
|
class Jwt {
|
|
718
728
|
/**
|
|
@@ -729,9 +739,9 @@ class Jwt {
|
|
|
729
739
|
*/
|
|
730
740
|
static async encode(header, payload, key) {
|
|
731
741
|
Guards.object(Jwt._CLASS_NAME, "header", header);
|
|
732
|
-
Guards.
|
|
742
|
+
Guards.stringValue(Jwt._CLASS_NAME, "header.alg", header.alg);
|
|
733
743
|
Guards.object(Jwt._CLASS_NAME, "payload", payload);
|
|
734
|
-
Guards.
|
|
744
|
+
Guards.defined(Jwt._CLASS_NAME, "key", key);
|
|
735
745
|
return Jwt.internalEncode(header, payload, key);
|
|
736
746
|
}
|
|
737
747
|
/**
|
|
@@ -743,7 +753,7 @@ class Jwt {
|
|
|
743
753
|
*/
|
|
744
754
|
static async encodeWithSigner(header, payload, signer) {
|
|
745
755
|
Guards.object(Jwt._CLASS_NAME, "header", header);
|
|
746
|
-
Guards.
|
|
756
|
+
Guards.stringValue(Jwt._CLASS_NAME, "header.alg", header.alg);
|
|
747
757
|
Guards.object(Jwt._CLASS_NAME, "payload", payload);
|
|
748
758
|
Guards.function(Jwt._CLASS_NAME, "signer", signer);
|
|
749
759
|
return Jwt.internalEncode(header, payload, undefined, signer);
|
|
@@ -790,13 +800,8 @@ class Jwt {
|
|
|
790
800
|
*/
|
|
791
801
|
static async verify(token, key) {
|
|
792
802
|
Guards.stringValue(Jwt._CLASS_NAME, "token", token);
|
|
793
|
-
Guards.
|
|
794
|
-
|
|
795
|
-
const verified = await Jwt.verifySignature(decoded.header, decoded.payload, decoded.signature, key);
|
|
796
|
-
return {
|
|
797
|
-
verified,
|
|
798
|
-
...decoded
|
|
799
|
-
};
|
|
803
|
+
Guards.defined(Jwt._CLASS_NAME, "key", key);
|
|
804
|
+
return Jwt.verifySignature(token, key);
|
|
800
805
|
}
|
|
801
806
|
/**
|
|
802
807
|
* Verify a token.
|
|
@@ -807,79 +812,98 @@ class Jwt {
|
|
|
807
812
|
static async verifyWithVerifier(token, verifier) {
|
|
808
813
|
Guards.stringValue(Jwt._CLASS_NAME, "token", token);
|
|
809
814
|
Guards.function(Jwt._CLASS_NAME, "verifier", verifier);
|
|
810
|
-
|
|
811
|
-
const decoded = await Jwt.decode(token);
|
|
812
|
-
const verified = await Jwt.verifySignature(decoded.header, decoded.payload, decoded.signature, undefined, verifier);
|
|
813
|
-
return {
|
|
814
|
-
verified,
|
|
815
|
-
...decoded
|
|
816
|
-
};
|
|
815
|
+
return Jwt.verifySignature(token, undefined, verifier);
|
|
817
816
|
}
|
|
818
817
|
/**
|
|
819
818
|
* Verify a token by parts.
|
|
820
|
-
* @param
|
|
821
|
-
* @param payload The payload to verify.
|
|
822
|
-
* @param signature The signature to verify.
|
|
819
|
+
* @param token The token to verify.
|
|
823
820
|
* @param key The key for verifying the token, if not provided no verification occurs.
|
|
824
821
|
* @param verifier Custom verification method.
|
|
825
822
|
* @returns True if the parts are verified.
|
|
826
823
|
*/
|
|
827
|
-
static async verifySignature(
|
|
824
|
+
static async verifySignature(token, key, verifier) {
|
|
825
|
+
Guards.stringValue(Jwt._CLASS_NAME, "token", token);
|
|
828
826
|
const hasKey = Is.notEmpty(key);
|
|
829
827
|
const hasVerifier = Is.notEmpty(verifier);
|
|
830
828
|
if (!hasKey && !hasVerifier) {
|
|
831
829
|
throw new GeneralError(Jwt._CLASS_NAME, "noKeyOrVerifier");
|
|
832
830
|
}
|
|
833
|
-
|
|
834
|
-
|
|
835
|
-
Is.object(payload) &&
|
|
836
|
-
Is.uint8Array(signature) &&
|
|
837
|
-
Is.arrayOneOf(header.alg, Object.values(JwtAlgorithms))) {
|
|
838
|
-
const segments = [];
|
|
839
|
-
const headerBytes = Converter.utf8ToBytes(JSON.stringify(header));
|
|
840
|
-
segments.push(Converter.bytesToBase64Url(headerBytes));
|
|
841
|
-
const payloadBytes = Converter.utf8ToBytes(JSON.stringify(payload));
|
|
842
|
-
segments.push(Converter.bytesToBase64Url(payloadBytes));
|
|
843
|
-
const jwtHeaderAndPayload = Converter.utf8ToBytes(segments.join("."));
|
|
844
|
-
verifier ??= async (alg, k, p, s) => Jwt.defaultVerifier(alg, k, p, s);
|
|
845
|
-
verified = await verifier(header.alg, key, jwtHeaderAndPayload, signature);
|
|
846
|
-
}
|
|
847
|
-
return verified;
|
|
831
|
+
verifier ??= async (t, k) => Jwt.defaultVerifier(t, k);
|
|
832
|
+
return verifier(token, key);
|
|
848
833
|
}
|
|
849
834
|
/**
|
|
850
835
|
* The default signer for the JWT.
|
|
851
|
-
* @param
|
|
852
|
-
* @param key The key to sign with.
|
|
836
|
+
* @param header The header to sign.
|
|
853
837
|
* @param payload The payload to sign.
|
|
838
|
+
* @param key The optional key to sign with.
|
|
854
839
|
* @returns The signature.
|
|
855
840
|
*/
|
|
856
|
-
static async defaultSigner(
|
|
857
|
-
Guards.
|
|
858
|
-
Guards.
|
|
859
|
-
|
|
860
|
-
|
|
861
|
-
|
|
841
|
+
static async defaultSigner(header, payload, key) {
|
|
842
|
+
Guards.object(Jwt._CLASS_NAME, "header", header);
|
|
843
|
+
Guards.object(Jwt._CLASS_NAME, "payload", payload);
|
|
844
|
+
Guards.defined(Jwt._CLASS_NAME, "key", key);
|
|
845
|
+
const signer = new SignJWT(payload);
|
|
846
|
+
signer.setProtectedHeader(header);
|
|
847
|
+
if (header.alg === "EdDSA" && Is.uint8Array(key)) {
|
|
848
|
+
// crypto.subtle.importKey does not support Ed25519 keys in raw format.
|
|
849
|
+
// We need to convert the key to PKCS8 format before importing.
|
|
850
|
+
// The PKCS8 format is the raw key prefixed with the ASN.1 sequence for an Ed25519 private key.
|
|
851
|
+
// The ASN.1 sequence is 48 46 02 01 00 30 05 06 03 2b 65 70 04 20 04 20
|
|
852
|
+
const pkcs8Prefix = new Uint8Array([
|
|
853
|
+
48, 46, 2, 1, 0, 48, 5, 6, 3, 43, 101, 112, 4, 34, 4, 32
|
|
854
|
+
]); // 0x302e020100300506032b657004220420
|
|
855
|
+
const pkcs8PrivateKey = Uint8ArrayHelper.concat([pkcs8Prefix, key]);
|
|
856
|
+
const imported = await crypto.subtle.importKey("pkcs8", pkcs8PrivateKey, "Ed25519", false, [
|
|
857
|
+
"sign"
|
|
858
|
+
]);
|
|
859
|
+
return signer.sign(imported);
|
|
862
860
|
}
|
|
863
|
-
return
|
|
861
|
+
return signer.sign(key);
|
|
864
862
|
}
|
|
865
863
|
/**
|
|
866
864
|
* The default verifier for the JWT.
|
|
867
|
-
* @param
|
|
865
|
+
* @param token The token to verify.
|
|
868
866
|
* @param key The key to verify with.
|
|
869
|
-
* @param payload The payload to verify.
|
|
870
|
-
* @param signature The signature to verify.
|
|
871
867
|
* @returns True if the signature was verified.
|
|
872
868
|
*/
|
|
873
|
-
static async defaultVerifier(
|
|
874
|
-
Guards.
|
|
875
|
-
Guards.
|
|
876
|
-
|
|
877
|
-
|
|
878
|
-
|
|
879
|
-
|
|
880
|
-
|
|
869
|
+
static async defaultVerifier(token, key) {
|
|
870
|
+
Guards.stringValue(Jwt._CLASS_NAME, "token", token);
|
|
871
|
+
Guards.defined(Jwt._CLASS_NAME, "key", key);
|
|
872
|
+
try {
|
|
873
|
+
const result = await jwtVerify(token, key);
|
|
874
|
+
return {
|
|
875
|
+
header: result.protectedHeader,
|
|
876
|
+
payload: result.payload
|
|
877
|
+
};
|
|
878
|
+
}
|
|
879
|
+
catch (err) {
|
|
880
|
+
throw new GeneralError(Jwt._CLASS_NAME, "verifyFailed", undefined, err);
|
|
881
881
|
}
|
|
882
|
-
|
|
882
|
+
}
|
|
883
|
+
/**
|
|
884
|
+
* Create bytes for signing from header and payload.
|
|
885
|
+
* @param header The header.
|
|
886
|
+
* @param payload The payload.
|
|
887
|
+
* @returns The bytes to sign.
|
|
888
|
+
*/
|
|
889
|
+
static createSignBytes(header, payload) {
|
|
890
|
+
const segments = [];
|
|
891
|
+
const headerBytes = Converter.utf8ToBytes(JSON.stringify(header));
|
|
892
|
+
segments.push(Converter.bytesToBase64Url(headerBytes));
|
|
893
|
+
const payloadBytes = Converter.utf8ToBytes(JSON.stringify(payload));
|
|
894
|
+
segments.push(Converter.bytesToBase64Url(payloadBytes));
|
|
895
|
+
return Converter.utf8ToBytes(segments.join("."));
|
|
896
|
+
}
|
|
897
|
+
/**
|
|
898
|
+
* Create token from bytes and signature.
|
|
899
|
+
* @param signedBytes The signed bytes.
|
|
900
|
+
* @param signature The signature.
|
|
901
|
+
* @returns The token.
|
|
902
|
+
*/
|
|
903
|
+
static createTokenFromBytes(signedBytes, signature) {
|
|
904
|
+
const signedBytesUtf8 = Converter.bytesToUtf8(signedBytes);
|
|
905
|
+
const signatureBase64 = Converter.bytesToBase64Url(signature);
|
|
906
|
+
return `${signedBytesUtf8}.${signatureBase64}`;
|
|
883
907
|
}
|
|
884
908
|
/**
|
|
885
909
|
* Encode a token.
|
|
@@ -896,19 +920,11 @@ class Jwt {
|
|
|
896
920
|
if (!hasKey && !hasSigner) {
|
|
897
921
|
throw new GeneralError(Jwt._CLASS_NAME, "noKeyOrSigner");
|
|
898
922
|
}
|
|
899
|
-
signer ??= async (
|
|
923
|
+
signer ??= async (h, p, k) => Jwt.defaultSigner(h, p, k);
|
|
900
924
|
if (Is.undefined(header.typ)) {
|
|
901
925
|
header.typ = "JWT";
|
|
902
926
|
}
|
|
903
|
-
|
|
904
|
-
const headerBytes = Converter.utf8ToBytes(JSON.stringify(header));
|
|
905
|
-
segments.push(Converter.bytesToBase64Url(headerBytes));
|
|
906
|
-
const payloadBytes = Converter.utf8ToBytes(JSON.stringify(payload));
|
|
907
|
-
segments.push(Converter.bytesToBase64Url(payloadBytes));
|
|
908
|
-
const jwtHeaderAndPayload = Converter.utf8ToBytes(segments.join("."));
|
|
909
|
-
const sigBytes = await signer(header.alg, key, jwtHeaderAndPayload);
|
|
910
|
-
segments.push(Converter.bytesToBase64Url(sigBytes));
|
|
911
|
-
return segments.join(".");
|
|
927
|
+
return signer(header, payload, key);
|
|
912
928
|
}
|
|
913
929
|
}
|
|
914
930
|
|
|
@@ -1048,4 +1064,4 @@ class MimeTypeHelper {
|
|
|
1048
1064
|
}
|
|
1049
1065
|
}
|
|
1050
1066
|
|
|
1051
|
-
export { FetchError, FetchHelper, HeaderTypes, HttpMethod, HttpStatusCode,
|
|
1067
|
+
export { FetchError, FetchHelper, HeaderTypes, HttpMethod, HttpStatusCode, Jwk, Jwt, MimeTypeHelper, MimeTypes };
|
package/dist/types/index.d.ts
CHANGED
|
@@ -7,8 +7,9 @@ export * from "./models/IHttpHeaders";
|
|
|
7
7
|
export * from "./models/IJwk";
|
|
8
8
|
export * from "./models/IJwtHeader";
|
|
9
9
|
export * from "./models/IJwtPayload";
|
|
10
|
-
export * from "./models/
|
|
10
|
+
export * from "./models/jwkCryptoKey";
|
|
11
11
|
export * from "./models/mimeTypes";
|
|
12
12
|
export * from "./utils/fetchHelper";
|
|
13
|
+
export * from "./utils/jwk";
|
|
13
14
|
export * from "./utils/jwt";
|
|
14
15
|
export * from "./utils/mimeTypeHelper";
|
|
@@ -1,62 +1,6 @@
|
|
|
1
|
-
import type {
|
|
1
|
+
import type { JWK } from "jose";
|
|
2
2
|
/**
|
|
3
3
|
* The fields in a JSON Web Key.
|
|
4
4
|
*/
|
|
5
|
-
export interface IJwk {
|
|
6
|
-
/**
|
|
7
|
-
* Additional fields in the key.
|
|
8
|
-
*/
|
|
9
|
-
[key: string]: unknown;
|
|
10
|
-
/**
|
|
11
|
-
* The cryptographic algorithm for the key.
|
|
12
|
-
*/
|
|
13
|
-
alg?: JwtAlgorithms;
|
|
14
|
-
/**
|
|
15
|
-
* The intended use for the key.
|
|
16
|
-
*/
|
|
17
|
-
use?: string;
|
|
18
|
-
/**
|
|
19
|
-
* The operation(s) that the key is intended to be used for.
|
|
20
|
-
*/
|
|
21
|
-
key_ops?: string[];
|
|
22
|
-
/**
|
|
23
|
-
* The key type parameter.
|
|
24
|
-
*/
|
|
25
|
-
kty: string;
|
|
26
|
-
/**
|
|
27
|
-
* The public key parameters.
|
|
28
|
-
*/
|
|
29
|
-
n?: string;
|
|
30
|
-
/**
|
|
31
|
-
* Exponent parameter.
|
|
32
|
-
*/
|
|
33
|
-
e?: string;
|
|
34
|
-
/**
|
|
35
|
-
* The private key parameters.
|
|
36
|
-
*/
|
|
37
|
-
d?: string;
|
|
38
|
-
/**
|
|
39
|
-
* The private key parameters.
|
|
40
|
-
*/
|
|
41
|
-
p?: string;
|
|
42
|
-
/**
|
|
43
|
-
* The private key parameters.
|
|
44
|
-
*/
|
|
45
|
-
q?: string;
|
|
46
|
-
/**
|
|
47
|
-
* The private key parameters.
|
|
48
|
-
*/
|
|
49
|
-
dp?: string;
|
|
50
|
-
/**
|
|
51
|
-
* The private key parameters.
|
|
52
|
-
*/
|
|
53
|
-
dq?: string;
|
|
54
|
-
/**
|
|
55
|
-
* The private key parameters.
|
|
56
|
-
*/
|
|
57
|
-
qi?: string;
|
|
58
|
-
/**
|
|
59
|
-
* The key ID.
|
|
60
|
-
*/
|
|
61
|
-
kid?: string;
|
|
5
|
+
export interface IJwk extends JWK {
|
|
62
6
|
}
|
|
@@ -1,22 +1,6 @@
|
|
|
1
|
-
import type {
|
|
1
|
+
import type { JWTHeaderParameters } from "jose";
|
|
2
2
|
/**
|
|
3
3
|
* The fields in a JSON Web Token header.
|
|
4
4
|
*/
|
|
5
|
-
export interface IJwtHeader {
|
|
6
|
-
/**
|
|
7
|
-
* Additional fields in the header.
|
|
8
|
-
*/
|
|
9
|
-
[key: string]: unknown;
|
|
10
|
-
/**
|
|
11
|
-
* The type of the token.
|
|
12
|
-
*/
|
|
13
|
-
typ?: string;
|
|
14
|
-
/**
|
|
15
|
-
* The algorithm used to sign the token.
|
|
16
|
-
*/
|
|
17
|
-
alg: JwtAlgorithms;
|
|
18
|
-
/**
|
|
19
|
-
* The key ID.
|
|
20
|
-
*/
|
|
21
|
-
kid?: string;
|
|
5
|
+
export interface IJwtHeader extends JWTHeaderParameters {
|
|
22
6
|
}
|