Getting ArgumentError in Guardian for encode_and_sign/4

I’m new to Phoenix/Elixir and having problems with getting Guardian to work, and google can’t provide an answer. I’m using Phoenix 1.3 and Elixir 1.5.2

I followed this tutorial https://gist.github.com/nikneroz/ba698a120bc037801f45328855cbdca8 to set up Guardian for my api, but I’m getting this ArgumentError:

[info] POST /api/v1/sign_in
[debug] Processing with FeedstaApiWeb.SessionController.sign_in/2
  Parameters: %{"session" => %{"email" => "new@gmail.com", "password" => "[FILTERED]"}}
  Pipelines: [:api]
[debug] QUERY OK source="users" db=4.6ms decode=0.1ms queue=0.2ms
SELECT u0."id", u0."first_name", u0."last_name", u0."email", u0."password_hash", u0."inserted_at", u0."updated_at" FROM "users" AS u0 WHERE (u0."email" = $1) ["new@gmail.com"]
[info] Sent 500 in 419ms
[error] #PID<0.456.0> running FeedstaApiWeb.Endpoint terminated
Server: localhost:4000 (http)
Request: POST /api/v1/sign_in
** (exit) an exception was raised:
    ** (ArgumentError) argument error
        :erlang.apply(%FeedstaApi.Accounts.User{__meta__: #Ecto.Schema.Metadata<:loaded, "users">, email: "new@gmail.com", first_name: "New", id: 11, inserted_at: ~N[2017-11-20 07:22:00.184085], last_name: "User", password: nil, password_hash: "$2b$12$Fzlf28oDo6YIfaurkYFgsud0NDZZLBIBCm0hRJpzfzJD4bv8nLGym", updated_at: ~N[2017-11-20 07:22:00.184096]}, :config, [])
        (guardian) lib/guardian.ex:575: Guardian.encode_and_sign/4
        (feedsta_api) lib/feedsta_api_web/controllers/session_controller.ex:10: FeedstaApiWeb.SessionController.sign_in/2
        (feedsta_api) lib/feedsta_api_web/controllers/session_controller.ex:1: FeedstaApiWeb.SessionController.action/2
        (feedsta_api) lib/feedsta_api_web/controllers/session_controller.ex:1: FeedstaApiWeb.SessionController.phoenix_controller_pipeline/2
        (feedsta_api) lib/feedsta_api_web/endpoint.ex:1: FeedstaApiWeb.Endpoint.instrument/4
        (phoenix) lib/phoenix/router.ex:278: Phoenix.Router.__call__/1
        (feedsta_api) lib/feedsta_api_web/endpoint.ex:1: FeedstaApiWeb.Endpoint.plug_builder_call/2
        (feedsta_api) lib/plug/debugger.ex:99: FeedstaApiWeb.Endpoint."call (overridable 3)"/2
        (feedsta_api) lib/feedsta_api_web/endpoint.ex:1: FeedstaApiWeb.Endpoint.call/2
        (plug) lib/plug/adapters/cowboy/handler.ex:15: Plug.Adapters.Cowboy.Handler.upgrade/4
        (cowboy) /Users/Frank/Google Drive/Coding/Apps/PhoenixApps/feedsta_api/deps/cowboy/src/cowboy_protocol.erl:442: :cowboy_protocol.execute/4

Here is my session controller:

defmodule FeedstaApiWeb.SessionController do
  use FeedstaApiWeb, :controller
  # use Guardian

  alias FeedstaApi.Accounts

  def sign_in(conn, %{"session" => %{"email" => email, "password" => password}}) do  
    case Accounts.find_and_confirm_password(email, password) do
      {:ok, user} ->
         {:ok, jwt, _full_claims} = Guardian.encode_and_sign(user, :api)

         conn
         |> render("sign_in.json", user: user, jwt: jwt)
      {:error, _reason} ->
        conn
        |> put_status(401)
        |> render("error.json", message: "Username or password is incorrect")
    end
  end  
end

Here is my accounts.ex:

defmodule FeedstaApi.Accounts do

  import Ecto.Query, warn: false
  alias FeedstaApi.Repo

  alias FeedstaApi.Accounts.User


  def list_users do
    Repo.all(User)
  end

  def get_user!(id), do: Repo.get!(User, id)


  def create_user(attrs \\ %{}) do
    %User{}
    |> User.changeset(attrs)
    |> Repo.insert()
  end

  def update_user(%User{} = user, attrs) do
    user
    |> User.changeset(attrs)
    |> Repo.update()
  end

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

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

  # Check if the login credentials are correct
  def find_and_confirm_password(email, password) do
    case Repo.get_by(User, email: email) do
      nil ->
        {:error, :not_found}
      user ->
        if Comeonin.Bcrypt.checkpw(password, user.password_hash) do
          {:ok, user}
        else
          {:error, :unauthorized}
        end
    end
  end
end

I’ve been stuck on this for 3 days so any help would be very much appreciated. This is my first post in a forum so I hope I’ve done it correctly.

1 Like

Tutorial specifies usage of Guardian 0.14, maybe you’re using 1.0 instead? Seems like 1.0 has changed the encode_and_sign function and it expects a different set of arguments now.

2 Likes

Thanks for replying @franciscoj, much appreciated. You are right, I am using Guardian 1.0 and I think I must have mixed things up in my implementation. I have now followed these docs: https://hexdocs.pm/guardian/readme.html#installation and have made it work, but only if I comment out the def resource_from_claims(claims) function in the implementation module suggested in the docs below:

defmodule MyApp.Guardian do
  use Guardian, otp_app: :my_app

  def subject_for_token(resource, _claims) do
    {:ok, to_string(resource.id)}
  end

  def subject_for_token(_, _) do
    {:error, :reason_for_error}
  end

 # def resource_from_claims(claims) do
   # {:ok, find_me_a_resource(claims["sub"])}
 # end

  def resource_from_claims(_claims) do
    {:error, :reason_for_error}
  end
end

If I don’t comment it out, I get this error:

== Compilation error in file lib/feedsta_api/guardian.ex ==
** (CompileError) lib/feedsta_api/guardian.ex:13: undefined function find_me_a_resource/1
    (stdlib) lists.erl:1338: :lists.foreach/2
    (stdlib) erl_eval.erl:670: :erl_eval.do_apply/6
    (elixir) lib/kernel/parallel_compiler.ex:121: anonymous fn/4 in Kernel.ParallelCompiler.spawn_compilers/1

Am I right to surmise that the “find_me_a_resource” part is just a placeholder for a function that I need to write myself? The docs don’t mention anything about this…

1 Like

Great!

I’ve never used Guardian :see_no_evil:, so I can’t be sure… but to me it looks like a placeholder you must fill in with your own logic.

2 Likes

Correct, you need to get your information in however you store it.

Guardian is just a JWT library (well it can handle more server communication stuff now too).

If you don’t know what JWT is or are not using it directly, then you probably should not be using Guardian as it has a lot of overhead over just Phoenix Tokens.

2 Likes

Thanks for your help @franciscoj and also thank you @OvermindDL1 for the clarification, much appreciated - you guys are awesome :slight_smile:

2 Likes