View Source GitHub.Webhook (GitHub REST API Client v0.3.3)

Helpers for validating and handling webhooks dispatched by GitHub

This module is made for Plug / Phoenix applications that wish to accept webhook requests that are sent from the GitHub API. Receiving these requests requires setting up a webhook or GitHub App, which is out of scope for this documentation.

configuration

Configuration

In order to validate that incoming webhooks are, in fact, from GitHub, it is strongly advised to set a webhook secret. This is a secret value that you generate and supply to GitHub in the webhook or GitHub App settings. If you are using Phoenix, an easy way to generate this value is by running mix phx.gen.secret.

Then, supply this value to the library at runtime using the following configuration:

config :oapi_github,
  webhook_secret: "my secret"

It is common to use System.fetch_env!/1 or a similar function to load this type of secret from environment variables. Alternatively, the webhook secret can be supplied directly to verify_github_signature/2 at compile time.

usage

Usage

If the optional dependencies Plug and Plug.Crypto are installed, this module provides several helpers to simplify the process of handling webhook requests. A typical webhook controller might look like this:

defmodule MyAppWeb.GitHubController do
  use MyAppWeb, :controller
  import GitHub.Webhook

  plug :verify_github_event
  plug :verify_github_signature

  def webhook(conn, params) do
    # Handle the webhook...
  end
end

In order to perform verification of the signature, it is necessary to store the original request body in an assign :raw_body or :body. This usually requires implementing a custom Plug.Parser for webhook requests.

See verify_github_event/2 and verify_github_signature/2 for more information.

Link to this section Summary

Functions

Construct the value of the :body_reader option for Plug.Parsers for caching request bodies

Cache request body as :raw_body assign for chosen connections

Get and store the GitHub webhook event type

Check the validity of a GitHub webhook request

Link to this section Functions

@spec body_reader(keyword()) :: tuple()

Construct the value of the :body_reader option for Plug.Parsers for caching request bodies

This function, called at compile time, creates a valid value for the :body_reader option in a call to the Plug.Parsers plug. Usually called in a Phoenix Endpoint, the code looks like:

plug Plug.Parsers,
  parsers: [:json, ...],
  json_decoder: Jason,
  # ...
  body_reader: GitHub.Webhook.body_reader()

It sets up cache_request_body/2 as the body reader, which will in turn cache the raw request body as an assign :raw_body for use during signature verification.

options

Options

  • :fallback: If using the :routes option below, which body reader function to call for requests that do not match one of the specified routes. Defaults to {Plug.Conn, :read_body, []}.

  • :routes: Optionally restrict the caching of request bodies to specific routes, given as a list of strings (ex. ["/hook/github"]). Defaults to caching request bodies for all requests, which can cause a significant performance impact.

Link to this function

cache_request_body(conn, parser_opts, cache_opts)

View Source
@spec cache_request_body(Plug.Conn.t(), keyword(), keyword()) ::
  {:ok, binary(), Plug.Conn.t()} | {:error, term()}

Cache request body as :raw_body assign for chosen connections

This function adheres to the needs of the :body_reader option for the Plug.Parsers plug. It saves the raw request body as a binary to the :raw_body assign on the Plug.Conn, so that it can be used for signature verification later.

This function is not usually called directly. Instead, use body_reader/1.

Link to this function

verify_github_event(conn, opts)

View Source
@spec verify_github_event(
  Plug.Conn.t(),
  keyword()
) :: Plug.Conn.t()

Get and store the GitHub webhook event type

This function looks at the X-GitHub-Event header to ensure the incoming request is a webhook event and stores the event as a :github_event assign on the connection. If the header is missing, the connection is immediately halted with a simple error message that will appear in GitHub's UI.

Link to this function

verify_github_signature(conn, opts)

View Source
@spec verify_github_signature(
  Plug.Conn.t(),
  keyword()
) :: Plug.Conn.t()

Check the validity of a GitHub webhook request

This function uses the configured :webhook_secret or an option :secret to verify the signature of an incoming GitHub webhook. If the signature is missing or invalid, the connection is immediately halted with a simple error message that will appear in GitHub's UI.

configuration

Configuration

  • :webhook_secret: Secret given to GitHub to use when signing webhook requests. If not supplied via configuration nor the :secret option, an error is raised.

options

Options

  • :secret: Compile-time secret to use as the webhook secret. If not supplied at compile time nor via the :webhook_secret configuration, an error is raised.