Skip to content

Commit

Permalink
Merge pull request #2 from starkfire/v0.4.0
Browse files Browse the repository at this point in the history
V0.4.0
  • Loading branch information
starkfire authored Jul 21, 2022
2 parents 501ee84 + 0db9c9d commit c438aaf
Show file tree
Hide file tree
Showing 6 changed files with 96 additions and 23 deletions.
64 changes: 54 additions & 10 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -32,7 +32,7 @@ npm install jwt-key-generator
```js
const { generateSecret } = require('jwt-key-generator');

const secret = await generateSecret('HS256');
let secret = await generateSecret('HS256');

console.log(secret); // CryptoKey
```
Expand All @@ -44,8 +44,21 @@ const secret = await generateKeyPair('RS256');

console.log(secret); // CryptoKey
```
### Generate a Secret Key and return as `KeyObject`

You can also return the generated key as a `KeyObject`. To do so, simply pass an additional object argument to `generateSecret()` with `toKeyObject: true` as a property.

This allows you to conveniently use this library with JWT libraries such as [jsonwebtoken](https://www.npmjs.com/package/jsonwebtoken).

```js
const { generateSecret } = require('jwt-key-generator');

let secret = await generateSecret('HS256', { toKeyObject: true });

console.log(secret); // KeyObject
```
### Convert or export key to a different format
Once you have a `CryptoKey` generated by `generateSecret()` or `generateKeyPair()`, you can convert it to other formats supported by Web Crypto API's [subtle.exportKey()](https://nodejs.org/api/webcrypto.html#subtleexportkeyformat-key): `spki`, `pkc8`, `jwk`, and `raw`.
Once you have a `CryptoKey` generated by `generateSecret()` or `generateKeyPair()`, you can convert it to other formats supported by Web Crypto API's [subtle.exportKey()](https://nodejs.org/api/webcrypto.html#subtleexportkeyformat-key): `spki`, `pkcs8`, `jwk`, and `raw`.

```js
const { exportKey, generateSecret } = require('jwt-key-generator');
Expand All @@ -56,6 +69,25 @@ const exported = await exportKey(secret, 'jwk');
console.log(exported); // JSON Web Key
```

## Integrations

This library works with JWT libraries such as [jsonwebtoken](https://www.npmjs.com/package/jsonwebtoken). To do so, simply pass the generated key as a `KeyObject` to `.sign()` and `.verify()`:

```js
const jwt = require('jsonwebtoken');
const { generateSecret } = require('jwt-key-generator');


let secret = await generateSecret('HS256', { toKeyObject: true });
let payload = { id: 123 };

let token = jwt.sign(payload, secret);
console.log(token);

let decoded = jwt.verify(token, secret);
console.log(decoded);
```

## Development
```sh
git clone https://github.com/starkfire/jwt-key-generator.git
Expand All @@ -68,25 +100,37 @@ npm test
If you are interested to submit issues and pull requests, contributions are highly welcome. Consider checking out [CONTRIBUTING.md](https://github.com/starkfire/jwt-key-generator/blob/main/CONTRIBUTING.md).

## API
### `generateSecret(algorithm, extractable=true)`
### `generateSecret(algorithm, [options])`
* generates and returns a secret key
* **Parameters:**
* **algorithm** (`<string>`)
* JWT algorithm
* must be either one of the following algorithms: `HS256`, `HS384`, `HS512`, `A128KW`, `A192KW`, `A256KW`, `A128GCM`, `A192GCM`, `A256GCM`, `A128GCMKW`, `A192GCMKW`, or `A256GCMKW`.
* **extractable** (`<boolean>`) (default: _true_)
* when `true`, the returned `CryptoKey` can be exported to other formats (i.e. `raw`, `pkcs8`, `spki`, or `jwk`)
* **options** (`<object>`)
* specifies additional options before the function returns the key
* **Properties:**
* `extractable` (`<boolean>`)
* when `true`, the returned `CryptoKey` can be exported to other formats using `exportKey()`
* default value is `true`
* `toKeyObject` (`<boolean>`)
* when `true`, the key will be returned as a `KeyObject` instead of `CryptoKey`
* default value is `false`
* **Returns:**
* **key** (`<CryptoKey>`)
* **key** (`<CryptoKey | KeyObject>`)

### `generateKeyPair(algorithm, extractable=true)`
### `generateKeyPair(algorithm, [options])`
* generates and returns a public/private key pair
* **Parameters:**
* **algorithm** (`<string>`)
* JWT algorithm
* must be either one of the following algorithms: `RS256`, `RS384`, `RS512`, `PS256`, `PS384`, `PS512`, `RSA-OAEP`, `RSA-OAEP-256`, `RSA-OAEP-384`, or `RSA-OAEP-512`.
* **extractable** (`<boolean>`) (default: _true_)
* when `true`, the returned `CryptoKey` can be exported to other formats (i.e. `raw`, `pkcs8`, `spki`, or `jwk`)
* **options** (`<object>`)
* specifies additional options before the function returns the key
* as of `v0.4.0`, `toKeyObject` property is not yet supported for this method
* **Properties:**
* `extractable` (`<boolean>`)
* when `true`, the returned `CryptoKey` can be exported to other formats using `exportKey()`
* default value is `true`
* **Returns:**
* **key** (`<CryptoKey>`)

Expand All @@ -97,6 +141,6 @@ If you are interested to submit issues and pull requests, contributions are high
* cryptographic key
* this key may refer to the value returned by `generateSecret()` and `generateKeyPair()`
* **format** (`<string>`)
* can be either one of the following formats recognized by Web Crypto API's [subtle.exportKey()](https://nodejs.org/api/webcrypto.html#subtleexportkeyformat-key) (`spki`, `pkc8`, `jwk`, or `raw`)
* can be either one of the following formats recognized by Web Crypto API's [subtle.exportKey()](https://nodejs.org/api/webcrypto.html#subtleexportkeyformat-key) (`spki`, `pkcs8`, `jwk`, or `raw`)
* **Returns:**
* **transformedKey**
2 changes: 1 addition & 1 deletion package.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
{
"name": "jwt-key-generator",
"version": "0.3.0",
"version": "0.4.0",
"description": "",
"main": "dist/src/index.js",
"scripts": {
Expand Down
31 changes: 30 additions & 1 deletion src/generateKeyPair.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,27 @@ import generateRSAKey from './rsa/generateRSAKey';
import generateECDSAKey from './ecdsa/generateECDSAKey';
import { ASYNC_ALGS } from './algorithms';

interface Options {
[index: string]: boolean | undefined;

/**
* determines whether generateKeyPair() method exports the CryptoKey
* in a manner which allows subtle.export() to transform it for other
* formats.
*/
extractable?: boolean;

// TODO: support for `toKeyObject` option
}

const optionsSchema: Options = {
extractable: true
}

function isOptionsValid(options: object) {
return Object.keys(options).filter(key => !optionsSchema.hasOwnProperty(key));
}

function isInputAlgorithmValid(alg: string) {
for (let algType in ASYNC_ALGS) {
if (ASYNC_ALGS[algType].includes(alg)) {
Expand All @@ -12,8 +33,16 @@ function isInputAlgorithmValid(alg: string) {
return false;
}

export default async function generateKeyPair(alg: string, extractable: boolean = true) {
export default async function generateKeyPair(alg: string, options: Options = optionsSchema) {
let secret;
let extractable = options.extractable ?? true;

let optionErrors = isOptionsValid(options);
if (optionErrors.length > 0) {
optionErrors.map(key => {
throw new Error(`${key} is not a valid option for generateKeyPair()`);
});
}

if (!isInputAlgorithmValid(alg)) {
throw new Error('Invalid Asymmetric Algorithm');
Expand Down
10 changes: 5 additions & 5 deletions src/generateSecret.ts
Original file line number Diff line number Diff line change
Expand Up @@ -24,10 +24,10 @@ interface Options {
const optionsSchema: Options = {
extractable: true,
toKeyObject: false
}
};

function isOptionsValid(options: object) {
return Object.keys(options).filter(key => !optionsSchema.hasOwnProperty(key))
return Object.keys(options).filter(key => !optionsSchema.hasOwnProperty(key));
}

function isInputAlgorithmValid(alg: string) {
Expand All @@ -44,11 +44,11 @@ export default async function generateSecret(alg: string, options: Options = opt
let secret;
let extractable = options.extractable ?? true;

let optionErrors = isOptionsValid(options)
let optionErrors = isOptionsValid(options);
if (optionErrors.length > 0) {
optionErrors.map(key => {
throw new Error(`${key} is not a valid option for generateSecret()`)
})
throw new Error(`${key} is not a valid option for generateSecret()`);
});
}

if (!isInputAlgorithmValid(alg)) {
Expand Down
4 changes: 2 additions & 2 deletions test/exportKey.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -66,7 +66,7 @@ describe("Test exportKey()", () => {

test("should be able to export keys under RSA algorithms", async () => {
ASYNC_ALGS['RSA'].forEach(async alg => {
let secret = await generateKeyPair(alg, true);
let secret = await generateKeyPair(alg);
let rsaType = getRSAType(alg);

if (secret && rsaType) {
Expand All @@ -87,7 +87,7 @@ describe("Test exportKey()", () => {

test("should be able to export keys under ECDSA algorithms", async () => {
ASYNC_ALGS['ECDSA'].forEach(async alg => {
let secret = await generateKeyPair(alg, true);
let secret = await generateKeyPair(alg);
let ecdsaType = getCurve(alg);

if (secret && ecdsaType) {
Expand Down
8 changes: 4 additions & 4 deletions test/generateKeyPair.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5,15 +5,15 @@ import generateKeyPair from "../src/generateKeyPair";

describe("Test generateKeyPair()", () => {
test("should return a CryptoKey", async () => {
const secret = await generateKeyPair('RS256', true);
const secret = await generateKeyPair('RS256');

expect(isCryptoKey(secret.publicKey)).toEqual(true);
expect(isCryptoKey(secret.privateKey)).toEqual(true);
});

test("should return an error if an invalid algorithm is placed as an argument", async () => {
try {
await generateKeyPair('HS256', true);
await generateKeyPair('HS256');
} catch (error) {
if (error instanceof Error) {
expect(error.message).toBe('Invalid Asymmetric Algorithm');
Expand All @@ -23,7 +23,7 @@ describe("Test generateKeyPair()", () => {

test("should work with every algorithm within RSA (algorithms.ts)", async () => {
ASYNC_ALGS['RSA'].forEach(async (alg) => {
let secret = await generateKeyPair(alg, true);
let secret = await generateKeyPair(alg);

expect(isCryptoKey(secret.publicKey)).toEqual(true);
expect(isCryptoKey(secret.privateKey)).toEqual(true);
Expand All @@ -32,7 +32,7 @@ describe("Test generateKeyPair()", () => {

test("should work with every algorithm within ECDSA (algorithms.ts)", async () => {
ASYNC_ALGS['ECDSA'].forEach(async (alg) => {
let secret = await generateKeyPair(alg, true);
let secret = await generateKeyPair(alg);

expect(isCryptoKey(secret.publicKey)).toEqual(true);
expect(isCryptoKey(secret.privateKey)).toEqual(true);
Expand Down

0 comments on commit c438aaf

Please sign in to comment.