Strange `CaseClauseError`

Hello everyone!

Can somebody help me understanding the following…

Why is it that:

mydata = %{id: user.id, email: user.email, username: user.username}
IO.inspect mydata
jwt = %{data: mydata, sub: user.id}

results in

iex(1)> %{email: "maria@hello.com", id: 4, username: "maria"}

while:

mydata = %{id: user.id, email: user.email, username: user.username}
jwt = %{data: mydata, sub: user.id}
IO.inspect mydata

results in

iex(1)> {:ok,
"eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.e30.B0QRhfQku78gSaxGoC8sxcKVL1wScNCAQGU_lzV4sQ8",
 %{}}

Because you are not showing full code? Perhaps do everything in an iex session.

It seems that there is something happening in the meantime. Could you provide us with Minimal Complete and Verifiable Example? Because right now I can say that “it works on my machine”

@NobbZ, @hauleth
My actual problem that led me to debugging that is the following line:


should return me a token after Postman POST

{
	"email": "juan@hello.com",
	"password": "asdf"
}

to http://localhost:4000/api/session

However, it falls into the rescue section that is why I tried debugging my render view

This is neither minimal nor verifiable example. I still do not get where you put that inspect nor what are the types you are inspecting.

Am sorry @hauleth for being a newbie.
I guess I will try to re-create a new thread and re-phrase my problem. Thanks for the guidance

That is a JWT Token, paste it on jwt.io, but there’s no data inside. So I would guess you are using a JWT token library between your assignment and your IO.inspect.

No need to do this, just update here with more information and we’ll be able to provide a more detailed answer.

2 Likes

Ok @benwilson512. trying now…

@benwilson512, @NobbZ, @hauleth

I have the following code in lib/library_api/library.ex

...
  def get_user_by_email!(email), do: Repo.get_by!(User, email: email)
...

and in my SessionController lib/library_api_web/controllers/session_controller.ex

defmodule LibraryApiWeb.SessionController do
  use LibraryApiWeb, :controller
  alias LibraryApi.Library
  #alias LibraryApi.Library.User

  def create(conn, %{"email" => email, "password" => _password}) do
    case Library.get_user_by_email!(email) do
      {:ok, user} ->
        conn
        |> put_view(LibraryApiWeb.SessionView)
        |> render("token.json", user: user)
      {:error, _reason} ->
        conn
        |> put_status(:unauthorized)
        |> put_view(LibraryApiWeb.ErrorView)
        |> render("401.json-api", %{detail: "Error logging in a user with that email and password"})
    end
  end
end

and in my SessionView: lib/library_api_web/views/session_view.ex

defmodule LibraryApiWeb.SessionView do
  use LibraryApiWeb, :view
  #import Joken.Config

  def render("token.json", %{user: user}) do
    mydata = %{id: user.id, email: user.email, username: user.username}
    jwt = %{data: mydata, sub: user.id}
    |> Joken.generate_and_sign(Joken.Signer.create("HS512", "SOMESECRETVALUE"))
    %{token: jwt.token}
  end
end

In my database I already was able have the following data:

{
	"data": {
		"type": "users",
		"attributes": {
			"email": "maria@hello.com",
			"username": "maria",
			"password": "asdf",
			"password_confirmation": "asdf"
		}
	}
}

NOTE: password & password_confirmation are virtual fields

Using Postman, I am trying to POST to http://localhost:4000/api/session the ff. data:

{
	"email": "maria@hello.com",
	"password": "asdf"
}

that should return a token.
Instead I encounter the following error in my iex console:

iex(1)> [info] POST /api/session
iex(1)> [debug] Processing with LibraryApiWeb.SessionController.create/2
  Parameters: %{"email" => "maria@hello.com", "password" => "[FILTERED]"}
  Pipelines: [:api]
iex(1)> [debug] QUERY OK source="users" db=0.0ms
SELECT u0."id", u0."email", u0."password_hash", u0."username", u0."inserted_at", u0."updated_at" FROM "users" AS u0 WHERE (u0."email" = $1) ["maria@hello.com"]
iex(1)> [info] Converted error {:case_clause, %LibraryApi.Library.User{__meta__: #Ecto.Schema.Metadata<:loaded, "users">, email: "maria@hello.com", id: 4, inserted_at: ~N[2019-05-12 08:41:18], password: nil, password_confirmation: nil, password_hash: "$2b$12$7qHpuOzPJaJShZoO4DCmAe9K/vHvsGwGPRlVmGzHQUlLkCBpgOZ82", updated_at: ~N[2019-05-12 08:41:18], username: "maria"}} to 500 response
iex(1)> [info] Sent 500 in 109ms
iex(1)> [error] #PID<0.435.0> running LibraryApiWeb.Endpoint (connection #PID<0.434.0>, stream id 1) terminated
Server: localhost:4000 (http)
Request: POST /api/session
** (exit) an exception was raised:
    ** (CaseClauseError) no case clause matching: %LibraryApi.Library.User{__meta__: #Ecto.Schema.Metadata<:loaded, "users">, email: "maria@hello.com", id: 4, inserted_at: ~N[2019-05-12 08:41:18], password: nil, password_confirmation: nil, password_hash: "$2b$12$7qHpuOzPJaJShZoO4DCmAe9K/vHvsGwGPRlVmGzHQUlLkCBpgOZ82", updated_at: ~N[2019-05-12 08:41:18], username: "maria"}
        (library_api) lib/library_api_web/controllers/session_controller.ex:7: LibraryApiWeb.SessionController.create/2
        (library_api) lib/library_api_web/controllers/session_controller.ex:1: LibraryApiWeb.SessionController.action/2
        (library_api) lib/library_api_web/controllers/session_controller.ex:1: LibraryApiWeb.SessionController.phoenix_controller_pipeline/2
        (library_api) lib/library_api_web/endpoint.ex:1: LibraryApiWeb.Endpoint.instrument/4
        (phoenix) lib/phoenix/router.ex:275: Phoenix.Router.__call__/1
        (library_api) lib/library_api_web/endpoint.ex:1: LibraryApiWeb.Endpoint.plug_builder_call/2
        (library_api) lib/library_api_web/endpoint.ex:1: LibraryApiWeb.Endpoint.call/2
        (phoenix) lib/phoenix/endpoint/cowboy2_handler.ex:33: Phoenix.Endpoint.Cowboy2Handler.init/2
        (cowboy) d:/@@@/@gilbertc77/library_api/deps/cowboy/src/cowboy_handler.erl:41: :cowboy_handler.execute/2
        (cowboy) d:/@@@/@gilbertc77/library_api/deps/cowboy/src/cowboy_stream_h.erl:296: :cowboy_stream_h.execute/3
        (cowboy) d:/@@@/@gilbertc77/library_api/deps/cowboy/src/cowboy_stream_h.erl:274: :cowboy_stream_h.request_process/3

        (stdlib) proc_lib.erl:249: :proc_lib.init_p_do_apply/3

get_user_by_email! returns a %LibraryApi.Library.User{} not {:ok, %LibraryApi.Library.User{}}, so your case in your controller doesn’t match.

Also, you probably know this, but never store user passwords in the database unhashed.

3 Likes

I understand about the password but it is being hashed somewhere but did not include it coz my problem was the exception being thrown. What is the best way to match it @benwilson512?

Well, let’s think about the control flow you might want. get_user_by_email! uses Repo.get_by!. What happens if it doesn’t return any records? It raises an exception. This means that you’d need to handle it with try / catch in your controller, but this probably isn’t what you want. So I’d get rid of the ! on get_by as well as on get_user_by_email to start with.

So now the question: what does get_by return? Well, it returns either the struct that that you asked for, or nil. So you need to handle the result out of get_user_by_email based on whether the result is nil or not. if is one option, pattern matching on %Library.User{} is another. I’ll leave the actual refactoring of the controller action as an exercise for the reader.

2 Likes

@benwilson512 thanks for the response!
I tried removing the ! at both get_user_by_email and get_by.
I also tried refactoring the SessionController to:

case Library.get_user_by_email(email) do
   {user} ->
      conn
      |> put_view(LibraryApiWeb.SessionView)
      |> render("token.json", user: user)
   {nil} ->
      conn
      |> put_status(:unauthorized)
      |> put_view(LibraryApiWeb.ErrorView)
      |> render("401.json-api", %{detail: "Error logging in a user with that email and password"})
   {:error, _reason} ->
      conn
      |> put_status(:unauthorized)
      |> put_view(LibraryApiWeb.ErrorView)
      |> render("401.json-api", %{detail: "Error logging in a user with that email and password"})
end

but the following error bugged me:

iex(1)> [info] POST /api/session
iex(1)> [debug] Processing with LibraryApiWeb.SessionController.create/2
  Parameters: %{"email" => "maria@hello.com", "password" => "[FILTERED]"}
  Pipelines: [:api]
iex(1)> [debug] QUERY OK source="users" db=0.0ms
SELECT u0."id", u0."email", u0."password_hash", u0."username", u0."inserted_at", u0."updated_at" FROM "users" AS u0 WHERE (u0."email" = $1) ["maria@hello.com"]
iex(1)> [info] Converted error {:case_clause, %LibraryApi.Library.User{__meta__: #Ecto.Schema.Metadata<:loaded, "users">, email: "maria@hello.com", id: 4, inserted_at: ~N[2019-05-12 08:41:18], password: nil, password_confirmation: nil, password_hash: "$2b$12$7qHpuOzPJaJShZoO4DCmAe9K/vHvsGwGPRlVmGzHQUlLkCBpgOZ82", updated_at: ~N[2019-05-12 08:41:18], username: "maria"}} to 500 response
iex(1)> [info] Sent 500 in 78ms
iex(1)> [error] #PID<0.451.0> running LibraryApiWeb.Endpoint (connection #PID<0.450.0>, stream id 1) terminated
Server: localhost:4000 (http)
Request: POST /api/session
** (exit) an exception was raised:
    ** (CaseClauseError) no case clause matching: %LibraryApi.Library.User{__meta__: #Ecto.Schema.Metadata<:loaded, "users">, email: "maria@hello.com", id: 4, inserted_at: ~N[2019-05-12 08:41:18], password: nil, password_confirmation: nil, password_hash: "$2b$12$7qHpuOzPJaJShZoO4DCmAe9K/vHvsGwGPRlVmGzHQUlLkCBpgOZ82", updated_at: ~N[2019-05-12 08:41:18], username: "maria"}
        (library_api) lib/library_api_web/controllers/session_controller.ex:7: LibraryApiWeb.SessionController.create/2
        (library_api) lib/library_api_web/controllers/session_controller.ex:1: LibraryApiWeb.SessionController.action/2
        (library_api) lib/library_api_web/controllers/session_controller.ex:1: LibraryApiWeb.SessionController.phoenix_controller_pipeline/2
        (library_api) lib/library_api_web/endpoint.ex:1: LibraryApiWeb.Endpoint.instrument/4
        (phoenix) lib/phoenix/router.ex:275: Phoenix.Router.__call__/1
        (library_api) lib/library_api_web/endpoint.ex:1: LibraryApiWeb.Endpoint.plug_builder_call/2
        (library_api) lib/library_api_web/endpoint.ex:1: LibraryApiWeb.Endpoint.call/2
        (phoenix) lib/phoenix/endpoint/cowboy2_handler.ex:33: Phoenix.Endpoint.Cowboy2Handler.init/2
        (cowboy) d:/@@@/@gilbertc77/library_api/deps/cowboy/src/cowboy_handler.erl:41: :cowboy_handler.execute/2
        (cowboy) d:/@@@/@gilbertc77/library_api/deps/cowboy/src/cowboy_stream_h.erl:296: :cowboy_stream_h.execute/3
        (cowboy) d:/@@@/@gilbertc77/library_api/deps/cowboy/src/cowboy_stream_h.erl:274: :cowboy_stream_h.request_process/3

        (stdlib) proc_lib.erl:249: :proc_lib.init_p_do_apply/3

There is a line in that iex console dump that catches my eye

iex(1)> [info] Converted error {:case_clause, %LibraryApi.Library.User{__meta__: #Ecto.Schema.Metadata<:loaded, "users">, email: "maria@hello.com", id: 4, inserted_at: ~N[2019-05-12 08:41:18], password: nil, password_confirmation: nil, password_hash: "$2b$12$7qHpuOzPJaJShZoO4DCmAe9K/vHvsGwGPRlVmGzHQUlLkCBpgOZ82", updated_at: ~N[2019-05-12 08:41:18], username: "maria"}} to 500 response

What does Converted error mean?

Also there was a warning before that:

warning: this clause cannot match because a previous clause at line 8 always matches
  lib/library_api_web/controllers/session_controller.ex:12

Got already beyond the CaseClauseError by editing the SessionController from:

def create(conn, %{"email" => email, "password" => _password}) do
    case Library.get_user_by_email(email) do
      {user} ->
        conn
        |> put_view(LibraryApiWeb.SessionView)
        |> render("token.json", user: user)
      {nil} ->
        conn
        |> put_status(:unauthorized)
        |> put_view(LibraryApiWeb.ErrorView)
        |> render("401.json-api", %{detail: "Error logging in a user with that email and password"})
      {:error, _reason} ->
        conn
        |> put_status(:unauthorized)
        |> put_view(LibraryApiWeb.ErrorView)
        |> render("401.json-api", %{detail: "Error logging in a user with that email and password"})
    end
  end

to

def create(conn, %{"email" => email, "password" => _password}) do
    case Library.get_user_by_email(email) do
      user ->
        conn
        |> put_view(LibraryApiWeb.SessionView)
        |> render("token.json", user: user)
      nil ->
        conn
        |> put_status(:unauthorized)
        |> put_view(LibraryApiWeb.ErrorView)
        |> render("401.json-api", %{detail: "Error logging in a user with that email and password"})
      {:error, _reason} ->
        conn
        |> put_status(:unauthorized)
        |> put_view(LibraryApiWeb.ErrorView)
        |> render("401.json-api", %{detail: "Error logging in a user with that email and password"})
    end
  end

This means my controller is already working and now another error is hounding me concerning JWT using Joken in my SessionView:

  def render("token.json", %{user: user}) do
    mydata = %{id: user.id, email: user.email, username: user.username}
    jwt = %{data: mydata, sub: user.id}
    |> Joken.generate_and_sign(nil, Joken.Signer.create("HS512", "SOMESECRETVALUE"))
    #IO.inspect jwt
    %{token: jwt.token}
  end

the error is:

iex(1)> [info] Converted error :badarg to 500 response
iex(1)> [info] Sent 500 in 94ms
iex(1)> [error] #PID<0.449.0> running LibraryApiWeb.Endpoint (connection #PID<0.448.0>, stream id 1) terminated
Server: localhost:4000 (http)
Request: POST /api/session
** (exit) an exception was raised:
    ** (ArgumentError) argument error
        :erlang.apply({:ok, "eyJhbGciOiJIUzUxMiIsInR5cCI6IkpXVCJ9.e30.OcmgLBuda2S8amZVN2V7ux2RdWJJKZiSZAv2IBndBQjuiWyoHmge8bmufXrA92wPCZLO0xXEA-b41fy7oOWNOg", %{}}, :token, [])
        (library_api) lib/library_api_web/views/session_view.ex:10: LibraryApiWeb.SessionView.render/2
        (phoenix) lib/phoenix/view.ex:399: Phoenix.View.render_to_iodata/3
        (phoenix) lib/phoenix/controller.ex:729: Phoenix.Controller.__put_render__/5
        (phoenix) lib/phoenix/controller.ex:746: Phoenix.Controller.instrument_render_and_send/4
        (library_api) lib/library_api_web/controllers/session_controller.ex:1: LibraryApiWeb.SessionController.action/2
        (library_api) lib/library_api_web/controllers/session_controller.ex:1: LibraryApiWeb.SessionController.phoenix_controller_pipeline/2
        (library_api) lib/library_api_web/endpoint.ex:1: LibraryApiWeb.Endpoint.instrument/4
        (phoenix) lib/phoenix/router.ex:275: Phoenix.Router.__call__/1
        (library_api) lib/library_api_web/endpoint.ex:1: LibraryApiWeb.Endpoint.plug_builder_call/2
        (library_api) lib/library_api_web/endpoint.ex:1: LibraryApiWeb.Endpoint.call/2
        (phoenix) lib/phoenix/endpoint/cowboy2_handler.ex:33: Phoenix.Endpoint.Cowboy2Handler.init/2
        (cowboy) d:/@@@/@gilbertc77/library_api/deps/cowboy/src/cowboy_handler.erl:41: :cowboy_handler.execute/2
        (cowboy) d:/@@@/@gilbertc77/library_api/deps/cowboy/src/cowboy_stream_h.erl:296: :cowboy_stream_h.execute/3
        (cowboy) d:/@@@/@gilbertc77/library_api/deps/cowboy/src/cowboy_stream_h.erl:274: :cowboy_stream_h.request_process/3

        (stdlib) proc_lib.erl:249: :proc_lib.init_p_do_apply/3

How can I extract the token from the jwt?

Use IO.inspect on the jwt value to figure out what its shape is if you can’t determine that from the error message. Then, based on its shape, consider how you might want to change jwt = so that jwt is bound to just the token string and not the entire return result.

1 Like

@benwilson512 thanks I have the following code now for my SessionView:

defmodule LibraryApiWeb.SessionView do
  use LibraryApiWeb, :view

  def render("token.json", %{user: user}) do
    data = %{id: user.id, email: user.email, username: user.username}
    jwt = %{data: data, sub: user.id}
    theToken = Joken.generate_and_sign!(jwt, nil, Joken.Signer.create("HS512", "SOMESECRETVALUE"))
    %{token: theToken}
  end
end

The current problem that I am facing is that the generated token is invalid.
What could I be doing wrong with Joken this time?

You have a variable here theToken. Do you understand what it is? What type of datastructure is it? A string? A map? A tuple?

1 Like

It is a string @benwilson512.
That last line produces:

{
    "token": "eyJhbGciOiJIUzUxMiIsInR5cCI6IkpXVCJ9.e30.OcmgLBuda2S8amZVN2V7ux2RdWJJKZiSZAv2IBndBQjuiWyoHmge8bmufXrA92wPCZLO0xXEA-b41fy7oOWNOg"
}

Ah yes, that is correct now, you changed from Joken.generate_and_sign which you had in the previous post to Joken.generate_and_sign!. Joken.generate_and_sign returns a tuple, Joken.generate_and_sign! returns a string. However if you changed that it shoul dhave solved yout :erlang.apply error, so what’s the new error you have?

2 Likes