{"id":15465175,"url":"https://github.com/gr2m/universal-github-app-jwt","last_synced_at":"2025-04-04T09:08:55.460Z","repository":{"id":35093912,"uuid":"205579747","full_name":"gr2m/universal-github-app-jwt","owner":"gr2m","description":"Calculate GitHub App bearer tokens for Node \u0026 modern browsers","archived":false,"fork":false,"pushed_at":"2025-03-17T22:35:30.000Z","size":941,"stargazers_count":30,"open_issues_count":6,"forks_count":12,"subscribers_count":3,"default_branch":"main","last_synced_at":"2025-03-28T11:45:46.551Z","etag":null,"topics":[],"latest_commit_sha":null,"homepage":"","language":"JavaScript","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"mit","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/gr2m.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":null,"funding":null,"license":"LICENSE","code_of_conduct":"CODE_OF_CONDUCT.md","threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null,"governance":null,"roadmap":null,"authors":null,"dei":null,"publiccode":null,"codemeta":null},"funding":{"github":"gr2m"}},"created_at":"2019-08-31T18:06:07.000Z","updated_at":"2025-03-17T22:35:08.000Z","dependencies_parsed_at":"2023-01-15T13:41:40.723Z","dependency_job_id":"5d12f98a-ec4d-4c2c-8a06-002691350830","html_url":"https://github.com/gr2m/universal-github-app-jwt","commit_stats":{"total_commits":131,"total_committers":10,"mean_commits":13.1,"dds":0.7022900763358779,"last_synced_commit":"d52064d3395a02363b6f80afeeb5d91e0a5f2040"},"previous_names":[],"tags_count":20,"template":false,"template_full_name":null,"repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/gr2m%2Funiversal-github-app-jwt","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/gr2m%2Funiversal-github-app-jwt/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/gr2m%2Funiversal-github-app-jwt/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/gr2m%2Funiversal-github-app-jwt/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/gr2m","download_url":"https://codeload.github.com/gr2m/universal-github-app-jwt/tar.gz/refs/heads/main","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":247024159,"owners_count":20870940,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2022-07-04T15:15:14.044Z","host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":[],"created_at":"2024-10-02T01:00:29.958Z","updated_at":"2025-04-04T09:08:55.431Z","avatar_url":"https://github.com/gr2m.png","language":"JavaScript","funding_links":["https://github.com/sponsors/gr2m"],"categories":[],"sub_categories":[],"readme":"# universal-github-app-jwt\n\n\u003e Calculate GitHub App bearer tokens for Node, Deno, and modern browsers\n\n[![@latest](https://img.shields.io/npm/v/universal-github-app-jwt)](https://www.npmjs.com/universal-github-app-jwt)\n[![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)\n\n## Usage\n\n\u003ctable\u003e\n\u003ctbody valign=top align=left\u003e\n\u003ctr\u003e\u003cth\u003e\nBrowsers\n\u003c/th\u003e\u003ctd width=100%\u003e\nLoad \u003ccode\u003euniversal-github-app-jwt\u003c/code\u003e directly from \u003ca href=\"https://esm.sh\"\u003eesm.sh\u003c/a\u003e\n        \n```html\n\u003cscript type=\"module\"\u003e\nimport githubAppJwt from \"https://esm.sh/universal-github-app-jwt\";\n\u003c/script\u003e\n```\n\n\u003c/td\u003e\u003c/tr\u003e\n\u003ctr\u003e\u003cth\u003e\nNode\n\u003c/th\u003e\u003ctd\u003e\n\nInstall with \u003ccode\u003enpm install universal-github-app-jwt\u003c/code\u003e\n\n```js\nimport githubAppJwt from \"universal-github-app-jwt\";\n```\n\n\u003c/td\u003e\u003c/tr\u003e\n\u003ctr\u003e\u003cth\u003e\nDeno\n\u003c/th\u003e\u003ctd\u003e\n\nLoad \u003ccode\u003euniversal-github-app-jwt\u003c/code\u003e directly from \u003ca href=\"https://esm.sh\"\u003eesm.sh\u003c/a\u003e, including types.\n\n```js\nimport githubAppJwt from \"https://esm.sh/universal-github-app-jwt\";\n```\n\n\u003c/td\u003e\u003c/tr\u003e\n\u003c/tbody\u003e\n\u003c/table\u003e\n\n```js\nconst { token, appId, expiration } = await githubAppJwt({\n  id: APP_ID,\n  privateKey: PRIVATE_KEY,\n});\n```\n\nThe retrieved `token` can now be used in Authorization request header, e.g. with [`@octokit/request`](https://github.com/octokit/request.js/#readme):\n\n```js\nrequest(\"GET /app\", {\n  headers: {\n    authorization: `bearer ${token}`,\n  },\n});\n```\n\nFor a complete implementation of GitHub App authentication strategies, see [`@octokit/auth-app.js`](https://github.com/octokit/auth-app.js/#readme).\n\n## `githubAppJwt(options)`\n\n\u003ctable width=\"100%\"\u003e\n  \u003cthead align=left\u003e\n    \u003ctr\u003e\n      \u003cth width=150\u003e\n        name\n      \u003c/th\u003e\n      \u003cth width=70\u003e\n        type\n      \u003c/th\u003e\n      \u003cth\u003e\n        description\n      \u003c/th\u003e\n    \u003c/tr\u003e\n  \u003c/thead\u003e\n  \u003ctbody align=left valign=top\u003e\n    \u003ctr\u003e\n      \u003cth\u003e\n        \u003ccode\u003eoptions.id\u003c/code\u003e\n      \u003c/th\u003e\n      \u003cth\u003e\n        \u003ccode\u003enumber | string\u003c/code\u003e\n      \u003c/th\u003e\n      \u003ctd\u003e\n        \u003cstrong\u003eRequired\u003c/strong\u003e. The GitHub App's ID or Client ID. For \u003ccode\u003egithub.com\u003c/code\u003e and GHES 3.14+, it is recommended to use the Client ID.\n      \u003c/td\u003e\n    \u003c/tr\u003e\n    \u003ctr\u003e\n      \u003cth\u003e\n        \u003ccode\u003eoptions.privateKey\u003c/code\u003e\n      \u003c/th\u003e\n      \u003cth\u003e\n        \u003ccode\u003estring\u003c/code\u003e\n      \u003c/th\u003e\n      \u003ctd\u003e\n        \u003cstrong\u003eRequired\u003c/strong\u003e. Content of the \u003ccode\u003e*.pem\u003c/code\u003e 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.\n      \u003c/td\u003e\n    \u003c/tr\u003e\n    \u003ctr\u003e\n      \u003cth\u003e\n        \u003ccode\u003eoptions.now\u003c/code\u003e\n      \u003c/th\u003e\n      \u003cth\u003e\n        \u003ccode\u003enumber\u003c/code\u003e\n      \u003c/th\u003e\n      \u003ctd\u003e\n        An optional override for the current time in seconds since the UNIX epoch. Defaults to \u003ccode\u003eMath.floor(Date.now() / 1000))\u003c/code\u003e. This value can be overridden to account for a time skew between the local machine and the authentication server.\n      \u003c/td\u003e\n    \u003c/tr\u003e\n  \u003c/tbody\u003e\n\u003c/table\u003e\n\n`githubAppJwt(options)` resolves with an object with the following keys\n\n\u003ctable width=\"100%\"\u003e\n  \u003cthead align=left\u003e\n    \u003ctr\u003e\n      \u003cth width=150\u003e\n        name\n      \u003c/th\u003e\n      \u003cth width=70\u003e\n        type\n      \u003c/th\u003e\n      \u003cth\u003e\n        description\n      \u003c/th\u003e\n    \u003c/tr\u003e\n  \u003c/thead\u003e\n  \u003ctbody align=left valign=top\u003e\n    \u003ctr\u003e\n      \u003cth\u003e\n        \u003ccode\u003etoken\u003c/code\u003e\n      \u003c/th\u003e\n      \u003cth\u003e\n        \u003ccode\u003estring\u003c/code\u003e\n      \u003c/th\u003e\n      \u003ctd\u003e\n        The JSON Web Token (JWT) to authenticate as the app.\n      \u003c/td\u003e\n    \u003c/tr\u003e\n    \u003ctr\u003e\n      \u003cth\u003e\n        \u003ccode\u003eappId\u003c/code\u003e\n      \u003c/th\u003e\n      \u003cth\u003e\n        \u003ccode\u003enumber\u003c/code\u003e\n      \u003c/th\u003e\n      \u003ctd\u003e\n        The GitHub App database ID or Client ID passed in \u003ccode\u003eoptions.id\u003c/code\u003e.\n      \u003c/td\u003e\n    \u003c/tr\u003e\n    \u003ctr\u003e\n      \u003cth\u003e\n        \u003ccode\u003eexpiration\u003c/code\u003e\n      \u003c/th\u003e\n      \u003cth\u003e\n        \u003ccode\u003enumber\u003c/code\u003e\n      \u003c/th\u003e\n      \u003ctd\u003e\n        Timestamp as UNIX epoch, e.g. \u003ccode\u003e1530922170\u003c/code\u003e. A Date object can be created using \u003ccode\u003enew Date(authentication.expiration)\u003c/code\u003e.\n      \u003c/td\u003e\n    \u003c/tr\u003e\n  \u003c/tbody\u003e\n\u003c/table\u003e\n\n\u003c!-- do not remove this anchor, it's used in error messages --\u003e\n\n\u003ca name=\"private-key-formats\"\u003e\u003c/a\u003e\n\n## About Private Key formats\n\nWhen downloading a `private-key.pem` file from GitHub, the format is in `PKCS#1` format. Unfortunately, the WebCrypto API only supports `PKCS#8`.\n\nIf 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.\n\nYou can identify the format based on the the first line\n\n| First Line                            | Format  |\n| ------------------------------------- | ------- |\n| `-----BEGIN RSA PRIVATE KEY-----`     | PKCS#1  |\n| `-----BEGIN PRIVATE KEY-----`         | PKCS#8  |\n| `-----BEGIN OPENSSH PRIVATE KEY-----` | OpenSSH |\n\n### Converting `PKCS#1` to `PKCS#8`\n\n- #### Using an Online Private Key Converter\n\nConvert quickly using the Web interface at https://private-key-converter.vercel.app\n\n- #### Using Node.js\n\nIf you use Node.js, you can convert the format before passing it to `universal-github-app-jwt`:\n\n```js\nimport crypto from \"node:crypto\";\nimport githubAppJwt from \"universal-github-app-jwt\";\n\nconst privateKeyPkcs8 = crypto\n  .createPrivateKey(process.env.PRIVATE_KEY)\n  .export({\n    type: \"pkcs8\",\n    format: \"pem\",\n  });\n\nconst { token, appId, expiration } = await githubAppJwt({\n  id: process.env.APP_ID,\n  privateKey: privateKeyPkcs8,\n});\n```\n\n- #### Using OpenSSL\n\nConvert the format using `openssl` before passing it to your app.\n\n```\nopenssl pkcs8 -topk8 -inform PEM -outform PEM -nocrypt -in private-key.pem -out private-key-pkcs8.key\n```\n\n### Converting `OpenSSH` to `PKCS#8`\n\n```\ncp private-key.pem private-key-pkcs8.key \u0026\u0026 ssh-keygen -p -m PKCS8  -N \"\" -f private-key-pkcs8.key\n```\n\nThis command forces a format change by asking `ssh-keygen` to set no password and then output in a different format.\n\nI'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.\n\n## License\n\n[MIT](LICENSE)\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fgr2m%2Funiversal-github-app-jwt","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fgr2m%2Funiversal-github-app-jwt","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fgr2m%2Funiversal-github-app-jwt/lists"}