teamdigitale/italia-ts-commons

View on GitHub
src/__tests__/jwk.test.ts

Summary

Maintainability
D
1 day
Test Coverage
import { JwkPublicKey, JwkPublicKeyFromToken, parseJwkOrError } from "../jwk";
import * as jose from "jose";
import * as E from "fp-ts/lib/Either";

const algorithm = "ES256";
const spki = `-----BEGIN PUBLIC KEY-----
MFYwEAYHKoZIzj0CAQYFK4EEAAoDQgAEQ8K81dZcC4DdKl52iW7bT0ubXXm2amN8
35M/v5AgpSGUuzDzZDjXjM9Y+W4jkGZ0ocrpdsV+KdzxpGptkIS/QA==
-----END PUBLIC KEY-----`;

const getJwkToken = () => jose.importSPKI(spki, algorithm);

const aJwkPublicKey: JwkPublicKey = {
  kty: "EC",
  crv: "crv",
  x: "x",
  y: "y"
};

const aRsaJwkPublicKey: JwkPublicKey = {
  kty: "RSA",
  alg: "alg",
  e: "e",
  n: "n"
};
describe("parseJwkOrError", () => {
  it("should parse a valid jwk token", async () => {
    const jwkToken = await getJwkToken();
    const jwk = await jose.exportJWK(jwkToken);
    const result = parseJwkOrError(jose.base64url.encode(JSON.stringify(jwk)));
    expect(E.isRight(result)).toBeTruthy();
    if (E.isRight(result)) {
      expect(result.right).toEqual(jwk);
    }
  });

  it("should return an error if jwk is empty", async () => {
    const result = parseJwkOrError("");
    expect(E.isLeft(result)).toBeTruthy();
    if (E.isLeft(result)) {
      expect(result.left.name).toContain("Error");
    }
  });

  it("should return an error if token contains non base64 chars", async () => {
    const result = parseJwkOrError(" # ");
    expect(E.isLeft(result)).toBeTruthy();
    if (E.isLeft(result)) {
      expect(result.left.name).toContain("Error");
      expect(result.left.message).toEqual("Cannot decode JWK Base64");
    }
  });

  it("should return an error if token is not well formed", async () => {
    const result = parseJwkOrError("e2E6ICJCIn0=");
    expect(E.isLeft(result)).toBeTruthy();
    if (E.isLeft(result)) {
      expect(result.left.name).toContain("Error");
      expect(result.left.message).toEqual("Cannot parse JWK to JSON format");
    }
  });
});

describe("JwkPublicKeyFromToken", () => {
  it("should decode a correct input", async () => {
    const jwkToken = await getJwkToken();
    const jwk = await jose.exportJWK(jwkToken);
    const result = JwkPublicKeyFromToken.decode(
      jose.base64url.encode(JSON.stringify(jwk))
    );
    expect(E.isRight(result)).toBeTruthy();
    if (E.isRight(result)) {
      expect(result.right).toEqual(jwk);
    }
  });

  it("should NOT decode a wrong input", async () => {
    const jwkToken = await getJwkToken();
    const jwk = await jose.exportJWK(jwkToken);
    const result = JwkPublicKeyFromToken.decode(
      jose.base64url.encode(JSON.stringify({ ...jwk, kty: -1 }))
    );
    expect(E.isLeft(result)).toBeTruthy();
    if (E.isLeft(result)) {
      expect(result.left).toEqual(
        expect.arrayContaining([
          expect.objectContaining({
            message: expect.stringContaining("is not a valid [JwkPublicKey]")
          })
        ])
      );
    }
  });

  it("should decode if a JWKPubKey object as input", () => {
    const result = JwkPublicKeyFromToken.decode(aJwkPublicKey);
    expect(E.isRight(result)).toBeTruthy();
    if (E.isRight(result)) expect(result.right).toEqual(aJwkPublicKey);
  });

  it("should encode a correct input", async () => {
    const jwkToken = await getJwkToken();
    const jwk = await jose.exportJWK(jwkToken);
    const result = JwkPublicKeyFromToken.encode(jwk as JwkPublicKey);
    expect(result).toStrictEqual(jose.base64url.encode(JSON.stringify(jwk)));
  });

  it("should NOT encode a wrong input", async () => {
    const circular: any = { ref: null };
    circular.ref = circular;
    // JSON stringify only throws when argument is an overflowed bigint or a circular structure
    expect(() => JwkPublicKeyFromToken.encode(circular)).toThrow();
  });

  it("should guard correctly a correct input", async () => {
    const jwkToken = await getJwkToken();
    const jwk = await jose.exportJWK(jwkToken);
    const result = JwkPublicKeyFromToken.is(
      jose.base64url.encode(JSON.stringify(jwk))
    );
    expect(result).toBeTruthy();
  });

  it("should guard correctly a wrong input", async () => {
    const result = JwkPublicKeyFromToken.is("");
    expect(result).toBeFalsy();
  });

  it("should guard if a JWKPubKey object as input", () => {
    const result = JwkPublicKeyFromToken.is(aJwkPublicKey);
    expect(result).toBeTruthy();
  });
});

describe("JwkPublicKey", () => {
  it("should decode an ECKey removing extra properties", () => {
    const result = JwkPublicKey.decode({
      ...aJwkPublicKey,
      otherProp: "other"
    });
    expect(E.isRight(result)).toBeTruthy();
    if (E.isRight(result)) {
      expect(result.right).toStrictEqual(aJwkPublicKey);
    }
  });

  it("should decode a RSAKey removing extra properties", () => {
    const result = JwkPublicKey.decode({
      ...aRsaJwkPublicKey,
      otherProp: "other"
    });
    expect(E.isRight(result)).toBeTruthy();
    if (E.isRight(result)) {
      expect(result.right).toStrictEqual(aRsaJwkPublicKey);
    }
  });
});