markupguy

markupguy

Authentication with Ueberauth, Google Strategy and Guardian

Hey Everyone,

I’ve been fighting this one for more than a day and decided to reach out. I’m sure it’s something trivial but I cannot seem to fix it :see_no_evil:

I’m building a simple admin to manage Organizations and Accounts. The Ueberauth with Google side seems to work fine, I can see a user being created in my database. However, upon the request coming back and hitting the callback, things go awry (as this is where Guardian comes to play, obviously). If y’all wouldn’t mind affording me a few seconds to look at my code?

router.ex

pipeline :browser_auth do
    plug Admin.Auth.Pipeline
  end

  pipeline :browser_ensure_auth do
    plug Guardian.Plug.EnsureAuthenticated
  end

scope "/auth", AdminWeb do
    pipe_through [:browser, :browser_auth]

    get "/login", SessionController, :login
    get "/logout", SessionController, :delete
    get "/:provider", SessionController, :request
    get "/:provider/callback", SessionController, :create
  end

  scope "/", AdminWeb do
    pipe_through [:browser, :browser_auth, :browser_ensure_auth]

    get "/", DashboardController, :index
    resources "/organizations", OrganizationController, only: [:index, :new, :create, :delete]
    resources "/accounts", AccountController
  end

session_controller.ex

defmodule AdminWeb.SessionController do
  use AdminWeb, :controller
  plug Ueberauth

  alias Admin.Repo
  alias Admin.Accounts.Account

  def login(conn, _params) do
    render conn, "login.html"
  end

  def create(%{assigns: %{ueberauth_auth: auth}} = conn, _params) do
    #map off info struct
    %Ueberauth.Auth.Info{
      email: account_email,
      first_name: account_first_name,
      image: account_photo,
      last_name: account_last_name} = auth.info
    # populate the params, always create as a viewer
    account_params = %{
      token: auth.credentials.token,
      first_name: account_first_name,
      last_name: account_last_name,
      photo: account_photo,
      email: account_email,
      provider: "google",
      roles: ["viewer"]
    }

    changeset = Account.changeset(%Account{}, account_params)

    case insert_or_update_account(changeset) do
      {:ok, account} ->
        Guardian.Plug.current_resource(conn)

        conn
        |> Guardian.Plug.sign_in(account)
        |> put_flash(:info, "Thank you for signing in!")
        |> redirect(to: Routes.dashboard_path(conn, :index))

      {:error, _reason} ->
        conn
        |> put_flash(:error, "Error signing in")
        |> redirect(to: Routes.session_path(conn, :login))
    end
  end

  defp insert_or_update_account(changeset) do
    case Repo.get_by(Account, email: changeset.changes.email) do
      nil ->
        Repo.insert(changeset)
      account ->
        {:ok, account}
    end
  end

  def delete(conn, _params) do
    conn
    |> Guardian.Plug.sign_out()
    |> redirect(to: Routes.session_path(conn, :login))
  end
end

guardian.ex

defmodule Admin.Auth.Guardian do
  use Guardian, otp_app: :admin

  alias Admin.Repo
  alias Admin.Accounts.Account

  def subject_for_token(%Account{} = account, _claims) do
    {:ok, "Account:#{account.id}"}
  end

  def resource_from_claims(claims) do
    IO.inspect claims
    case claims["sub"] do
      "Account:" <> account_id ->
        case Repo.get!(Account, account_id) do
          nil ->
            {:error, :account_not_found}
          account ->
            {:ok, account}
        end
      _ ->
        {:error, :account_not_found}
    end
  end
end

serializer.ex

defmodule Admin.Auth.GuardianSerializer do
  @behaviour Guardian.Serializer

  alias Admin.Repo
  alias Admin.Accounts.Account

  def for_token(account = %Account{}), do: {:ok, "Account:#{account.id}"}
  def for_token(_), do: {:error, "Unknown resource type"}

  def from_token("Account:" <> id), do: {:ok, Repo.get(Account, id)}
  def from_token(_), do: {:error, "Unknown resource type"}
end

pipeline.ex

defmodule Admin.Auth.Pipeline do
  use Guardian.Plug.Pipeline,
    otp_app: :admin,
    module: Admin.Auth.Guardian,
    error_handler: Admin.Auth.ErrorHandler

    @claims %{iss: "Admin"}

  plug Guardian.Plug.VerifySession, claims: @claims
  plug Guardian.Plug.VerifyHeader, claims: @claims, realm: "Bearer"
  plug Guardian.Plug.LoadResource, allow_blank: true
end

error_handler.ex

defmodule Admin.Auth.ErrorHandler do
  import Plug.Conn
  require Logger

  @behaviour Guardian.Plug.ErrorHandler

  @impl Guardian.Plug.ErrorHandler
  def auth_error(conn, {type, reason}, _opts) do
    Logger.warn(Jason.encode!(%{message: to_string(type)}), [])
    Phoenix.Controller.redirect(conn, to: AdminWeb.Router.Helpers.session_path(conn, :login))
  end
end

config.exs

config :guardian, Admin.Auth.Guardian,
  issuer: "Admin",
  verify_issuer: true,
  secret_key: "key",
  serializer: Admin.Auth.GuardianSerializer

I feel like I followed the Getting Started guide pretty closely, but no dice :pensive:

First Post!

wolfiton

wolfiton

Hi,
Welcome to the forum.

Do you get any errors or what do you mean by things go awry also can you post the link of the guide you are following?

Also the more details you provide the better people can offer asisstance

Where Next?

Popular in Questions Top

sen
Hi All, I set a environment variables in dev.exs , like below code. when i start server, how can i set the ${enable} value? thanks. d...
New
senggen
Erlang/OTP 25 [erts-13.2.2] [source] [64-bit] [smp:8:8] [ds:8:8:10] [async-threads:1] 15:22:35.803 [error] gen_event {lager_file_backend...
New
jononomo
I am trying to figure out how Mix knows whether the environment is test, dev, or prod – where is this set? Thanks.
New
vac
Hi, I’m quite new in Elixir and I’m trying to format a string to a PEM format. I have the certificate value like MIIDBTCCAe2...... and I...
New
vrod
I am using the Starship cross-shell prompt – it seems pretty nice, but I get some errors: [WARN] - (starship::utils): Executing command ...
New
freewebwithme
Using vs code and installed ElixirLS: support and debugger. And I got an error popped up on start up says Failed to run ‘elixir’ comma...
New
bsollish-terakeet
Credo is smart enough to check for (something like) this: assert length(the_list) == 0 with this response: Checking if an enum is empt...
New
nsuchy
Hi. I’ve noticed that Windows Powershell has it’s own IEX command and you cannot access Elixir’s IEX due to the conflict. This isn’t a cr...
New
hariharasudhan94
I would like to know what is the best IDE for elixir development?
New
hariharasudhan94
Lets say I have map like this fetching from my database %{"_id" =&gt; #BSON.ObjectId&lt;58eb1a7a9ad169198c3dXXXX&gt;, "email" =&gt; ...
New

Other popular topics Top

New
albydarned
Hello all! I am typing this post from my new MacBook Pro with the M1 chip. I’m loving it so far, and will probably use it as my daily dr...
New
lessless
I believe there are people here who are dealing with CSV files import on the daily basis, and since Excel is a really popular tool there ...
New
gshaw
What is the idiomatic way of matching for not nil in Elixir? E.g., First way: defp halt_if_not_signed_in(conn, signed_in_account) when...
New
dokuzbir
I want to highlight html closing tags when i click a html tag. That works in .html files but doesnt work for html.eex templates. How can...
New
vegabook
I’m brand new to Phoenix and I have stripped one of the demo applications to the bone. I just want to get an svg up on the screen. Here i...
New
stefanluptak
Hello everybody, usually, I use a 29" ultra-wide monitor for VSCode which can easily accomodate explorer (files panel) + file with code ...
New
grych
Hi folks, Few months ago I have announced the proof-of-concept of the library to manipulate the browsers DOM objects directly from Elixi...
639 52341 488
New
AstonJ
We’ve put together this wiki for Phoenix LiveView - please feel free to add any info you feel is worth including. What is Phoenix LiveV...
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

Latest on Elixir Forum

We're in Beta

About us Mission Statement