ueberauth_mastodon View Source

Log into Mastodon and Pleroma with Überauth.

This library makes it easy to spin up Elixir microservices to run alongside your social media website.

You can configure one or more Mastodon/Pleroma servers as login options.

Usage guide

Configuration

# Tesla
config :tesla, adapter: Tesla.Adapter.Hackney

# Ueberauth
config :ueberauth, Ueberauth,
  providers: [
    # You will create routes matching the provider name:
    # - /auth/mastodon
    # - /auth/mastodon/callback
    mastodon: {Ueberauth.Strategy.Mastodon, [
      # instance: "https://example.tld",
      # client_id: "********",
      # client_secret: "********"
    ]},

    # This one will be at /auth/gleasonator
    gleasonator:
      {Ueberauth.Strategy.Mastodon,
       [
         # You MUST provide an instance.
         instance: "https://gleasonator.com",
         # You MUST provide app credentials.
         # Generate your app before getting started.
         client_id: "3WCR-5e3nOg2SJ90W134VLIIwmib2T96qsXWSJAAEUs",
         client_secret: "r-vCWcOk_7IY202yYMMgEHEVEtd5Gv4tlByZqVChRm0"
       ]}
  ]

Tesla

Under the hood, ueberauth_mastodon uses Tesla to make HTTP requests.

Tesla is not an HTTP client itself, but a flexible layer for switchable HTTP clients. In this guide we use Hackney, but you can use whatever you want. Just don't leave it blank.

Options

  • instance (required) - A URL to the Mastodon/Pleroma instance.
  • client_id (required) - Generated by an app. Create the app first.
  • client_secret (required) - Generated by an app. Create the app first.
  • redirect_uri - Override the redirect URL. By default it goes to /auth/:provider/callback
  • scope - Space-separated list of scopes, eg read write follow. It defaults to read.
  • uid_field - Which field from Mastodon API to map to Überauth. It's set to "url" (the ActivityPub ID) by default.

Routes

You'll need to create matching routes in router.ex:

scope "/auth", PatronWeb do
  pipe_through [:browser, Ueberauth]

  get "/mastodon", AuthController, :request
  get "/mastodon/callback", AuthController, :callback

  # You don't have to have more than one, but you can have any number
  get "/gleasonator", AuthController, :request
  get "/gleasonator/callback", AuthController, :callback
end

The Ueberauth plug will match names from your config and intercept the conn before it arrives at your controller.

You do not need to implement the :request view in your controller. Just put a link to /auth/:provider somewhere on your website, and it will redirect to the OAuth signup page.

You must provide a :callback view.

Controller

You'll need to create a controller to handle the callback. The OAuth form will redirect here.

defmodule PatronWeb.AuthController do
  use PatronWeb, :controller

  alias Ueberauth.Auth
  alias Ueberauth.Auth.Credentials
  alias Ueberauth.Failure
  alias Ueberauth.Failure.Error

  # /auth/:provider/callback
  # After the user authorizes the OAuth form, they'll be redirected back here.
  def callback(
        # An `:ueberauth_auth` key is provided upon success.
        # It contains a `%Ueberauth.Auth{}` struct.
        # https://hexdocs.pm/ueberauth/Ueberauth.Auth.html#t:t/0
        %{assigns: %{ueberauth_auth: %Auth{uid: uid, credentials: %Credentials{} = credentials}}} = conn,
        _params
      ) do
    conn
    # Store the credentials in a cookie, or anywhere else
    |> put_session(:token_data, credentials)
    |> put_session(:uid, uid)
    |> redirect(to: "/")
  end

  def callback(
        # Upon failure, you'll get `:ueberauth_failure`.
        # It contains a `%Ueberauth.Failure{}` struct.
        # https://hexdocs.pm/ueberauth/Ueberauth.Failure.html#t:t/0
        %{assigns: %{ueberauth_failure: %Failure{errors: [%Error{message: message} | _]}}} = conn,
        _params
      ) do
    conn
    |> put_flash(:error, message)
    |> redirect(to: "/")
  end

  # If neither exist, just redirect home
  def callback(conn, _params) do
    redirect(conn, to: "/")
  end
end

Authentication Plug

Finally, you'll likely want to create a plug to authenticate the user on pageload. This is one possible way:

defmodule PatronWeb.Plugs.BootstrapUser do
  import Plug.Conn
  alias Patron.User
  alias Ueberauth.Auth.Credentials
  alias Ueberauth.Strategy.Mastodon

  @behaviour Plug

  def init(_), do: nil

  def call(conn, _) do
    # Get the token set from the callback
    case get_session(conn, :token_data) do
      nil -> conn
      # Make an HTTP request
      %Credentials{token: token} -> verify_token(conn, token)
      # Delete invalid token
      _ -> delete_session(conn, :token_data)
    end
  end

  # Fetch the account from the token
  defp verify_token(conn, token) do
    # The uid is an ActivityPub ID, which serves as a convenient base URL
    with %Credentials{uid: ap_id} <- get_session(conn, :token_data),
         {:ok, %{status: 200, body: %{"url" => ap_id} = data}} <-
           Mastodon.API.account_verify_credentials(ap_id, token) do
      conn
      |> assign(:user_data, data)
    else
      _ -> delete_session(conn, :token_data)
    end
  end
end

And in the router:

pipeline :browser do
  # ...
  plug PatronWeb.Plugs.BootstrapUser
end

Installation

Add ueberauth, ueberauth_mastodon, and a Tesla adapter to your list of dependencies in mix.exs:

def deps do
  [
    {:ueberauth, "~> 0.7.0"},
    {:ueberauth_mastodon, "~> 0.1.0"},

    # For `Tesla.Adapter.Hackney` to work
    {:hackney, "~> 1.18"}
  ]
end

License

ueberauth_mastodon is licensed under the MIT license. See LICENSE.md for the full text.