Ecosyste.ms: Awesome
An open API service indexing awesome lists of open source software.
https://github.com/nubank/clj-github-app
A library to implement GitHub Apps in Clojure.
https://github.com/nubank/clj-github-app
authentication client clojure github webhook
Last synced: about 2 hours ago
JSON representation
A library to implement GitHub Apps in Clojure.
- Host: GitHub
- URL: https://github.com/nubank/clj-github-app
- Owner: nubank
- License: epl-1.0
- Created: 2018-08-21T16:49:42.000Z (about 6 years ago)
- Default Branch: master
- Last Pushed: 2024-08-07T19:25:13.000Z (3 months ago)
- Last Synced: 2024-10-28T13:38:17.478Z (20 days ago)
- Topics: authentication, client, clojure, github, webhook
- Language: Clojure
- Homepage: https://developer.github.com/apps/about-apps/#about-github-apps
- Size: 67.4 KB
- Stars: 45
- Watchers: 654
- Forks: 4
- Open Issues: 1
-
Metadata Files:
- Readme: README.md
- Changelog: CHANGELOG.md
- License: LICENSE
Awesome Lists containing this project
README
# clj-github-app
[![Build Status](https://travis-ci.org/nubank/clj-github-app.svg?branch=master)](https://travis-ci.org/nubank/clj-github-app)
[![codecov](https://codecov.io/gh/nubank/clj-github-app/branch/master/graph/badge.svg)](https://codecov.io/gh/nubank/clj-github-app)
[![Clojars Project](https://img.shields.io/clojars/v/nubank/clj-github-app.svg)](https://clojars.org/nubank/clj-github-app)A library to implement [GitHub Apps] in Clojure.
```clj
[nubank/clj-github-app "0.2.0"]
```Includes:
* [Webhook payload signature checker][webhook-signatures] with [secure comparison](https://github.com/weavejester/crypto-equality).
* API client with HTTP connection pool.
* Access token manager with caching ([Authenticating with GitHub Apps] is tricky).## Usage
### Checking webhook signatures
When implementing a webhook handler, it is recommended to check the webhook request signature before processing it.
Please read the [official documentation][webhook-signatures] first.Imagine you have a webhook handler:
```clj
(ns your-project.webhooks
(:require [clj-github-app.webhook-signature :as webhook-signature]))(def GITHUB_WEBHOOK_SECRET (System/getenv "GITHUB_WEBHOOK_SECRET"))
(defn post-github
"Checks if the webhook is valid and handles it."
[request]
(let [{:strs [x-github-delivery x-github-event x-hub-signature-256]} (:headers request)
payload (slurp (:body request))]
(case (webhook-signature/check-payload-signature-256 GITHUB_WEBHOOK_SECRET x-hub-signature-256 payload)
::webhook-signature/missing-signature {:status 400 :body "x-hub-signature-256 header is missing"}
::webhook-signature/wrong-signature {:status 401 :body "x-hub-signature-256 does not match"}
(let [parsed-payload (json/parse-string payload keyword)]
;; process your webhook here
{:status 200 :body "This is fine."}))))
```The key part here is the call to `check-payload-signature-256`. It takes 3 arguments:
* `webhook-secret` — the exact secret string that you set when configuring webhook for your repo.
If this argument is blank or nil, `check-payload-signature-256` will do nothing and return
`:clj-github-app.webhook-signature/not-checked`.
* `x-hub-signature-256` — contents of "X-Hub-Signature-256" request header.
* `payload` — request body as a string.Possible return values:
* `:clj-github-app.webhook-signature/ok` — signature matches the payload.
* `:clj-github-app.webhook-signature/wrong-signature` — signature does not match the payload.
* `:clj-github-app.webhook-signature/missing-signature` — `x-hub-signature` parameter was blank or nil.
* `:clj-github-app.webhook-signature/not-checked` — no check was done because `webhook-secret` parameter was blank or nil.### Authenticating as a GitHub App
Please read [Authenticating with GitHub Apps] official documentation first.
Example (uses [mount-lite]):
```clj
(ns your-project.external.github
(:require [mount.lite :as m]
[clj-github-app.client :as client]))(def GITHUB_API_URL "https://api.github.com")
(def GITHUB_APP_ID (System/getenv "GITHUB_APP_ID"))
(def GITHUB_APP_PRIVATE_KEY_PEM (System/getenv "GITHUB_APP_PRIVATE_KEY_PEM"))(m/defstate client
:start (client/make-app-client GITHUB_API_URL GITHUB_APP_ID GITHUB_APP_PRIVATE_KEY_PEM {})
:stop (.close @client))
````clj-github-app.client/make-app-client` takes 4 parameters:
* `github-api-url` — Base URL of GitHub API. Usually `https://api.github.com` or something like `https://github.example.com/api/v3` for GHE.
* `github-app-id` — GitHub App ID as string (can be found on the app settings page).
* `private-key-pem-str` — String contents of the private key file that you [generated when configuring the app](https://developer.github.com/apps/building-github-apps/authenticating-with-github-apps/#generating-a-private-key).
* `connection-pool-opts` — [clj-http connection pool parameters](https://github.com/dakrone/clj-http#persistent-connections).
Can be set to `{}` to use all defaults.It returns an object that implements `AutoCloseable` interface and `AppClient` protocol, which has the following functions:
* `request*` — to [authenticate as an installation][as-installation].
Given `installation-id` and `opts`, makes an HTTP request to GitHub API, automatically retrieving an access token.
Uses [clj-http], `opts` argument is given to `request` function as described [here](https://github.com/dakrone/clj-http#raw-request).
`opts` is supposed to include `:method` and `:url` keys.
This function is the main workhorse.
* `request` — same as `request*`, but has separate arguments for method and URL.
* `app-request*` — to [authenticate as a GitHub App][as-app].
This is only useful for querying app metadata.
* `app-request` — same as `app-request*`, but has separate arguments for method and URL.You can [authenticate as a GitHub App][as-app]:
```clj
(client/app-request* @client {:method :get :url "/app" :accept "application/vnd.github.machine-man-preview+json"})
(client/app-request @client :get "/app" {:accept "application/vnd.github.machine-man-preview+json"})
```You can also [authenticate as an installation][as-installation]. For this you need Installation ID,
(which is usually given to you in webhook payloads):```clj
(client/request* @client 42 {:method :get :url "/repos/myname/myrepo/issues/123/comments")
(client/request @client 42 :get "/repos/myname/myrepo/issues/123/comments" {})
```All these functions can accept either a full URL or just a relative path, which will be automatically appended to the base
GitHub API URL, given earlier to `make-app-client`.
The "path only" mode is useful when you are constructing the URL yourself and don't want to repeat the base API URL there.
The path can start with a `/` or not, which makes no difference, both cases are handled the same way.
The "full URL" mode is useful when you use a URL extracted from a webhook payload
and don't want to strip the base URL part from there.```clj
;; Use github-api-url (provided earlier to make-app-client) as base API URL
(client/app-request @client :get "foo" {})
(client/app-request @client :get "/foo" {})
;; The same call, but without relying on github-api-url
(client/app-request @client :get "https://api.github.com/foo" {})
```#### Convenience wrappers for API endpoints
This library does not provide any wrappers like
```clj
(list-issue-comments "owner" "repo" "123" {:since "2018-01-01"})
```Such wrappers are really easy to implement on your own:
```clj
(defn create-list-issue-comments-request [owner repo issue-number params]
{:method :get
:url (format "/repos/%s/%s/issues/%s/comments" owner repo issue-number)
:query-params params})
```and then use like this:
```clj
(client/request @client 42 (create-list-issue-comments-request "owner" "repo" "123" {:since "2018-01-01"}))
```Full GitHub API reference can be found [here](https://developer.github.com/v3/).
## Development
With every commit, add important changes from it to the "Unreleased" section of _CHANGELOG.md_.
### Release procedure
Run `lein release` as described below, depending on how much changes are made since previous release.
Library version will be updated in _project.clj_ and _README.md_ automatically after calling `lein release`.
`## Unreleased` section int _CHANGELOG.md_ will be automatically changed into the version being released.lein release :patch
# or
lein release :minor
# or
lein release :major## License
Copyright © 2018 Dmitrii Balakhonskii
Distributed under the Eclipse Public License version 1.0.
[GitHub Apps]: https://developer.github.com/apps/about-apps/#about-github-apps
[Authenticating with GitHub Apps]: https://developer.github.com/apps/building-github-apps/authenticating-with-github-apps/
[webhook-signatures]: https://developer.github.com/webhooks/securing/#validating-payloads-from-github
[as-app]: https://developer.github.com/apps/building-github-apps/authenticating-with-github-apps/#accessing-api-endpoints-as-a-github-app
[as-installation]: https://developer.github.com/apps/building-github-apps/authenticating-with-github-apps/#accessing-api-endpoints-as-an-installation
[clj-http]: https://github.com/dakrone/clj-http
[mount-lite]: https://github.com/aroemers/mount-lite