diff --git a/README.md b/README.md index e7f4db1..78649d3 100644 --- a/README.md +++ b/README.md @@ -101,7 +101,7 @@ For a complete implementation of GitHub App authentication strategies, see [`@oc string - Required. Content of the *.pem file you downloaded from the app’s about page. You can generate a new private key if needed. Make sure to preserve the line breaks. + Required. Content of the *.pem file you downloaded from the app’s about page. You can generate a new private key if needed. Make sure to preserve the line breaks. If your private key contains escaped newlines (`\\n`), they will be automatically replaced with actual newlines. diff --git a/index.js b/index.js index eb45a22..c0dcc07 100644 --- a/index.js +++ b/index.js @@ -12,6 +12,10 @@ export default async function githubAppJwt({ privateKey, now = Math.floor(Date.now() / 1000), }) { + // Private keys are often times configured as environment variables, in which case line breaks are escaped using `\\n`. + // Replace these here for convenience. + const privateKeyWithNewlines = privateKey.replace(/\\n/g, '\n'); + // When creating a JSON Web Token, it sets the "issued at time" (iat) to 30s // in the past as we have seen people running situations where the GitHub API // claimed the iat would be in future. It turned out the clocks on the @@ -26,7 +30,7 @@ export default async function githubAppJwt({ }; const token = await getToken({ - privateKey, + privateKey: privateKeyWithNewlines, payload, }); diff --git a/test/node.test.js b/test/node.test.js index 5f6c13b..18a5d34 100644 --- a/test/node.test.js +++ b/test/node.test.js @@ -162,3 +162,18 @@ test("Include the time difference in the expiration and issued_at field", async t.is(resultPayload.exp, 580); t.is(resultPayload.iat, -20); }); + +test("Replace escaped line breaks with actual linebreaks", async (t) => { + MockDate.set(0); + + const result = await githubAppJwt({ + id: APP_ID, + privateKey: PRIVATE_KEY_PKCS8.replace(/\n/g, "\\n"), + }); + + t.deepEqual(result, { + appId: APP_ID, + expiration: 570, + token: BEARER, + }); +});