Skip to content

Commit

Permalink
initial security package
Browse files Browse the repository at this point in the history
  • Loading branch information
DylanBulmer committed Nov 13, 2023
1 parent 00270da commit 68649bf
Show file tree
Hide file tree
Showing 8 changed files with 256 additions and 8 deletions.
28 changes: 26 additions & 2 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@

## Purpose

This package should be used in all of Codr's microservices to ensure strict authentication verification is implemented.
This package should be used in all of Codr's microservices to ensure strict authentication verification is implemented.

## Getting started

Expand All @@ -15,9 +15,33 @@ Install the package from the npm registry.
yarn add @codrjs/security
```

```js
// expected usage:
import { JwtPayload } from "jsonwebtoken";
import JwtSecurity from "@codrjs/security";

// Create's an instance of the JwtSecurity class. Use this as a singleton.
const jwtSecurity = new JwtSecurity({ aud: "codr", iss: "https://codrml.com" });

// Creating and verifying a token.
const token = jwtSecurity.sign("subject-id", { permissions: ["CREATE:ANNOTATION"] });
const decoded = jwtSecurity.verify(token) as JwtPayload;

console.log(decoded.permissions);
// output ["CREATE:ANNOTATION"]

// Rotate signing keys, important for secuity mishaps.
// `-> Keys should be rotated often, at least once a month, preferrably at least once a week.
jwtSecurity.rotate();

// The authentication service show have these values exposed for other services to verify signed tokens.
jwtSecurity.keyId;
jwtSecurity.publicKey;
```

## TODO

- [ ]
- [ ]

## Contributing

Expand Down
14 changes: 10 additions & 4 deletions package.json
Original file line number Diff line number Diff line change
@@ -1,11 +1,11 @@
{
"name": "@codrjs/template",
"name": "@codrjs/security",
"version": "1.0.0",
"description": "",
"description": "Codr's security package to be used in all microservices.",
"main": "./cjs/index.js",
"module": "./esm/index.js",
"types": "./types/index.d.ts",
"repository": "[email protected]:CodrJS/ts-npm-template.git",
"repository": "[email protected]:CodrJS/security.git",
"author": "Dylan Bulmer <[email protected]>",
"license": "MIT",
"type": "module",
Expand Down Expand Up @@ -35,7 +35,9 @@
"@swc/core": "^1.3.22",
"@swc/jest": "^0.2.24",
"@types/jest": "^29.0.3",
"@types/jsonwebtoken": "^9.0.5",
"@types/node": "^18.7.21",
"@types/uuid": "^9.0.7",
"@typescript-eslint/eslint-plugin": "^5.38.0",
"@typescript-eslint/parser": "^5.38.0",
"eslint": "^8.24.0",
Expand All @@ -45,5 +47,9 @@
"prettier": "^2.7.1",
"typescript": "^4.9.4"
},
"dependencies": {}
"dependencies": {
"jsonwebtoken": "^9.0.2",
"keypair": "^1.0.4",
"uuid": "^9.0.1"
}
}
Empty file removed src/__tests__/.gitkeep
Empty file.
30 changes: 30 additions & 0 deletions src/__tests__/Jwt.test.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
import { JsonWebTokenError, JwtPayload } from "jsonwebtoken";
import { JwtSecurity } from "..";

describe("JwtSecurity", () => {
it("creates and verifies successfully", () => {
const security = new JwtSecurity({ aud: "jest", iss: "test" });

// create token.
const token = security.sign("test-1234", { hello: "world" });
// verify token with same signing key.
const decoded = security.verify(token) as JwtPayload;

// expect an output, using subject for test.
expect(decoded.sub).toEqual("test-1234");
});

it("creates and verifies unsuccessfully", () => {
const security = new JwtSecurity({ aud: "jest", iss: "test" });

// create token
const token = security.sign("test-1234", { hello: "world" });

// invalidate the signing key by rotating
security.rotate();

// expect the signing to fail.
expect(() => security.verify(token)).toThrow(JsonWebTokenError);
expect(() => security.verify(token)).toThrow("invalid signature");
});
});
2 changes: 1 addition & 1 deletion src/index.ts
Original file line number Diff line number Diff line change
@@ -1 +1 @@
console.log("Hello World");
export { default as JwtSecurity } from "./utils/JwtSecurity";
61 changes: 61 additions & 0 deletions src/utils/JwtSecurity.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,61 @@
import jwt, { Algorithm } from "jsonwebtoken";
import Key from "./Key";

export default class JwtSecurity {
private issuer: string;
private audience: string;
private algorithm: Algorithm = "RS256";
private expiresIn = "1h";

keyId!: string;
publicKey!: string;
private privateKey!: string;

constructor({
aud,
iss,
alg,
exp,
}: {
aud: string;
iss: string;
alg?: Algorithm;
exp?: string;
}) {
// set values
this.audience = aud;
this.issuer = iss;
if (alg) this.algorithm = alg;
if (exp) this.expiresIn = exp;

// generate new keys
this.rotate();
}

rotate() {
const { keyId, publicKey, privateKey } = Key.generate();
this.keyId = keyId;
this.publicKey = publicKey;
this.privateKey = privateKey;
}

sign(subject: string, payload: Record<string, any>) {
return jwt.sign(payload, this.privateKey, {
subject,
expiresIn: this.expiresIn,
audience: this.audience,
encoding: "UTF8",
keyid: this.keyId,
algorithm: this.algorithm,
issuer: this.issuer,
});
}

verify(token: string) {
return jwt.verify(token, this.publicKey, {
issuer: this.issuer,
audience: this.audience,
algorithms: [this.algorithm],
});
}
}
20 changes: 20 additions & 0 deletions src/utils/Key.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
/**
* This allows us to dynamically create and rotate keys, especially for signing jwts
*/
// import { generateKeyPairSync } from "crypto";
import keypair from "keypair";
import { v4 as uuidv4 } from "uuid";

class KeySecurity {
generate() {
const keys = keypair({ bits: 2048 });
return {
keyId: uuidv4(),
privateKey: keys.private,
publicKey: keys.public,
};
}
}

const Key = new KeySecurity();
export default Key;
109 changes: 108 additions & 1 deletion yarn.lock
Original file line number Diff line number Diff line change
Expand Up @@ -868,6 +868,13 @@
resolved "https://registry.yarnpkg.com/@types/json-schema/-/json-schema-7.0.15.tgz#596a1747233694d50f6ad8a7869fcb6f56cf5841"
integrity sha512-5+fP8P8MFNC+AyZCDxrB2pkZFPGzqQWUzpSeuuVLvm8VMcorNYavBqoFcxK8bQz4Qsbn4oUEEem4wDLfcysGHA==

"@types/jsonwebtoken@^9.0.5":
version "9.0.5"
resolved "https://registry.yarnpkg.com/@types/jsonwebtoken/-/jsonwebtoken-9.0.5.tgz#0bd9b841c9e6c5a937c17656e2368f65da025588"
integrity sha512-VRLSGzik+Unrup6BsouBeHsf4d1hOEgYWTm/7Nmw1sXoN1+tRly/Gy/po3yeahnP4jfnQWWAhQAqcNfH7ngOkA==
dependencies:
"@types/node" "*"

"@types/keyv@^3.1.4":
version "3.1.4"
resolved "https://registry.yarnpkg.com/@types/keyv/-/keyv-3.1.4.tgz#3ccdb1c6751b0c7e52300bcdacd5bcbf8faa75b6"
Expand Down Expand Up @@ -906,6 +913,11 @@
resolved "https://registry.yarnpkg.com/@types/stack-utils/-/stack-utils-2.0.3.tgz#6209321eb2c1712a7e7466422b8cb1fc0d9dd5d8"
integrity sha512-9aEbYZ3TbYMznPdcdr3SmIrLXwC/AKZXQeCf9Pgao5CKb8CyHuEX5jzWPTkvregvhRJHcpRO6BFoGW9ycaOkYw==

"@types/uuid@^9.0.7":
version "9.0.7"
resolved "https://registry.yarnpkg.com/@types/uuid/-/uuid-9.0.7.tgz#b14cebc75455eeeb160d5fe23c2fcc0c64f724d8"
integrity sha512-WUtIVRUZ9i5dYXefDEAI7sh9/O7jGvHg7Df/5O/gtH3Yabe5odI3UWopVR1qbPXQtvOxWu3mM4XxlYeZtMWF4g==

"@types/yargs-parser@*":
version "21.0.3"
resolved "https://registry.yarnpkg.com/@types/yargs-parser/-/yargs-parser-21.0.3.tgz#815e30b786d2e8f0dcd85fd5bcf5e1a04d008f15"
Expand Down Expand Up @@ -1217,6 +1229,11 @@ [email protected]:
dependencies:
node-int64 "^0.4.0"

[email protected]:
version "1.0.1"
resolved "https://registry.yarnpkg.com/buffer-equal-constant-time/-/buffer-equal-constant-time-1.0.1.tgz#f8e71132f7ffe6e01a5c9697a4c6f3e48d5cc819"
integrity sha512-zRpUiDwd/xk6ADqPMATG8vc9VPrkck7T07OIx0gnjmJAnHnTVXNQG3vfvWNuiZIkwu9KrKdA1iJKfsfTVxE6NA==

buffer-from@^1.0.0:
version "1.1.2"
resolved "https://registry.yarnpkg.com/buffer-from/-/buffer-from-1.1.2.tgz#2b146a6fd72e80b4f55d255f35ed59a3a9a41bd5"
Expand Down Expand Up @@ -1453,6 +1470,13 @@ doctrine@^3.0.0:
dependencies:
esutils "^2.0.2"

[email protected]:
version "1.0.11"
resolved "https://registry.yarnpkg.com/ecdsa-sig-formatter/-/ecdsa-sig-formatter-1.0.11.tgz#ae0f0fa2d85045ef14a817daa3ce9acd0489e5bf"
integrity sha512-nagl3RYrbNv6kQkeJIpt6NJZy8twLB/2vtz6yN9Z4vRKHN4/QZJIEbqohALSgwKdnksuY3k5Addp5lg8sVoVcQ==
dependencies:
safe-buffer "^5.0.1"

electron-to-chromium@^1.4.535:
version "1.4.581"
resolved "https://registry.yarnpkg.com/electron-to-chromium/-/electron-to-chromium-1.4.581.tgz#23b684c67bf56d4284e95598c05a5d266653b6d8"
Expand Down Expand Up @@ -2545,6 +2569,44 @@ jsonc-parser@^3.2.0:
resolved "https://registry.yarnpkg.com/jsonc-parser/-/jsonc-parser-3.2.0.tgz#31ff3f4c2b9793f89c67212627c51c6394f88e76"
integrity sha512-gfFQZrcTc8CnKXp6Y4/CBT3fTc0OVuDofpre4aEeEpSBPV5X5v4+Vmx+8snU7RLPrNHPKSgLxGo9YuQzz20o+w==

jsonwebtoken@^9.0.2:
version "9.0.2"
resolved "https://registry.yarnpkg.com/jsonwebtoken/-/jsonwebtoken-9.0.2.tgz#65ff91f4abef1784697d40952bb1998c504caaf3"
integrity sha512-PRp66vJ865SSqOlgqS8hujT5U4AOgMfhrwYIuIhfKaoSCZcirrmASQr8CX7cUg+RMih+hgznrjp99o+W4pJLHQ==
dependencies:
jws "^3.2.2"
lodash.includes "^4.3.0"
lodash.isboolean "^3.0.3"
lodash.isinteger "^4.0.4"
lodash.isnumber "^3.0.3"
lodash.isplainobject "^4.0.6"
lodash.isstring "^4.0.1"
lodash.once "^4.0.0"
ms "^2.1.1"
semver "^7.5.4"

jwa@^1.4.1:
version "1.4.1"
resolved "https://registry.yarnpkg.com/jwa/-/jwa-1.4.1.tgz#743c32985cb9e98655530d53641b66c8645b039a"
integrity sha512-qiLX/xhEEFKUAJ6FiBMbes3w9ATzyk5W7Hvzpa/SLYdxNtng+gcurvrI7TbACjIXlsJyr05/S1oUhZrc63evQA==
dependencies:
buffer-equal-constant-time "1.0.1"
ecdsa-sig-formatter "1.0.11"
safe-buffer "^5.0.1"

jws@^3.2.2:
version "3.2.2"
resolved "https://registry.yarnpkg.com/jws/-/jws-3.2.2.tgz#001099f3639468c9414000e99995fa52fb478304"
integrity sha512-YHlZCB6lMTllWDtSPHz/ZXTsi8S00usEV6v1tjq8tOUZzw7DpSDWVXjXDre6ed1w/pd495ODpHZYSdkRTsa0HA==
dependencies:
jwa "^1.4.1"
safe-buffer "^5.0.1"

keypair@^1.0.4:
version "1.0.4"
resolved "https://registry.yarnpkg.com/keypair/-/keypair-1.0.4.tgz#a749a45f388593f3950f18b3757d32a93bd8ce83"
integrity sha512-zwhgOhhniaL7oxMgUMKKw5219PWWABMO+dgMnzJOQ2/5L3XJtTJGhW2PEXlxXj9zaccdReZJZ83+4NPhVfNVDg==

keyv@^4.0.0, keyv@^4.5.3:
version "4.5.4"
resolved "https://registry.yarnpkg.com/keyv/-/keyv-4.5.4.tgz#a879a99e29452f942439f2a405e3af8b31d4de93"
Expand Down Expand Up @@ -2589,11 +2651,46 @@ locate-path@^6.0.0:
dependencies:
p-locate "^5.0.0"

lodash.includes@^4.3.0:
version "4.3.0"
resolved "https://registry.yarnpkg.com/lodash.includes/-/lodash.includes-4.3.0.tgz#60bb98a87cb923c68ca1e51325483314849f553f"
integrity sha512-W3Bx6mdkRTGtlJISOvVD/lbqjTlPPUDTMnlXZFnVwi9NKJ6tiAk6LVdlhZMm17VZisqhKcgzpO5Wz91PCt5b0w==

lodash.isboolean@^3.0.3:
version "3.0.3"
resolved "https://registry.yarnpkg.com/lodash.isboolean/-/lodash.isboolean-3.0.3.tgz#6c2e171db2a257cd96802fd43b01b20d5f5870f6"
integrity sha512-Bz5mupy2SVbPHURB98VAcw+aHh4vRV5IPNhILUCsOzRmsTmSQ17jIuqopAentWoehktxGd9e/hbIXq980/1QJg==

lodash.isinteger@^4.0.4:
version "4.0.4"
resolved "https://registry.yarnpkg.com/lodash.isinteger/-/lodash.isinteger-4.0.4.tgz#619c0af3d03f8b04c31f5882840b77b11cd68343"
integrity sha512-DBwtEWN2caHQ9/imiNeEA5ys1JoRtRfY3d7V9wkqtbycnAmTvRRmbHKDV4a0EYc678/dia0jrte4tjYwVBaZUA==

lodash.isnumber@^3.0.3:
version "3.0.3"
resolved "https://registry.yarnpkg.com/lodash.isnumber/-/lodash.isnumber-3.0.3.tgz#3ce76810c5928d03352301ac287317f11c0b1ffc"
integrity sha512-QYqzpfwO3/CWf3XP+Z+tkQsfaLL/EnUlXWVkIk5FUPc4sBdTehEqZONuyRt2P67PXAk+NXmTBcc97zw9t1FQrw==

lodash.isplainobject@^4.0.6:
version "4.0.6"
resolved "https://registry.yarnpkg.com/lodash.isplainobject/-/lodash.isplainobject-4.0.6.tgz#7c526a52d89b45c45cc690b88163be0497f550cb"
integrity sha512-oSXzaWypCMHkPC3NvBEaPHf0KsA5mvPrOPgQWDsbg8n7orZ290M0BmC/jgRZ4vcJ6DTAhjrsSYgdsW/F+MFOBA==

lodash.isstring@^4.0.1:
version "4.0.1"
resolved "https://registry.yarnpkg.com/lodash.isstring/-/lodash.isstring-4.0.1.tgz#d527dfb5456eca7cc9bb95d5daeaf88ba54a5451"
integrity sha512-0wJxfxH1wgO3GrbuP+dTTk7op+6L41QCXbGINEmD+ny/G/eCqGzxyCsh7159S+mgDDcoarnBw6PC1PS5+wUGgw==

lodash.merge@^4.6.2:
version "4.6.2"
resolved "https://registry.yarnpkg.com/lodash.merge/-/lodash.merge-4.6.2.tgz#558aa53b43b661e1925a0afdfa36a9a1085fe57a"
integrity sha512-0KpjqXRVvrYyCsX1swR/XTK0va6VQkQM6MNo7PqW77ByjAhoARA8EfrP1N4+KlKj8YS0ZUCtRT/YUuhyYDujIQ==

lodash.once@^4.0.0:
version "4.1.1"
resolved "https://registry.yarnpkg.com/lodash.once/-/lodash.once-4.1.1.tgz#0dd3971213c7c56df880977d504c88fb471a97ac"
integrity sha512-Sb487aTOCr9drQVL8pIxOzVhafOjZN9UU54hiN8PU3uAiSV7lx1yYNpbNmex2PK6dSJoNTSJUUswT651yww3Mg==

lowercase-keys@^2.0.0:
version "2.0.0"
resolved "https://registry.yarnpkg.com/lowercase-keys/-/lowercase-keys-2.0.0.tgz#2603e78b7b4b0006cbca2fbcc8a3202558ac9479"
Expand Down Expand Up @@ -2685,6 +2782,11 @@ [email protected]:
resolved "https://registry.yarnpkg.com/ms/-/ms-2.1.2.tgz#d09d1f357b443f493382a8eb3ccd183872ae6009"
integrity sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w==

ms@^2.1.1:
version "2.1.3"
resolved "https://registry.yarnpkg.com/ms/-/ms-2.1.3.tgz#574c8138ce1d2b5861f0b44579dbadd60c6615b2"
integrity sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==

natural-compare-lite@^1.4.0:
version "1.4.0"
resolved "https://registry.yarnpkg.com/natural-compare-lite/-/natural-compare-lite-1.4.0.tgz#17b09581988979fddafe0201e931ba933c96cbb4"
Expand Down Expand Up @@ -3032,7 +3134,7 @@ run-parallel@^1.1.9:
dependencies:
queue-microtask "^1.2.2"

[email protected], safe-buffer@~5.2.0:
[email protected], safe-buffer@^5.0.1, safe-buffer@~5.2.0:
version "5.2.1"
resolved "https://registry.yarnpkg.com/safe-buffer/-/safe-buffer-5.2.1.tgz#1eaf9fa9bdb1fdd4ec75f58f9cdb4e6b7827eec6"
integrity sha512-rp3So07KcdmmKbGvgaNxQSJr7bGVSVk5S9Eq1F+ppbRo70+YeaDxkw5Dd8NPN+GD6bjnYm2VuPuCXmpuYvmCXQ==
Expand Down Expand Up @@ -3344,6 +3446,11 @@ util-deprecate@^1.0.1:
resolved "https://registry.yarnpkg.com/util-deprecate/-/util-deprecate-1.0.2.tgz#450d4dc9fa70de732762fbd2d4a28981419a0ccf"
integrity sha512-EPD5q1uXyFxJpCrLnCc1nHnq3gOa6DZBocAIiI2TaSCA7VCJ1UJDMagCzIkXNsUYfD1daK//LTEQ8xiIbrHtcw==

uuid@^9.0.1:
version "9.0.1"
resolved "https://registry.yarnpkg.com/uuid/-/uuid-9.0.1.tgz#e188d4c8853cc722220392c424cd637f32293f30"
integrity sha512-b+1eJOlsR9K8HJpow9Ok3fiWOWSIcIzXodvv0rQjVoOVNpWMpxf1wZNpt4y9h10odCNrqnYp1OBzRktckBe3sA==

v8-to-istanbul@^9.0.1:
version "9.1.3"
resolved "https://registry.yarnpkg.com/v8-to-istanbul/-/v8-to-istanbul-9.1.3.tgz#ea456604101cd18005ac2cae3cdd1aa058a6306b"
Expand Down

0 comments on commit 68649bf

Please sign in to comment.