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: "********",
# scope: "read write follow"
]},
# 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",
scope: "read write follow"
]}
]
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.scope
- Space-separated list of scopes, egread write follow
. It defaults toread
.
Advanced
redirect_uri
- Override the redirect URL. By default it goes to/auth/:provider/callback
uid_field
- Which field from Mastodon API to map to Überauth. It's set to"url"
(the ActivityPub ID) by default.
Runtime configuration
All configuration options are strings. For runtime configuration, it's possible to pass values that will be evaulated to strings:
- string, eg
"123456"
- {m, f, a} tuple, eg
{System, :get_env, ["CLIENT_SECRET"]}
- function, eg
fn -> System.get_env("CLIENT_SECRET") end
# Runtime configuration
config :ueberauth, Ueberauth,
providers: [
mastodon: {Ueberauth.Strategy.Mastodon, [
# Just a plain old hardcoded string
instance: "https://example.tld",
# {module, function, args} format
client_id: {System, :get_env, "MASTODON_CLIENT_ID"},
# Anonymous function
client_secret: fn -> System.get_env("MASTODON_CLIENT_SECRET") end
]}
]
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.
request/2
You do not need to implement the request/2
view in your controller. The plug intercepts it and does a redirect before it hits your controller.
Just put a link to /auth/:provider
somewhere on your website, and it will redirect to the OAuth signup page.
callback/2
You must provide a callback/2
view.
def callback(%{assigns: %{ueberauth_auth: auth} = conn, _params) do
# TODO: Store the auth somewhere
conn
end
Controller
You'll need to create a controller to handle the callback. Below is an example of a full controller.
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.