diff --git a/.gitignore b/.gitignore index fd345b3..5e2421f 100644 --- a/.gitignore +++ b/.gitignore @@ -1,4 +1,6 @@ node_modules dist/ lib/ -*.tgz \ No newline at end of file +*.tgz +.yarn/ +yarn.lock diff --git a/.npmignore b/.npmignore index a001f15..2f9ebc2 100644 --- a/.npmignore +++ b/.npmignore @@ -6,4 +6,4 @@ tsconfig.json yarn.lock .npmignore rollup.config.js -jest.config.js +jest.config.cjs diff --git a/.yarnrc.yml b/.yarnrc.yml new file mode 100644 index 0000000..3186f3f --- /dev/null +++ b/.yarnrc.yml @@ -0,0 +1 @@ +nodeLinker: node-modules diff --git a/jest.config.js b/jest.config.cjs similarity index 100% rename from jest.config.js rename to jest.config.cjs diff --git a/package.json b/package.json index 71ec387..a07396a 100644 --- a/package.json +++ b/package.json @@ -8,21 +8,22 @@ "repository": "git@github.com:keratin/authn-node.git", "author": "Lance Ivy ", "license": "LGPL-3.0-only", + "type": "module", "devDependencies": { - "@types/jest": "^27.0.2", - "@types/jsonwebtoken": "^8.5.5", - "@types/node-rsa": "^1.1.1", - "jest": "^27.2.5", + "@types/jest": "^29.5.12", + "@types/jsonwebtoken": "^9.0.6", + "@types/node-rsa": "^1.1.4", + "jest": "^29.7.0", "node-rsa": "^1.1.1", - "prettier": "^2.4.1", - "rollup": "^2.58.0", - "ts-jest": "^27.0.7", - "typescript": "^4.4.4" + "prettier": "^3.2.5", + "rollup": "^4.13.0", + "ts-jest": "^29.1.2", + "typescript": "^5.4.3" }, "dependencies": { - "axios": "^0.21.1", - "jsonwebtoken": "^8.5.1", - "jwks-rsa": "^1.12.1" + "axios": "^1.6.8", + "jsonwebtoken": "^9.0.2", + "jwks-rsa": "^3.1.0" }, "scripts": { "build": "yarn tsc && rollup -c && cp lib/*.d.ts dist", @@ -31,5 +32,6 @@ "problems": "yarn tsc --noEmit && yarn lint:format", "release": "yarn problems && yarn test && yarn build && git push && yarn publish --access public && git push --tags", "test": "jest" - } + }, + "packageManager": "yarn@4.1.1" } diff --git a/src/Client.ts b/src/Client.ts index a3e0b7a..62bb236 100644 --- a/src/Client.ts +++ b/src/Client.ts @@ -1,5 +1,5 @@ -import TokenVerifier, { VerifyToken } from "./TokenVerifier"; -import Keychain from "./Keychain"; +import TokenVerifier, { VerifyToken } from "./TokenVerifier.js"; +import Keychain from "./Keychain.js"; import axios, { AxiosRequestConfig } from "axios"; interface ClientConfig { diff --git a/src/Keychain.ts b/src/Keychain.ts index 8533c46..83791bf 100644 --- a/src/Keychain.ts +++ b/src/Keychain.ts @@ -19,7 +19,7 @@ const Keychain = (config: Config): GetKey => { return async (header) => await new Promise((resolve, reject) => { client.getSigningKey(header.kid || "unknown", function (err, key) { - err ? reject(err) : resolve(key.getPublicKey()); + err ? reject(err) : resolve(key ? key.getPublicKey(): ""); }); }); }; diff --git a/src/TokenVerifier.test.ts b/src/TokenVerifier.test.ts index 843177d..c1f0ffa 100644 --- a/src/TokenVerifier.test.ts +++ b/src/TokenVerifier.test.ts @@ -3,7 +3,7 @@ import { JsonWebTokenError, sign } from "jsonwebtoken"; import Key from "node-rsa"; describe("TokenVerifier", () => { - const key = new Key({ b: 512 }); + const key = new Key({ b: 2048 }); const verifier = TokenVerifier({ issuer: "authn.example.com", audiences: ["myapp.example.com"], @@ -34,14 +34,14 @@ describe("TokenVerifier", () => { }); test("with unsigned JWT", async () => { - const token = jwt(key, {}, "none"); + const token = jwt(null, {}, "none"); await expect(verifier(token)).rejects.toEqual( new JsonWebTokenError("jwt signature is required") ); }); test("with JWT signed by unknown keypair", async () => { - const unknownKey = new Key({ b: 512 }); + const unknownKey = new Key({ b: 2048 }); const token = jwt(unknownKey); await expect(verifier(token)).rejects.toEqual( new JsonWebTokenError("invalid signature") @@ -90,13 +90,22 @@ describe("TokenVerifier", () => { }); test("with tampered alg=hmac JWT", async () => { - const token = jwt(key, {}, "HS256"); + const token = jwt(makeString(2048), {}, "HS256"); await expect(verifier(token)).rejects.toEqual( new JsonWebTokenError("invalid algorithm") ); }); }); +const makeString = (len: number): string => { + let outString: string = ''; + let inOptions: string = 'abcdefghijklmnopqrstuvwxyz0123456789'; + for (let i = 0; i < len; i++) { + outString += inOptions.charAt(Math.floor(Math.random() * inOptions.length)); + } + return outString; +} + const secondsFromNow = (seconds: number): number => Math.floor(Date.now() / 1000) + seconds * 60; @@ -109,7 +118,7 @@ interface Claims { } const jwt = ( - key: Key, + key: Key | string | null, claims: Partial = {}, algorithm: "RS256" | "HS256" | "none" = "RS256" ): string => @@ -122,7 +131,7 @@ const jwt = ( iat: secondsFromNow(-60), ...claims, }, - key.exportKey("pkcs1-private-pem"), + (key && key instanceof Key) ? key.exportKey("pkcs1-private-pem"): (!key ? "": key), { algorithm } ); diff --git a/src/TokenVerifier.ts b/src/TokenVerifier.ts index b7cf2b4..7e3ba0d 100644 --- a/src/TokenVerifier.ts +++ b/src/TokenVerifier.ts @@ -1,5 +1,5 @@ import { verify } from "jsonwebtoken"; -import { GetKey } from "./Keychain"; +import { GetKey } from "./Keychain.js"; interface Config { issuer: string; diff --git a/src/index.ts b/src/index.ts index 80c8ce4..c651efe 100644 --- a/src/index.ts +++ b/src/index.ts @@ -1,3 +1,3 @@ -import AuthN from "./Client"; +import AuthN from "./Client.js"; export { AuthN }; export { JsonWebTokenError } from "jsonwebtoken"; diff --git a/tsconfig.json b/tsconfig.json index a10c5ae..6a0f56a 100644 --- a/tsconfig.json +++ b/tsconfig.json @@ -1,8 +1,8 @@ { "compilerOptions": { - "module": "es2015", - "moduleResolution": "node", - "target": "es6", + "module": "NodeNext", + "moduleResolution": "NodeNext", + "target": "es2022", "esModuleInterop": true, "allowSyntheticDefaultImports": true, "sourceMap": true, @@ -11,11 +11,16 @@ /* checks */ "strict": true, + "skipLibCheck": true, "noUnusedLocals": true, "noUnusedParameters": true, "noImplicitReturns": true, "noFallthroughCasesInSwitch": true, "forceConsistentCasingInFileNames": true }, - "include": ["src/**/*"] + "include": ["src/**/*"], + "exclude": [ + "src/**/*.spec.ts", + "src/**/*.test.ts" + ] }