# Mastodon Client
WARNING
This part of the documentation is based on few tests have made. Please be careful if you want to use that in production.
# Resources and References
- https://docs.joinmastodon.org/api/
- https://github.com/scrogson/oauth2
# Gihub Webhook
WARNING
This documentation was not totally tested and is only based on a debugging session.
The first part of the webhook is pretty straighforward. You can create a new controller and define what kind of action the webhook will do.
defmodule MyApp.WebhookController do
use FriendlyHookWeb, :controller
require Logger
def valid_signature(value, body) do
secret = Application.fetch_env!(:my_app, MyApp.Endpoint)
|> Keyword.get(:github)
|> Keyword.get(:secret)
hashed_secret = :crypto.hmac(:sha256, secret, body)
|> Base.encode16
|> String.downcase
final = "sha256=" <> hashed_secret
final == value
end
def index(conn, param) do
[github_signature|_] = get_req_header(conn, "x-hub-signature-256")
body = conn.assigns[:raw_body]
Logger.debug(body)
case valid_signature(github_signature, body) do
true -> valid_connection(conn, param)
false -> conn |> put_status(403) |> json(%{})
end
end
defp valid_connection(conn, param) do
json(conn, %{ :status => :ok })
end
end
The second part is to validate the signature. To do that, your
application and Phoenix in particular will require to have access to
the raw body part of the payload. Unfortunately, by default, this
value is not available in the conn parameter.
lib/endpoint.ex is containing all information about the default
Plug but also many other configuration. The interesting part here is
Plug.Parsers, this module will read the raw body and convert it in
the desired format. If you follow the
documentation (opens new window)
about this solution, it will not give you where to add this
behavior. So, here the original Plug:
plug Plug.Parsers,
parsers: [:urlencoded, :multipart, :json],
pass: ["*/*"],
json_decoder: Phoenix.json_library()
Now, this is the new one, crafted to use CacheBodyReader module to
store the raw body directly in the connection.
plug Plug.Parsers,
parsers: [:urlencoded, :multipart, :json],
pass: ["*/*"],
body_reader: {CacheBodyReader, :read_body, []},
json_decoder: Jason
You can create the CacheBodyReader anywhere you want, I created it
in lib/cache_body_reader.ex:
defmodule CacheBodyReader do
def read_body(conn, opts) do
{:ok, body, conn} = Plug.Conn.read_body(conn, opts)
conn = update_in(conn.assigns[:raw_body], &[body | (&1 || [])])
{:ok, body, conn}
end
end
So, now, you will have access also to the raw body by using this function:
raw_body = conn.assigns[:raw_body]
Reload your application and you have now a secure way to deal with Github webhooks.