Guardian Library issue - (Plug.Conn.AlreadySentError) the response was already sent

Hey everyone!

Actually i am getting following error in Elixir Guardian library. I tried many solutions and double check my code but everything seems ok. I don’t know where the problem is :slightly_frowning_face:

error] #PID<0.635.0> running SportsconnectWeb.Endpoint (connection #PID<0.634.0>, stream id 1) terminated
Server: localhost:4000 (http)
Request: POST /api/users/posts/comments/reply
** (exit) an exception was raised:
    ** (Plug.Conn.AlreadySentError) the response was already sent
        (phoenix 1.6.13) lib/phoenix/controller.ex:529: Phoenix.Controller.put_new_layout/2
        (sportsconnect 0.1.0) lib/sportsconnect_web/controllers/comments_controller.ex:1: SportsconnectWeb.CommentsController.phoenix_controller_pipeline/2
        (phoenix 1.6.13) lib/phoenix/router.ex:354: Phoenix.Router.__call__/2
        (sportsconnect 0.1.0) lib/plug/error_handler.ex:80: SportsconnectWeb.Router.call/2
        (sportsconnect 0.1.0) lib/sportsconnect_web/endpoint.ex:1: SportsconnectWeb.Endpoint.plug_builder_call/2
        (sportsconnect 0.1.0) lib/plug/debugger.ex:136: SportsconnectWeb.Endpoint."call (overridable 3)"/2
        (sportsconnect 0.1.0) lib/sportsconnect_web/endpoint.ex:1: SportsconnectWeb.Endpoint.call/2
        (phoenix 1.6.13) lib/phoenix/endpoint/cowboy2_handler.ex:54: Phoenix.Endpoint.Cowboy2Handler.init/4
        (cowboy 2.9.0) /home/dev/sportsconnect/deps/cowboy/src/cowboy_handler.erl:37: :cowboy_handler.execute/2
        (cowboy 2.9.0) /home/dev/sportsconnect/deps/cowboy/src/cowboy_stream_h.erl:306: :cowboy_stream_h.execute/3
        (cowboy 2.9.0) /home/dev/sportsconnect/deps/cowboy/src/cowboy_stream_h.erl:295: :cowboy_stream_h.request_process/3
        (stdlib 4.0.1) proc_lib.erl:240: :proc_lib.init_p_do_apply/3

Hi Alia, if you do not show your code we can’t really help you.

1 Like

@benwilson512 i am getting this issue when my cookie or my session get expire. whenever i am using halt() at the end of the response message this error got fixed but i don’t know why ?

Any guess about it ?

defmodule SportsConnect.Guardian.AuthPipeline do
  @claims %{"typ" => "access"}

  alias SportsConnect.Models.User

  use Guardian.Plug.Pipeline,
    otp_app: :sportsconnect,
    module: SportsConnect.Guardian,
    error_handler: SportsConnect.Guardian.AuthErrorHandler

  plug Guardian.Plug.VerifySession, claims: @claims
  plug :load_user

  # Load the current user if user is logged in the system.
  def load_user(%{cookies: %{"access" => access_token}} = conn, _) do
    user = SportsConnect.Guardian.resource_from_token(access_token)

    case user do
      {:ok, %User{id: user_id}, _claims} ->
        user = Sportsconnect.Repo.get(User, user_id)
        Plug.Conn.assign(conn, :current_user, user)

      _ ->
        body = Jason.encode!(%{error: "Please Login to Continue"})
        send_resp(conn, 401, body) |> halt
    end
  end

  def load_user(conn, _) do
    body = Jason.encode!(%{error: "Please Login to Continue"})
    send_resp(conn, 401, body) |> halt
  end
end

As you said yourself, you have to use Plug.Conn.halt/1 otherwise plug will continue to process the other plugs that come after.

send_response does not halt / end your plug pipeline :slight_smile:

1 Like

This is not the right way to use Guardian. You should use the EnsureAuthenticated plug to ensure they are signed in, and handle any errors in your AuthErrorHandler.

However, it looks like you are only storing the user id in the token, in which case you can probably remove Guardian completely and just store the user id directly in the session, avoiding all the complexity and indirection that comes with using Guardian.

1 Like

@adamu actually i am putting my token received after from Guardian in my Cookie. If i will completely remove the Guardian then what will i store in my cookie ? make sense ?

conn = Plug.Conn.put_session(conn, :user_id, user.id)
1 Like

okay and how i will save my session for some time ? as well as how i will verify it at the time of sign in ?

The session is stored, encrypted, in the browser. The code I wrote saves it, that’s all you have to do.

user_id = Plug.Conn.get_session(conn, :user_id)

The session cookie is encrypted by Phoenix and can be trusted, the end user is not able to set the user_id

1 Like

yes @benwilson512 you are right. Yeah i should save the user-id into my session. But in every request i need to verify that some one is login or not means i need to check and verify the session ?

In guardian i am doing it in an AuthPipeline but in your idea where i will do these things ?

The Programming Phoenix book has a chapter where it walks you through setting up user authentication.

There’s also phx.gen.auth.

1 Like

Thanks @benwilson512 @kwando @adamu for your help. You guys are amazing. :slightly_smiling_face: