universal-github-app-jwt
Advanced tools
Comparing version 2.0.2 to 2.0.3
// @ts-check | ||
// @ts-ignore - #get-token is defined in "imports" in package.json | ||
import { getToken } from "#get-token"; | ||
import { getToken } from "./lib/get-token.js"; | ||
@@ -6,0 +6,0 @@ /** |
// we don't @ts-check here because it chokes crypto which is a global API in modern JS runtime environments | ||
import { | ||
isPkcs1, | ||
isOpenSsh, | ||
getEncodedMessage, | ||
@@ -10,2 +12,4 @@ getDERfromPEM, | ||
import { subtle, convertPrivateKey } from "#crypto"; | ||
/** | ||
@@ -16,13 +20,17 @@ * @param {import('../internals').GetTokenOptions} options | ||
export async function getToken({ privateKey, payload }) { | ||
const convertedPrivateKey = convertPrivateKey(privateKey); | ||
// WebCrypto only supports PKCS#8, unfortunately | ||
if (privateKey.includes("-----BEGIN RSA PRIVATE KEY-----")) { | ||
/* c8 ignore start */ | ||
if (isPkcs1(convertedPrivateKey)) { | ||
throw new Error( | ||
"[universal-github-app-jwt] Private Key is in PKCS#1 format, but only PKCS#8 is supported by WebCrypto. See https://github.com/gr2m/universal-github-app-jwt#readme" | ||
"[universal-github-app-jwt] Private Key is in PKCS#1 format, but only PKCS#8 is supported. See https://github.com/gr2m/universal-github-app-jwt#readme" | ||
); | ||
} | ||
/* c8 ignore stop */ | ||
// WebCrypto does not support OpenSSH, unfortunately | ||
if (privateKey.includes("-----BEGIN OPENSSH PRIVATE KEY-----")) { | ||
if (isOpenSsh(convertedPrivateKey)) { | ||
throw new Error( | ||
"[universal-github-app-jwt] Private Key is in OpenSSH format, but only PKCS#8 is supported by WebCrypto. See https://github.com/gr2m/universal-github-app-jwt#readme" | ||
"[universal-github-app-jwt] Private Key is in OpenSSH format, but only PKCS#8 is supported. See https://github.com/gr2m/universal-github-app-jwt#readme" | ||
); | ||
@@ -39,4 +47,4 @@ } | ||
const privateKeyDER = getDERfromPEM(privateKey); | ||
const importedKey = await crypto.subtle.importKey( | ||
const privateKeyDER = getDERfromPEM(convertedPrivateKey); | ||
const importedKey = await subtle.importKey( | ||
"pkcs8", | ||
@@ -52,3 +60,3 @@ privateKeyDER, | ||
const signatureArrBuf = await crypto.subtle.sign( | ||
const signatureArrBuf = await subtle.sign( | ||
algorithm.name, | ||
@@ -55,0 +63,0 @@ importedKey, |
// we don't @ts-check here because it chokes on atob and btoa which are available in all modern JS runtime environments | ||
/** | ||
* @param {string} privateKey | ||
* @returns {boolean} | ||
*/ | ||
export function isPkcs1(privateKey) { | ||
return privateKey.includes("-----BEGIN RSA PRIVATE KEY-----"); | ||
} | ||
/** | ||
* @param {string} privateKey | ||
* @returns {boolean} | ||
*/ | ||
export function isOpenSsh(privateKey) { | ||
return privateKey.includes("-----BEGIN OPENSSH PRIVATE KEY-----"); | ||
} | ||
/** | ||
* @param {string} str | ||
@@ -32,3 +48,2 @@ * @returns {ArrayBuffer} | ||
/** | ||
* | ||
* @param {import('../internals').Header} header | ||
@@ -66,3 +81,2 @@ * @param {import('../internals').Payload} payload | ||
/** | ||
* | ||
* @param {Record<string,unknown>} obj | ||
@@ -69,0 +83,0 @@ * @returns {string} |
{ | ||
"name": "universal-github-app-jwt", | ||
"type": "module", | ||
"version": "2.0.2", | ||
"version": "2.0.3", | ||
"exports": "./index.js", | ||
"imports": { | ||
"#get-token": { | ||
"node": "./lib/get-token-node.js", | ||
"default": "./lib/get-token.js" | ||
"#crypto": { | ||
"node": "./lib/crypto-node.js", | ||
"default": "./lib/crypto-native.js" | ||
} | ||
@@ -37,8 +37,5 @@ }, | ||
"license": "MIT", | ||
"dependencies": { | ||
"jsonwebtoken": "^9.0.0" | ||
}, | ||
"devDependencies": { | ||
"ava": "^5.1.0", | ||
"c8": "^7.12.0", | ||
"c8": "^8.0.0", | ||
"esbuild": "^0.16.10", | ||
@@ -50,3 +47,14 @@ "mockdate": "^3.0.5", | ||
"typescript": "^4.9.4" | ||
}, | ||
"release": { | ||
"branches": [ | ||
"+([0-9]).x", | ||
"main", | ||
"next", | ||
{ | ||
"name": "beta", | ||
"prerelease": true | ||
} | ||
] | ||
} | ||
} |
# universal-github-app-jwt | ||
> Calculate GitHub App bearer tokens for Node & modern browsers | ||
> Calculate GitHub App bearer tokens for Node, Deno, and modern browsers | ||
[![@latest](https://img.shields.io/npm/vuniversal-github-app-jwt.svg)](https://www.npmjs.com/packageuniversal-github-app-jwt) | ||
[![@latest](https://img.shields.io/npm/universal-github-app-jwt.svg)](https://www.npmjs.com/universal-github-app-jwt) | ||
[![Build Status](https://github.com/gr2m/universal-github-app-jwt/workflows/Test/badge.svg)](https://github.com/gr2m/universal-github-app-jwt/actions?query=workflow%3ATest+branch%3Amaster) | ||
⚠ The private keys provide by GitHub are in `PKCS#1` format, but the WebCrypto API only supports `PKCS#8`. And neither Node nor the WEbCrypto API supports private keys in the `OpenSSH` format. You can see the difference in the first line, `PKCS#1` format starts with `-----BEGIN RSA PRIVATE KEY-----` while `PKCS#8` starts with `-----BEGIN PRIVATE KEY-----`, and `OpenSSH` starts with `-----BEGIN OPENSSH PRIVATE KEY-----`. | ||
You can convert `PKCS#1` to `PKCS#8` using `oppenssl`: | ||
``` | ||
openssl pkcs8 -topk8 -inform PEM -outform PEM -nocrypt -in private-key.pem -out private-key-pkcs8.key | ||
``` | ||
You can convert `OpenSSH` to `PKCS#8` using `ssh-keygen`: | ||
``` | ||
cp private-key.pem private-key-pkcs8.key && ssh-keygen -m PKCS8 -N "" -f private-key-pkcs8.key | ||
``` | ||
It's also possible to convert the formats with JavaScript, e.g. using [node-rsa](https://github.com/rzcoder/node-rsa), but it turns a 4kb to a 200kb+ built. I'm looking for help to create a minimal `PKCS#1` to `PKCS#8` convert library that I can recommend people to use before passing the private key to `githubAppJwt`. Please create an issue if you'd like to help. The same to convert `OpenSSH` to `PKCS#8`. | ||
You can convert `PKCS#1` to `PKCS#8` in Node.js using the built-in `crypto` module: | ||
```js | ||
const crypto = require("crypto"); | ||
const PRIVATE_KEY = `-----BEGIN RSA PRIVATE KEY----- | ||
... | ||
-----END RSA PRIVATE KEY-----`; | ||
const privateKeyPkcs8 = crypto.createPrivateKey(PRIVATE_KEY).export({ | ||
type: "pkcs8", | ||
format: "pem", | ||
}); | ||
``` | ||
When using a node, a conversion is not necessary, the implementation is agnostic to either `PKCS` format. | ||
However, if you got the error `Private Key is in PKCS#1 format, but only PKCS#8 is supported.` inside Node.js, it is possible that your bundler or your app framework incorrectly bundled the web version instead of the node version ([example](https://github.com/backstage/backstage/issues/9959)). | ||
## Usage | ||
@@ -208,4 +174,55 @@ | ||
<!-- do not remove this anchor, it's used in error messages --> | ||
<a name="private-key-formats"></a> | ||
## About Private Key formats | ||
When downloading a `private-key.pem` file from GitHub, the format is in `PKCS#1` format. Unfortunately, the WebCrypto API only supports `PKCS#8`. | ||
If you use 1Password to store a private key as an SSH key, it will be transformed to the `OpenSSH` format, which is also not supported by WebCrypto. | ||
You can identify the format based on the the first line | ||
| First Line | Format | | ||
| ------------------------------------- | ------- | | ||
| `-----BEGIN RSA PRIVATE KEY-----` | PKCS#1 | | ||
| `-----BEGIN PRIVATE KEY-----` | PKCS#8 | | ||
| `-----BEGIN OPENSSH PRIVATE KEY-----` | OpenSSH | | ||
### Converting `PKCS#1` to `PKCS#8` | ||
If you use Node.js, you can convert the format before passing it to `universal-github-app-jwt`: | ||
```js | ||
import crypto from "node:crypto"; | ||
import githubAppJwt from "universal-github-app-jwt"; | ||
const privateKeyPkcs8 = crypto.createPrivateKey(process.env.PRIVATE_KEY).export({ | ||
type: "pkcs8", | ||
format: "pem", | ||
} | ||
const { token, appId, expiration } = await githubAppJwt({ | ||
id: process.env.APP_ID, | ||
privateKey: privateKeyPkcs8, | ||
}); | ||
``` | ||
But we recommend to convert the format using `openssl` before passing it to your app. | ||
``` | ||
openssl pkcs8 -topk8 -inform PEM -outform PEM -nocrypt -in private-key.pem -out private-key-pkcs8.key | ||
``` | ||
### Converting `OpenSSH` to `PKCS#8` | ||
``` | ||
cp private-key.pem private-key-pkcs8.key && ssh-keygen -m PKCS8 -N "" -f private-key-pkcs8.key | ||
``` | ||
I'm looking for help to create a minimal `OpenSSH` to `PKCS` convert library that I can recommend people to use before passing the private key to `githubAppJwt`. Please create an issue if you'd like to help. | ||
## License | ||
[MIT](LICENSE) |
import test from "ava"; | ||
import MockDate from "mockdate"; | ||
import crypto from "node:crypto"; | ||
import githubAppJwt from "../index.js"; | ||
const APP_ID = 1; | ||
const PRIVATE_KEY = `-----BEGIN RSA PRIVATE KEY----- | ||
// private key in pkcs1 format, as it's provided by GitHub | ||
const PRIVATE_KEY_PKCS1 = `-----BEGIN RSA PRIVATE KEY----- | ||
MIIEpAIBAAKCAQEA1c7+9z5Pad7OejecsQ0bu3aozN3tihPmljnnudb9G3HECdnH | ||
@@ -100,3 +104,3 @@ lWu2/a1gB9JW5TBQ+AVpum9Okx7KfqkfBKL9mcHgSL0yWMdjMfNOqNtrQqKlN4kE | ||
test("README example for app auth", async (t) => { | ||
test("README example for app auth with private key in PKCS#8 format", async (t) => { | ||
MockDate.set(0); | ||
@@ -106,3 +110,3 @@ | ||
id: APP_ID, | ||
privateKey: PRIVATE_KEY, | ||
privateKey: PRIVATE_KEY_PKCS8, | ||
}); | ||
@@ -117,3 +121,3 @@ | ||
test("README example for app auth with private key in PKCS#8 format", async (t) => { | ||
test("README example for app auth with private key in PKCS#1 format", async (t) => { | ||
MockDate.set(0); | ||
@@ -123,3 +127,3 @@ | ||
id: APP_ID, | ||
privateKey: PRIVATE_KEY_PKCS8, | ||
privateKey: PRIVATE_KEY_PKCS1, | ||
}); | ||
@@ -134,3 +138,3 @@ | ||
test("Throw error if key is OpenSSH", async (t) => { | ||
test("Throws error if key is OpenSSH", async (t) => { | ||
MockDate.set(0); | ||
@@ -147,3 +151,3 @@ | ||
error.message, | ||
"[universal-github-app-jwt] Private Key is in OpenSSH format, but only PKCS is supported. See https://github.com/gr2m/universal-github-app-jwt#readme" | ||
"[universal-github-app-jwt] Private Key is in OpenSSH format, but only PKCS#8 is supported. See https://github.com/gr2m/universal-github-app-jwt#readme" | ||
); | ||
@@ -150,0 +154,0 @@ } |
Sorry, the diff of this file is not supported yet
License Policy Violation
LicenseThis package is not allowed per your license policy. Review the package's license to ensure compliance.
Found 1 instance in 1 package
License Policy Violation
LicenseThis package is not allowed per your license policy. Review the package's license to ensure compliance.
Found 1 instance in 1 package
29620
0
17
414
228
- Removedjsonwebtoken@^9.0.0
- Removedbuffer-equal-constant-time@1.0.1(transitive)
- Removedecdsa-sig-formatter@1.0.11(transitive)
- Removedjsonwebtoken@9.0.2(transitive)
- Removedjwa@1.4.1(transitive)
- Removedjws@3.2.2(transitive)
- Removedlodash.includes@4.3.0(transitive)
- Removedlodash.isboolean@3.0.3(transitive)
- Removedlodash.isinteger@4.0.4(transitive)
- Removedlodash.isnumber@3.0.3(transitive)
- Removedlodash.isplainobject@4.0.6(transitive)
- Removedlodash.isstring@4.0.1(transitive)
- Removedlodash.once@4.1.1(transitive)
- Removedms@2.1.3(transitive)
- Removedsafe-buffer@5.2.1(transitive)
- Removedsemver@7.6.3(transitive)