michael_teter

michael_teter

How to externalize user authentication within Phoenix?

Can someone with current experience suggest a path on how to get started externalizing user authentication within Phoenix? I would like to give my users the ability to register/sign-in using Google, Facebook, etc.

Further, if I integrate with enough external providers, I assume that means I could (potentially?) forego entirely the responsibility of storing/managing users passwords. Is that a reasonable assumption?

There is a lot of information about this topic, but most of it seems dated… and the Elixir/Phoenix world moves quickly. I’d like to avoid starting on a dead-end path :).

Thanks in advance!

Most Liked

danschultzer

danschultzer

Pow Core Team

Do you still want to register the users? I wrote CoherenceAssent to handle multi-provider login. While it’s built to use Coherence, I’ve been using it for just Github OAuth login in an admin interface where I just store the user information in the session. The controller looks like this:

defmodule MyAdminApp.AuthController do
  use MyAdminApp, :controller

  @organization "my-organization"

  @spec new(Conn.t, map) :: Conn.t
  def new(conn, _params) do
    config = Keyword.put(config(), :redirect_uri, callback_url(conn))
    {:ok, %{conn: conn, url: url}} = CoherenceAssent.Strategy.Github.authorize_url(conn, config)

    redirect(conn, external: url)
  end

  @spec callback(Conn.t, map) :: Conn.t
  def callback(conn, params) do
    params = Map.merge(%{"redirect_uri" => callback_url(conn)}, params)

    conn
    |> CoherenceAssent.Strategy.Github.callback(config(), params)
    |> verify_organization()
    |> login_or_redirect()
  end

  defp login_or_redirect({:ok, %{conn: conn, user: user}}) do
    conn
    |> put_session(:current_admin_user, user)
    |> redirect(to: index_path(conn, :index))
  end
  defp login_or_redirect({:error, %{error: error}}) do
    raise error
  end

  defp config do
    config = Application.get_env(:my_admin_app, :admin_github_provider) || raise "Need to set :admin_github_provider first!"
    Keyword.put(config, :authorization_params, [scope: "read:user,user:email,read:org"])
  end

  defp verify_organization({:error, error}), do: {:error, error}
  defp verify_organization({:ok, %{conn: conn, client: client, user: user}}) do
    case OAuth2.Client.get(client, "/user/orgs") do
      {:ok, %OAuth2.Response{body: orgs}} ->
        case Enum.find_value(orgs, &(&1["login"] == @organization)) do
          true -> {:ok, %{conn: conn, user: user}}
          _    -> {:error, %{error: "#{user["nickname"]} is not a member of #{@organization}"}}
        end

      {:error, error} ->
        {:error, %{conn: conn, error: error}}
    end
  end

  defp callback_url(conn) do
    auth_url(conn, :callback)
  end
end

And the plug looks like this:

defmodule MyAdminApp.Plug.RequireLogin do
  @moduledoc false

  import Plug.Conn
  import Phoenix.Controller, only: [redirect: 2]
  alias MyAdminApp.Router.Helpers

  @behaviour Plug

  @dialyzer [
    {:nowarn_function, call: 2},
    {:nowarn_function, init: 1},
  ]

  @spec init(Keyword.t) :: [tuple]
  def init(options) do
    %{option: options}
  end

  @spec call(Plug.Conn.t, any) :: Plug.Conn.t
  def call(conn, _opts) do
    user = get_session(conn, :current_admin_user)

    if user do
      conn
      |> assign(:current_admin_user, user)
    else
      conn
      |> redirect(to: Helpers.auth_path(conn, :new))
      |> halt
    end
  end

end

Routes:

# ...
  pipeline :protected do
    plug NomadRentalAdmin.Plug.RequireLogin
  end

  scope "/", MyAdminApp do
    pipe_through [:browser]

    get "/auth", AuthController, :new
    get "/auth/callback", AuthController, :callback
  end
# ...

It’s a very basic setup (since we’re just using it to log in to an internal admin interface with our Github organization), but might be something like this you’re looking for?

If you want to register the users still then I recommend checking out the readme for CoherenceAssent. It takes a minute, and then you can remove the ability to change the password on user admin page.

kokolegorille

kokolegorille

Did You try ueberauth?

Kurisu

Kurisu

Great work!
That is what I’m looking for. I’m using Phauxth instead of Coherence but after what I saw on your own library Pow, I will definitely give it a try.

Thank you for sharing this. ^^

Last Post!

danschultzer

danschultzer

Pow Core Team

Thanks! I hope it works for you.

PowAssent has been improved to not be dependent on Plug, so if you use it without any user registration like in the example above, the API is a little different. But if you use Pow for user registration then there’s nothing you have to think about :smile:

Where Next?

Popular in Questions Top

hariharasudhan94
I would like to know what is the best IDE for elixir development?
New
ovidiubadita
Hey all, I discovered Elixir and I love it. I always wanted to learn a functional programming and I intended to go for Haskell, but afte...
New
jononomo
For some reason my phoenix channels are working for me in my local dev environment, but as soon as I deploy via Docker, I get a 403 error...
New
lanycrost
Hi everyone! I need implement if…else if…else condition from my elixir code, and anymore of this control flow structures not work proper...
New
9mm
I am constructing a JSON object (map) and I need to conditionally set a field. I’m trying to write proper elixir-way code… and I’m at a l...
New
belgoros
I’m not a pro in using Regex and can’t figure out why the following behaviour happens, especially if we take into account the difference ...
New
fayddelight
I tried installing elixir 1.11.2 erlang 23.3.4 via asdf in my zsh shell. Enabled the versions locally and globally. When I list them ...
New

Other popular topics Top

nobody
Hi! In PHP: $_SERVER[‘SERVER_ADDR’] - in Elixir? Searched the docs for ip address and the web, no good results. Thanks!
New
Qqwy
Original source of discussion: This topic on the Pragmatic Programmers’ Functional Web Development with Elixir, OTP, and Phoenix forum. ...
New
alice
Hey, Just curious what are the main benefits of Elixir compared to Clojure? When is Elixir more useful than Clojure and vice versa? Th...
New
saif
Hello everyone, Long time lurker first time poster here. I’ve recently begun working on Elixir full-time again! :raised_hands: It’s been...
New
AngeloChecked
What learn first? Rust or Elixir Hi Elixir community! I’m here because i want learn a new language. I’m a junior developer and mainly i ...
New
sergio
Kind of like when jquery came out, it was super necessary. Existing drag and drop libraries have a bunch of baggage to support old browse...
New

We're in Beta

About us Mission Statement