{"id":23613670,"url":"https://github.com/freckle/github-app-token","last_synced_at":"2025-05-12T21:40:41.004Z","repository":{"id":256595105,"uuid":"855872255","full_name":"freckle/github-app-token","owner":"freckle","description":"Generate an installation token for a GitHub App","archived":false,"fork":false,"pushed_at":"2025-03-21T22:23:39.000Z","size":35,"stargazers_count":0,"open_issues_count":2,"forks_count":0,"subscribers_count":6,"default_branch":"main","last_synced_at":"2025-05-02T10:53:09.018Z","etag":null,"topics":["ghvm-managed"],"latest_commit_sha":null,"homepage":"","language":"Haskell","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/freckle.png","metadata":{"files":{"readme":"README.lhs","changelog":"CHANGELOG.md","contributing":null,"funding":null,"license":"LICENSE","code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":".github/CODEOWNERS","security":null,"support":null,"governance":null,"roadmap":null,"authors":null,"dei":null,"publiccode":null,"codemeta":null}},"created_at":"2024-09-11T15:39:05.000Z","updated_at":"2025-02-07T20:49:44.000Z","dependencies_parsed_at":"2024-12-12T20:32:21.950Z","dependency_job_id":"d43eb94f-8b96-4003-8b07-6e75a85c1bb9","html_url":"https://github.com/freckle/github-app-token","commit_stats":null,"previous_names":["freckle/github-app-token"],"tags_count":5,"template":false,"template_full_name":null,"repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/freckle%2Fgithub-app-token","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/freckle%2Fgithub-app-token/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/freckle%2Fgithub-app-token/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/freckle%2Fgithub-app-token/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/freckle","download_url":"https://codeload.github.com/freckle/github-app-token/tar.gz/refs/heads/main","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":253826707,"owners_count":21970402,"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":["ghvm-managed"],"created_at":"2024-12-27T17:18:40.047Z","updated_at":"2025-05-12T21:40:40.982Z","avatar_url":"https://github.com/freckle.png","language":"Haskell","funding_links":[],"categories":[],"sub_categories":[],"readme":"# GitHub App Token\n\n[![Hackage](https://img.shields.io/hackage/v/github-app-token.svg?style=flat)](https://hackage.haskell.org/package/github-app-token)\n[![Stackage Nightly](http://stackage.org/package/github-app-token/badge/nightly)](http://stackage.org/nightly/package/github-app-token)\n[![Stackage LTS](http://stackage.org/package/github-app-token/badge/lts)](http://stackage.org/lts/package/github-app-token)\n[![CI](https://github.com/freckle/github-app-token/actions/workflows/ci.yml/badge.svg)](https://github.com/freckle/github-app-token/actions/workflows/ci.yml)\n\n[Generate an installation access token for a GitHub App][docs]\n\n[docs]: https://docs.github.com/en/apps/creating-github-apps/authenticating-with-a-github-app/authenticating-as-a-github-app-installation\n\n## Getting an AccessToken\n\n\u003c!--\n```haskell\nmodule Main (module Main) where\n\nimport Configuration.Dotenv qualified as Dotenv\nimport Control.Monad (when)\nimport Data.Traversable (for)\nimport GitHub.App.Token.Refresh\nimport System.Directory (doesFileExist)\nimport Test.Hspec qualified as Hspec\nimport Text.Markdown.Unlit ()\n```\n--\u003e\n\n```haskell\nimport Prelude\n\nimport Data.Aeson (FromJSON)\nimport Data.ByteString.Char8 qualified as BS8\nimport Data.Text (Text)\nimport Data.Text.Encoding (encodeUtf8)\nimport GHC.Generics (Generic)\nimport GitHub.App.Token\nimport Network.HTTP.Simple\nimport Network.HTTP.Types.Header (hAuthorization, hUserAgent)\nimport System.Environment\n\ngetAppToken :: IO AccessToken\ngetAppToken = do\n  appId \u003c- AppId . read \u003c$\u003e getEnv \"GITHUB_APP_ID\"\n  privateKey \u003c- PrivateKey . BS8.pack \u003c$\u003e getEnv \"GITHUB_PRIVATE_KEY\"\n  installationId \u003c- InstallationId . read \u003c$\u003e getEnv \"GITHUB_INSTALLATION_ID\"\n\n  let creds = AppCredentials {appId, privateKey}\n  generateInstallationToken creds installationId\n```\n\n## Using an AccessToken\n\n```haskell\ndata Repo = Repo\n  { name :: Text\n  , description :: Text\n  }\n  deriving stock (Eq, Show, Generic)\n  deriving anyclass FromJSON\n\ngetRepo :: AccessToken -\u003e String -\u003e IO Repo\ngetRepo token name = do\n  req \u003c- parseRequest $ \"https://api.github.com/repos/\" \u003c\u003e name\n  resp \u003c- httpJSON\n    $ addRequestHeader hAuthorization (\"Bearer \" \u003c\u003e encodeUtf8 token.token)\n    $ addRequestHeader hUserAgent \"github-app-token/example\"\n    $ req\n\n  pure $ getResponseBody resp\n```\n\n## Getting a Scoped AccessToken\n\nBy default, a token is created with repositories access and permissions as\ndefined in the installation configuration. Either of these can be changed by\nusing `generateInstallationTokenScoped`:\n\n```haskell\ngetScopedAppToken :: IO AccessToken\ngetScopedAppToken = do\n  appId \u003c- AppId . read \u003c$\u003e getEnv \"GITHUB_APP_ID\"\n  privateKey \u003c- PrivateKey . BS8.pack \u003c$\u003e getEnv \"GITHUB_PRIVATE_KEY\"\n  installationId \u003c- InstallationId . read \u003c$\u003e getEnv \"GITHUB_INSTALLATION_ID\"\n\n  let\n    creds = AppCredentials {appId, privateKey}\n    create = mempty\n      { repositories = [\"github-app-token\"]\n      , permissions = contents Read\n      }\n\n  generateInstallationTokenScoped create creds installationId\n```\n\n## Getting an AccessToken for an Owner\n\n```haskell\ngetOwnerAppToken :: IO AccessToken\ngetOwnerAppToken = do\n  appId \u003c- AppId . read \u003c$\u003e getEnv \"GITHUB_APP_ID\"\n  privateKey \u003c- PrivateKey . BS8.pack \u003c$\u003e getEnv \"GITHUB_PRIVATE_KEY\"\n\n  let creds = AppCredentials {appId, privateKey}\n  generateOwnerToken creds $ Org \"freckle\"\n```\n\n## Getting a Self-Refreshing AccessToken\n\nInstallation tokens are good for one hour, after which point using them will\nrespond with `401 Unauthorized`. To avoid this, you can use the\n`GitHub.App.Token.Refresh` module to maintain a background thread that refreshes\nthe token as necessary:\n\n```haskell\ngetRepos :: [String] -\u003e IO [Repo]\ngetRepos names = do\n  ref \u003c- refreshing getAppToken\n\n  repos \u003c- for names $ \\name -\u003e do\n    token \u003c- getRefresh ref\n    getRepo token name\n\n  cancelRefresh ref\n  pure repos\n```\n\n\u003c!--\n```haskell\nmain :: IO ()\nmain = do\n  isLocal \u003c- doesFileExist \".env\"\n  when isLocal $ Dotenv.loadFile Dotenv.defaultConfig\n\n  Hspec.hspec $ do\n    Hspec.describe \"Basic usage\" $ do\n      Hspec.it \"works\" $ do\n        token \u003c- getAppToken\n        getRepo token \"freckle/github-app-token\"\n          `Hspec.shouldReturn` Repo\n            { name = \"github-app-token\"\n            , description = \"Generate an installation token for a GitHub App\"\n            }\n\n    Hspec.describe \"Scoped usage\" $ do\n      Hspec.it \"works\" $ do\n        token \u003c- getScopedAppToken\n        getRepo token \"freckle/github-app-token\"\n          `Hspec.shouldReturn` Repo\n            { name = \"github-app-token\"\n            , description = \"Generate an installation token for a GitHub App\"\n            }\n\n    Hspec.describe \"By owner\" $ do\n      Hspec.it \"works\" $ do\n        token \u003c- getOwnerAppToken\n        getRepo token \"freckle/github-app-token\"\n          `Hspec.shouldReturn` Repo\n            { name = \"github-app-token\"\n            , description = \"Generate an installation token for a GitHub App\"\n            }\n\n    Hspec.describe \"Self-refreshing tokens\" $ do\n      Hspec.it \"works\" $ do\n        let\n          names :: [String]\n          names =\n            [ \"freckle/github-app-token\"\n            , \"freckle/stack-action\"\n            , \"freckle/stackctl\"\n            ]\n\n        getRepos names `Hspec.shouldReturn`\n          [ Repo {name=\"github-app-token\", description = \"Generate an installation token for a GitHub App\"}\n          , Repo {name=\"stack-action\", description = \"GitHub Action to build, test, and lint Stack-based Haskell projects\"}\n          , Repo {name=\"stackctl\", description = \"Manage CloudFormation Stacks through specifications\"}\n          ]\n```\n--\u003e\n\n---\n\n[CHANGELOG](./CHANGELOG.md) | [LICENSE](./LICENSE)\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Ffreckle%2Fgithub-app-token","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Ffreckle%2Fgithub-app-token","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Ffreckle%2Fgithub-app-token/lists"}