Protocol Enumerable not implemented for error with guardian login

Hey I a trying to make a login system for my phoenix app, but I am having an error on successful login attempt:
# protocol Enumerable not implemented for %Userteam1.Web.User{__meta__: #Ecto.Schema.Metadata<:loaded, "users">, id: 1, inserted_at: ~N[2018-08-19 09:10:38.661295], name: "bence", password: nil, password_hash: "$2b$12$EEH/mgGuM1GyKHkUSIJG..THKJ6W/0iN/tWH6FiikZ4dRjGqPwt3G", role: #Ecto.Association.NotLoaded<association :role is not loaded>, role_id: 1, team: #Ecto.Association.NotLoaded<association :team is not loaded>, team_id: nil, updated_at: ~N[2018-08-19 09:10:38.661303]}. This protocol is implemented for: DBConnection.PrepareStream, DBConnection.Stream, Date.Range, Ecto.Adapters.SQL.Stream, File.Stream, Function, GenEvent.Stream, HashDict, HashSet, IO.Stream, List, Map, MapSet, Postgrex.Stream, Range, Stream

my controller for login looks like this:

def create(conn, %{"session" => %{"name" => name, "password" => password}}) do
    case Userteam1.Web.name_password_auth(name, password) do
      {:ok, user} ->
        IO.inspect(user)

        conn
        |> put_flash(:info, "Successfully signed in")
        |> Guardian.encode_and_sign(user)
        |> redirect(to: user_path(conn, :index))

      {:error, _reason} ->
        conn
        |> put_flash(:error, "Invalid name or password")
        |> render("new.html")
    end
  end

and my schema that it is trying to load looks like this:

  schema "users" do
    field(:name, :string)
    field(:password_hash, :string)
    field(:password, :string, virtual: true)
    belongs_to(:role, Userteam1.Role)
    belongs_to(:team, Userteam1.Team)
    timestamps()
  end

How can this be fixed?

Guardian.encode_and_sign/4 does not return a Plug.Conn.t, nor anything that would implement the Enumerable protocol.

Please give the Getting Started of guardian another read and try to incorporate the things you learned there into your application.

1 Like

ok found something, I changed it into this:
|> Guardian.Plug.sign_in(user)
but no I am having a new error:
(Guardian.Plug.UnauthenticatedError) {:error, :secret_not_found}

Did you config guardian ? (config.exs)

config :my_app, MyApp.Guardian,
       issuer: "my_app",
       secret_key: "Secret key. You can use `mix guardian.gen.secret` to get one"

missed to change this part, thanks

I’ve got the same error (so frustrating!):

Protocol.UndefinedError at POST /login

protocol Enumerable not implemented for %Dog.UserManager.User{meta: ecto.Schema.Metadata<:loaded, β€œusers”>, id: 3, inserted_at: ~N[2019-07-15 05:56:22], password: β€œ$argon2id$v=19$m=131072,t=8,p=4$jinhp9gEDVENO2PIdm5zkg$ovideTURD59gCoqtE6UUuYZ/32YMnpgppuRf0NFAJ/k”, updated_at: ~N[2019-07-15 05:56:22], username: β€œme”}. This protocol is implemented for: Ecto.Adapters.SQL.Stream, Postgrex.Stream, DBConnection.Stream, DBConnection.PrepareStream, HashSet, Range, Map, Function, List, Stream, Date.Range, HashDict, GenEvent.Stream, MapSet, File.Stream, IO.Stream

The stack trace seems to point to my SessionController:

* elixir /home/build/elixir/lib/elixir/lib/enum.ex:1Enumerable.impl_for!/1
* elixir /home/build/elixir/lib/elixir/lib/enum.ex:141Enumerable.reduce/3
* elixir lib/enum.ex:3015Enum.reverse/1
* elixir lib/enum.ex:2647Enum.to_list/1
* elixir lib/map.ex:181Map.new_from_enum/1
* guardian lib/guardian.ex:573Guardian.encode_and_sign/4
* guardian lib/guardian/plug.ex:208Guardian.Plug.sign_in/5
* lib/dog_web/controllers/session_controller.ex:29DogWeb.SessionController.login_reply/2
* lib/dog_web/controllers/session_controller.ex:1DogWeb.SessionController.action/2
* lib/dog_web/controllers/session_controller.ex:1DogWeb.SessionController.phoenix_controller_pipeline/2
* phoenix lib/phoenix/router.ex:288Phoenix.Router.__call__/2
* lib/dog_web/endpoint.ex:1DogWeb.Endpoint.plug_builder_call/2
* lib/plug/debugger.ex:122DogWeb.Endpoint."call (overridable 3)"/2
* lib/dog_web/endpoint.ex:1DogWeb.Endpoint.call/2
* phoenix lib/phoenix/endpoint/cowboy2_handler.ex:40Phoenix.Endpoint.Cowboy2Handler.init/2
* cowboy /Users/7stud/phoenix_apps/dog/deps/cowboy/src/cowboy_handler.erl:41:cowboy_handler.execute/2
* cowboy /Users/7stud/phoenix_apps/dog/deps/cowboy/src/cowboy_stream_h.erl:296:cowboy_stream_h.execute/3
* cowboy /Users/7stud/phoenix_apps/dog/deps/cowboy/src/cowboy_stream_h.erl:274:cowboy_stream_h.request_process/3

Here’s my SessionController:

defmodule DogWeb.SessionController do
  use DogWeb, :controller
  alias Dog.{UserManager, UserManager.User, UserManager.Guardian}

  def new(conn, _) do
    changeset = UserManager.change_user(%User{})
    maybe_user = Guardian.Plug.current_resource(conn)
    if maybe_user do
      redirect(conn, to: "/secret")
    else
      render(conn, "new.html",
             changeset: changeset,
             action: Routes.session_path(conn, :login)
      ) 
    end
  end

  def login(conn, 
            %{"user" => %{"username" => username,
                          "password" => password}})
  do
    UserManager.authenticate_user(username, password)
    |> login_reply(conn)
  end

  def login_reply({:ok, user}, conn) do
    conn
    |> put_flash(:info, "Welcome back!")
    |> Guardian.Plug.sign_in(Guardian, user)
    |> redirect(to: "/secret")
  end
  def login_reply({:error, reason}, conn) do
    conn
    |> put_flash(:error, to_string(reason))
    |> new(%{})
  end

  def logout(conn, _) do
    conn
    |> Guardian.Plug.sign_out(Guardian)
    |> redirect(to: "/login")
  end

end

I checked the Guardian.Plug docs for the sign_in() function:

sign_in(conn, impl, resource, claims \\ %{}, opts \\ []) 

sign_in(
  Plug.Conn.t(),
  module(),
  any(),
  Guardian.Token.claims(),
  Guardian.options()
) :: Plug.Conn.t()

And, I"m calling sign_in() with the correct types for the arguments. I’m stuck.

Here’s my config.exs:

use Mix.Config

config :dog,
  ecto_repos: [Dog.Repo]

config :dog, Dog.Repo,
  datbase: "dog",
  username: "7stud",
  password: "",
  hostname: "localhost",
  port: "5432"

config :dog, Dog.UserManager.Guardian,
  issuer: "dog",
  secret_key: "e8UPW0Doh0iBonm8ZK0RJVzaAA8r+Jgw4t3lVEoB1SiByke/V9cxcz//1iPkpVrq"

# Configures the endpoint
config :dog, DogWeb.Endpoint,
  url: [host: "localhost"],
  secret_key_base: "xpVFSaAFJZ8xUEhWLrzT/sTvL/Z6y+mSvfobiKiKanWexNwL2Wj2EbRXr7V9fQSj",
  render_errors: [view: DogWeb.ErrorView, accepts: ~w(html json)],
  pubsub: [name: Dog.PubSub, adapter: Phoenix.PubSub.PG2]

# Configures Elixir's Logger
config :logger, :console,
  format: "$time $metadata[$level] $message\n",
  metadata: [:request_id]

# Use Jason for JSON parsing in Phoenix
config :phoenix, :json_library, Jason

# Import environment specific config. This must remain at the bottom
# of this file so it overrides the configuration defined above.
import_config "#{Mix.env()}.exs"

I’m using a UserManager context for the public interface:

defmodule Dog.UserManager do
  @repo Dog.Repo
  alias Dog.UserManager.User
  alias Argon2

  import Ecto.Query, only: [from: 2]

  def list_users() do
    @repo.all(User)
  end

  def get_user(id) do
    @repo.get!(User, id)
  end

  def change_user(%User{}=user) do
    User.changeset(user, %{})
  end

  def insert_user(attrs) do
    %User{}
    |> User.changeset(attrs)
    |> @repo.insert()
  end

  def delete_user(%User{}=user) do
    @repo.delete(user)
  end

  def authenticate_user(username, plain_text_password) do
    query = from u in User, where: u.username == ^username
    case @repo.one(query) do
      nil ->
        Argon2.no_user_verify()
        {:error, :invalid_credentials}
      user ->
        if Argon2.verify_pass(plain_text_password, user.password) do
          {:ok, user}
        else
          {:error, :invalid_credentials}
        end
    end
  end


end

Here’s my directory structure:

~/phoenix_apps/dog$ tree lib
lib
β”œβ”€β”€ dog
β”‚   β”œβ”€β”€ application.ex
β”‚   β”œβ”€β”€ repo.ex
β”‚   └── user_manager
β”‚       β”œβ”€β”€ error_handler.ex
β”‚       β”œβ”€β”€ guardian.ex
β”‚       β”œβ”€β”€ pipeline.ex
β”‚       β”œβ”€β”€ user.ex
β”‚       └── user_manager.ex
β”œβ”€β”€ dog.ex
β”œβ”€β”€ dog_web
β”‚   β”œβ”€β”€ channels
β”‚   β”‚   └── user_socket.ex
β”‚   β”œβ”€β”€ controllers
β”‚   β”‚   β”œβ”€β”€ page_controller.ex
β”‚   β”‚   └── session_controller.ex
β”‚   β”œβ”€β”€ endpoint.ex
β”‚   β”œβ”€β”€ gettext.ex
β”‚   β”œβ”€β”€ router.ex
β”‚   β”œβ”€β”€ templates
β”‚   β”‚   β”œβ”€β”€ layout
β”‚   β”‚   β”‚   └── app.html.eex
β”‚   β”‚   β”œβ”€β”€ page
β”‚   β”‚   β”‚   β”œβ”€β”€ index.html.eex
β”‚   β”‚   β”‚   └── protected.html
β”‚   β”‚   └── session
β”‚   β”‚       └── new.html.eex
β”‚   └── views
β”‚       β”œβ”€β”€ error_helpers.ex
β”‚       β”œβ”€β”€ error_view.ex
β”‚       β”œβ”€β”€ layout_view.ex
β”‚       β”œβ”€β”€ page_view.ex
β”‚       └── session_view.ex
└── dog_web.ex

Your app actually fails at this line: https://github.com/ueberauth/guardian/blob/9ed23acbef1ddfde5a79c139570fada9399b0877/lib/guardian.ex#L573

Looks like you are passing your user as claims.

1 Like
  1. How do you know that?

  2. As far as I can tell, I’m passing the default for claims, which is %{}:

 conn
    |> put_flash(:info, "Welcome back!")
    |> Guardian.Plug.sign_in(Guardian, user)

put_flash() returns a conn, so I am calling:

Guardian.Plug.sign_in(conn, Guardian, user)

and based on the definition of sign_in():

sign_in(conn, impl, resource, claims \\ %{}, opts \\ [])

I’m actually calling:

Guardian.Plug.sign_in(conn, Guardian, user, %{}, [])

How do you know that?

See the stacktrace:

* elixir /home/build/elixir/lib/elixir/lib/enum.ex:1Enumerable.impl_for!/1
* elixir /home/build/elixir/lib/elixir/lib/enum.ex:141Enumerable.reduce/3
* elixir lib/enum.ex:3015Enum.reverse/1
* elixir lib/enum.ex:2647Enum.to_list/1
* elixir lib/map.ex:181Map.new_from_enum/1
* guardian lib/guardian.ex:573Guardian.encode_and_sign/4 # <-- this is where user=claims 
* guardian lib/guardian/plug.ex:208Guardian.Plug.sign_in/5
* lib/dog_web/controllers/session_controller.ex:29DogWeb.SessionController.login_reply/2

Try passing all arguments to sign_in without relying on the defaults, something strange is going on:

conn
|> put_flash(:info, "Welcome back!")
|> Guardian.Plug.sign_in(Guardian, user, %{}, [])
1 Like

I tried this:

  def login_reply({:ok, user}, conn) do
    put_flash(conn, :info, "Welcome back!")
    new_conn = Guardian.Plug.sign_in(conn, Guaridan, user, %{}, [])
    redirect(new_conn, to: "/secret")
  end
  def login_reply({:error, reason}, conn) do
    conn
    |> put_flash(:error, to_string(reason))
    |> new(%{})
  end

And, I got this warning:


~/phoenix_apps/dog$ mix phx.server

warning: function Dog.UserManager.Guardian.Plug.sign_in/5 is undefined or private. Did you mean one of:

      * sign_in/2
      * sign_in/3
      * sign_in/4

  lib/dog_web/controllers/session_controller.ex:28

...
...

In iex:

~/phoenix_apps/dog$ iex -S mix
Erlang/OTP 20 [erts-9.3] [source] [64-bit] [smp:4:4] [ds:4:4:10] [async-threads:10] [hipe] [kernel-poll:false]
Interactive Elixir (1.8.2) - press Ctrl+C to exit (type h() ENTER for help)

iex(1)> h Guardian.Plug.sign_in                  

          def sign_in(conn, impl, resource, claims \\ %{}, opts \\ [])          

  @spec sign_in(
          Plug.Conn.t(),
          module(),
          any(),
          Guardian.Token.claims(),
          Guardian.options()
        ) :: Plug.Conn.t()

iex(2)>

That looks like 5 arguments to me! So, I decided to pick the two most important arguments, and go with that:

new_conn = Guardian.Plug.sign_in(conn, user)

That got rid of the Enumerable error. Now, I’ve got a routing error that I’m working on.

Dog.UserManager.Guardian.Plug != Guardian.Plug

Seems like you’ve confused one with the other because of

alias Dog.{UserManager, UserManager.User, UserManager.Guardian}
4 Likes

First time trying Guardian–I did not know that!

Seems like you’ve confused one with the other because of

alias Dog.{UserManager, UserManager.User, UserManager.Guardian}

And, I checked that line to make sure that Guardian.Plug was indeed equivalent to Dog.UserManager.Guardian.Plug! All that code is from the official Guardian β€œGetting Started Tutorial”.

And, the route problem I was having was due to the tutorial alternately using β€œ/protected” and β€œ/secret” to refer to the Guardian protected route.

By the way, things work when I do:

 alias Dog.{UserManager, UserManager.User, UserManager.Guardian}
 ...
 new_conn = Guardian.Plug.sign_in(conn, user)

So, I guess I was looking at the wrong docs??? This is what my lib/dog/user_manager/guardian.ex file looks like:

defmodule Dog.UserManager.Guardian do
  use Guardian, otp_app: :dog
  alias Dog.UserManager

  def subject_for_token(user, _claims) do
    # You can use any value for the subject of your token but
    # it should be useful in retrieving the resource later, see
    # how it being used on `resource_from_claims/1` function.
    # A unique `id` is a good subject, a non-unique email address
    # is a poor subject.

    {:ok, to_string(user.id)}
  end

  def resource_from_claims(%{"sub" => id}) do
    # Here we'll look up our resource from the claims, the subject can be
    # found in the `"sub"` key. In `above subject_for_token/2` we returned
    # the resource id so here we'll rely on that to look it up.

    case UserManager.get_user(id) do
      nil -> {:error, :user_not_found}
      user -> {:ok, user}
    end
  end

end

I guess the line:

use Guardian, otp_app: :dog

injects a Plug module? I looked at the source code for the Guardian module:

 defmacro __using__(opts \\ []) do
    otp_app = Keyword.get(opts, :otp_app)

    # credo:disable-for-next-line Credo.Check.Refactor.LongQuoteBlocks
    quote do
      @behaviour Guardian

      if Code.ensure_loaded?(Plug) do
        __MODULE__
        |> Module.concat(:Plug)    
        |> Module.create(  ## CREATES THE Dog.UserManager.Guardian.Plug MODULE
          quote do
            use Guardian.Plug, unquote(__MODULE__)
          end,
          Macro.Env.location(__ENV__)
        )
      end

And Guardian.Plug’s __using__() function looks like this:

   defmacro __using__(impl) do
      quote do
        ...
        ...
        def sign_in(conn, resource, claims \\ %{}, opts \\ []),
          do: Guardian.Plug.sign_in(conn, implementation(), resource, claims, opts)

And, there’s the two argument sign_in() function that I lucked into; and, like you said, the way I was calling it originally:

Guardian.Plug.sign_in(conn, Guaridan, user)

matched the parameter variable claims to user.

lol. And the original solution to this question was:

ok found something, I changed it into this:
|> Guardian.Plug.sign_in(user)

Thanks idiot!

2 Likes