diff --git a/src/enums.ts b/src/enums.ts index 6455334..129bc94 100644 --- a/src/enums.ts +++ b/src/enums.ts @@ -18,13 +18,14 @@ export enum JwtAlgorithmsEnum { ES256K = "ES256K", Ed25519 = "Ed25519", X25519 = "X25519", + Ed448 = "Ed448", + X448 = "X448", // web crypto RSA_PKCS1 = "RSASSA-PKCS1-v1_5", RSA_PSS = "RSA-PSS", // similar to RSAPSS EC_P256 = "PS256", EC_P384 = "PS384", - EC_P521 = "PS512", - + EC_P521 = "PS512" } // https://datatracker.ietf.org/doc/html/rfc7517#section-4.2 diff --git a/src/errors/messages.ts b/src/errors/messages.ts index 13d5c10..d9dbe05 100644 --- a/src/errors/messages.ts +++ b/src/errors/messages.ts @@ -34,4 +34,5 @@ export const INVALID_SCOPE_FIELD_TYPE = "Invalid scp field type"; export const INVALID_PEM_STRING = "Invalid PEM string"; -export const INVALID_PUBLIC_KEY_FORMAT = "Invalid public key format (must be PEM, JWK, adhoc JWks or JWKs URI)" \ No newline at end of file +export const INVALID_PUBLIC_KEY_FORMAT = + "Invalid public key format (must be PEM, JWK, adhoc JWks or JWKs URI)"; diff --git a/src/vendors/jwks/jwks.test.ts b/src/vendors/jwks/jwks.test.ts index a8d3bf9..4772781 100644 --- a/src/vendors/jwks/jwks.test.ts +++ b/src/vendors/jwks/jwks.test.ts @@ -9,6 +9,7 @@ import { JwtAlgorithmsEnum as Algs, JwtKeyTypes as Kty } from "../../enums"; import { default as nock } from "nock"; import * as c from "../../constants"; +import { SignJWT, jwtVerify } from "jose"; const AUTHDOG_API_ROOT = "https://api.authdog.xyz"; it("verifies token with public key - es256k", async () => { @@ -396,7 +397,7 @@ it("verifies token with public key - EdDSA", async () => { }); }); -it("THROWS an error: verifies token with public key - ES256k / pem", async () => { +it("verifies token with public key - ES256k / pem", async () => { const keyPairES256k = await getKeyPair({ keyFormat: "pem", algorithmIdentifier: Algs.ES256K, @@ -423,6 +424,69 @@ it("THROWS an error: verifies token with public key - ES256k / pem", async () => ).toBeTruthy(); }); +// Ed25519 is the EdDSA signature scheme using SHA-512 (SHA-2) and Curve25519 +it("signs with Ed25519 key pair", async () => { + const crypto = require("crypto"); + const { publicKey, privateKey } = crypto.generateKeyPairSync("ed25519"); + + expect(publicKey).toBeTruthy(); + expect(privateKey).toBeTruthy(); + + const protectedHeaders = { + alg: "EdDSA", + typ: "JWT" + }; + + const payload = { + urn: "urn:test:test" + }; + + const jwt = await new SignJWT(payload) + .setProtectedHeader(protectedHeaders) + .sign(privateKey); + + expect(jwt).toBeTruthy(); + + const verifiedPayload = await jwtVerify(jwt, publicKey); + + expect(verifiedPayload).toBeTruthy(); + + expect(verifiedPayload?.payload).toMatchObject(payload); + expect(verifiedPayload?.protectedHeader).toMatchObject(protectedHeaders); + +}); +it("verifies Ed448 Key pair", async () => { + const crypto = require("crypto"); + const { publicKey, privateKey } = crypto.generateKeyPairSync("ed448"); + expect(publicKey).toBeTruthy(); + expect(privateKey).toBeTruthy(); + + const protectedHeaders = { + alg: "EdDSA", // Ed448 uses the EdDSA algorithm + typ: "JWT" + }; + + const payload = { + urn: "urn:test:test" + }; + + const jwt = await new SignJWT(payload) + .setProtectedHeader(protectedHeaders) + .sign(privateKey); + + expect(jwt).toBeTruthy(); + + const verifiedPayload = await jwtVerify(jwt, publicKey); + + expect(verifiedPayload).toBeTruthy(); + + expect(verifiedPayload?.payload).toMatchObject(payload); + expect(verifiedPayload?.protectedHeader).toMatchObject(protectedHeaders); +}); + + + + it("verifies correctly token with public uri", async () => { const tenantUuid2 = "d84ddef4-81dd-4ce6-9594-03ac52cac367"; const applicationUuid2 = "b867db48-4e11-4cae-bb03-086dc97c8ddd"; diff --git a/src/vendors/jwks/jwks.ts b/src/vendors/jwks/jwks.ts index c9932d1..09d2362 100644 --- a/src/vendors/jwks/jwks.ts +++ b/src/vendors/jwks/jwks.ts @@ -6,7 +6,7 @@ import { JWK } from "jose"; import { extractAlgFromJwtHeader } from "../jwt"; -import {INVALID_PUBLIC_KEY_FORMAT} from "../../errors/messages" +import { INVALID_PUBLIC_KEY_FORMAT } from "../../errors/messages"; export interface IJwksClient { jwksUri?: string; // required for RS256 @@ -106,10 +106,10 @@ export const verifyTokenWithPublicKey = async ( let adhocKeys = opts?.adhoc || adhocJwks; JWKS = createLocalJWKSet({ - keys: adhocKeys ? adhocKeys: [jwk] + keys: adhocKeys ? adhocKeys : [jwk] }); } else if (opts?.jwksUri) { - JWKS = createRemoteJWKSet(new URL(opts?.jwksUri)) + JWKS = createRemoteJWKSet(new URL(opts?.jwksUri)); } else { throw new Error(INVALID_PUBLIC_KEY_FORMAT); } diff --git a/src/vendors/jwt/jwt-verify.ts b/src/vendors/jwt/jwt-verify.ts index 1cd0a3e..9e2faa2 100644 --- a/src/vendors/jwt/jwt-verify.ts +++ b/src/vendors/jwt/jwt-verify.ts @@ -39,7 +39,7 @@ export const checkTokenValidness = async ( requiredScopes, publicKey }: IcheckTokenValidnessCredentials -): Promise => { +): Promise => { const algorithm = getAlgorithmJwt(token); const missingCredentials = []; let extractedPayload: ITokenExtractedWithPubKey | any = null; @@ -139,7 +139,7 @@ export const verifyHSTokenWithSecretString = async ( } } catch (e) {} - return isVerified ? decoded?.payload: null; + return isVerified ? decoded?.payload : null; }; export const checkJwtFields = ( @@ -316,6 +316,6 @@ export const extractAlgFromJwtHeader = (jwt: string) => { // Split the JWT into its three parts: header, payload, and signature const parts = jwt.split("."); const headerJson = atob(parts[0]); - const {alg} = JSON.parse(headerJson); + const { alg } = JSON.parse(headerJson); return alg; };