Olympus Docs
IntegrateBackends

Phoenix (Elixir) integration

Authenticate Phoenix routes against Olympus

Phoenix integrates with Olympus via standard OIDC. Use Ueberauth with the OIDC strategy.

Setup

mix.exs:

defp deps do
  [
    {:ueberauth, "~> 0.10"},
    {:ueberauth_oidcc, "~> 0.3"}
  ]
end

Config

config/config.exs:

config :ueberauth, Ueberauth,
  providers: [
    olympus: {Ueberauth.Strategy.Oidcc, [
      issuer: System.get_env("OLYMPUS_ISSUER"),
      scopes: ["openid", "profile", "email"]
    ]}
  ]

config :ueberauth, Ueberauth.Strategy.Oidcc.OAuth,
  client_id: System.get_env("OLYMPUS_CLIENT_ID"),
  client_secret: System.get_env("OLYMPUS_CLIENT_SECRET"),
  redirect_uri: System.get_env("OLYMPUS_REDIRECT_URI")

Router

defmodule YourAppWeb.Router do
  use YourAppWeb, :router

  pipeline :auth do
    plug Ueberauth
  end

  scope "/auth", YourAppWeb do
    pipe_through [:browser, :auth]
    get "/:provider", AuthController, :request
    get "/:provider/callback", AuthController, :callback
    post "/logout", AuthController, :logout
  end
end

Controller

defmodule YourAppWeb.AuthController do
  use YourAppWeb, :controller
  plug Ueberauth

  def callback(%{assigns: %{ueberauth_auth: auth}} = conn, _params) do
    user = %{
      sub: auth.uid,
      email: auth.info.email,
      name: auth.info.name
    }
    conn
    |> put_session(:current_user, user)
    |> redirect(to: "/")
  end

  def logout(conn, _params) do
    conn
    |> clear_session()
    |> redirect(external: "https://ciam.your-domain/oauth2/sessions/logout")
  end
end

API token validation

For API endpoints:

defmodule YourAppWeb.TokenAuth do
  import Plug.Conn

  def init(opts), do: opts

  def call(conn, _opts) do
    with ["Bearer " <> token] <- get_req_header(conn, "authorization"),
         {:ok, info} <- introspect(token),
         true <- info["active"] do
      assign(conn, :user_sub, info["sub"])
    else
      _ -> conn |> send_resp(401, ~s({"error":"unauth"})) |> halt()
    end
  end

  defp introspect(token) do
    HTTPoison.post(
      "#{System.get_env("OLYMPUS_ISSUER")}/admin/oauth2/introspect",
      "token=#{token}",
      [{"content-type", "application/x-www-form-urlencoded"}],
      hackney: [basic_auth: {System.get_env("HYDRA_ADMIN_USER"), System.get_env("HYDRA_ADMIN_PASS")}]
    )
    |> case do
      {:ok, %{body: body}} -> Jason.decode(body)
      _ -> {:error, :introspection_failed}
    end
  end
end

Phoenix LiveView

For LiveView pages, lift the session into the socket:

def on_mount(:default, _params, session, socket) do
  user = session["current_user"]
  if user, do: {:cont, assign(socket, :current_user, user)}, else: {:halt, redirect(socket, to: "/auth/olympus")}
end

On this page