Skip to content

Conversation

@jeffwilcox
Copy link
Contributor

Resolves #711 711

To use @octokit/auth-app.js, you must always provide a full privateKey option. This can expose a GitHub App's private key to runtime environments which is much less secure than keeping the private key in a HSM or remote key signing service.

The GitHub docs on storing private keys specifically suggests using sign-only keys stored securely.

This change is a basic update to allow for this alternate path from a privateCertificate for this more advanced and secure approach.

I'm looking for feedback on this pull request, especially around variable names, approach on the types for the Strategy, etc. Since most users will continue to use privateKey, unless the external JWT signing option is truthy, I've kept the "privateKey required" type helpful error message in place.

Before the change?

  • No support for remote signing servers or HSMs.

After the change?

  • Can optionally provide a callback to remotely sign a JWT, instead of using the universal-github-app-jwt dependency and an in-memory key

Pull request checklist

  • Tests for the changes have been added (for bug fixes / features)
  • Docs have been reviewed and added / updated if needed (for bug fixes / features)

Does this introduce a breaking change?

  • No

To enable the use of Azure Key Vault keys, or
other HSMs, this allows for a callback to be
provided in lieu of a GitHub App's private
certificate.

Does not contain test updates.
@github-actions
Copy link
Contributor

github-actions bot commented Aug 6, 2025

👋 Hi! Thank you for this contribution! Just to let you know, our GitHub SDK team does a round of issue and PR reviews twice a week, every Monday and Friday! We have a process in place for prioritizing and responding to your input. Because you are a part of this community please feel free to comment, add to, or pick up any issues/PRs that are labeled with Status: Up for grabs. You & others like you are the reason all of this works! So thank you & happy coding! 🚀

Copy link
Contributor

@gr2m gr2m left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Hi Jeff!! I think it's a great change! The request to accept a JWT instead of a private key came up several times across octokit and its dependents.

I like the approach of using a callback instead of passing a fixed JWT. That way @octokit/auth-app can re-create the JWT as needed.

I'd recommend to verify that usage with Octokit keeps working as before. It should be able to create installation tokens upon first request and auto-renew it when it expires.

Maybe consider renaming externalSignJwt to something simpler, like createJwt? The method should also accept a timeDifference argument to account for time skew between client and server, see

auth-app.js/src/hook.ts

Lines 69 to 79 in 3b732be

state.log.warn(error.message);
state.log.warn(
`[@octokit/auth-app] GitHub API time and system time are different by ${diff} seconds. Retrying request with the difference accounted for.`,
);
const { token } = await getAppAuthentication({
...state,
timeDifference: diff,
});
endpoint.headers.authorization = `bearer ${token}`;
return request(endpoint as EndpointOptions);

@jeffwilcox
Copy link
Contributor Author

Sounds good, I'll get this cleaned up a bit. We've been using it in production with rest.js on jobs that run 24/7 and renew just fine but I'll integrate the updates I can make here to bake it a bit more.

@wolfy1339 wolfy1339 added the Type: Feature New feature or request label Aug 11, 2025
@wolfy1339 wolfy1339 moved this from 🆕 Triage to 🏗 In progress in 🧰 Octokit Active Aug 11, 2025
@jeffwilcox
Copy link
Contributor Author

@gr2m k, minor updates addressed for now. Hopefully straightforward to rebase vs merge at some point.

  • name createJwt for simplicity
  • passing along any timeDifference seconds to the method

Since the universal JWT module itself has a built-in -30s buffer in its logic, I don't love it, but added a comment in the README example to as much. It's probably not worth refactoring multiple libraries to consider passing now as a true Date or not implicitly addressing clock skew with the 30s in there.

@jeffwilcox
Copy link
Contributor Author

Also just to confirm, wrote a hello world octokit app that calls rest.repos.get every minute and let it run the last hour+, along with some classy console logging when a new JWT is minted by my remote signing service in the callback.

crafting an awesome JWT now: 418639 at 2025-08-19T20:04:33.495Z
crafting an awesome JWT now: 418639 at 2025-08-19T21:03:56.167Z

App ran fine.

appId: APP_ID,
// @ts-ignore
privateKey: undefined,
privateKey: undefined as string,
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This shouldn't be needed. The // @ts-ignore should make it be ignored

Copy link
Member

@wolfy1339 wolfy1339 left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

LGTM

@wolfy1339 wolfy1339 merged commit ef7a95d into octokit:main Aug 26, 2025
7 checks passed
@github-project-automation github-project-automation bot moved this from 🏗 In progress to ✅ Done in 🧰 Octokit Active Aug 26, 2025
@github-actions
Copy link
Contributor

🎉 This PR is included in version 8.1.0 🎉

The release is available on:

Your semantic-release bot 📦🚀

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

released Type: Feature New feature or request

Projects

Status: ✅ Done

Development

Successfully merging this pull request may close these issues.

[FEAT]: Support HSMs and remote JWT signing services

3 participants