Cookie set is always empty when it is fetched in plug

Hi,

Why is that when I’m trying to fetch the cookies in my plug it always says.

[debug] Plug.Session could not verify incoming session cookie. This may happen when the session settings change or a stale cookie is sent.

When user is created, a token will be generated to put it in httpOnly cookie which will be used in the auth plug.

  def create(conn, %{"name" => name}) do
    player = Player.new(name)
    token = Auth.generate_token(player)

    conn
    |> put_resp_cookie("current_player", token,
      http_only: true,
      max_age: 864_000
    )
    |> render("user.json", player: player, token: token)
  end

Auth plug implementation.

 def call(conn, _opts) do
    token = fetch_cookies(conn) |> Map.from_struct() |> get_in([:cookies, "current_player"])
    Logger.debug("Fetched token: #{token}")

    case Shiritori.Auth.authenticate(token) do
      #code omitted
    end
end

In the client I call the api endpoint which will pipe through the auth plug.

fetch(`${process.env.REACT_APP_API_URL}/session/current_user`, {
      method: "GET",
      mode: "cors",
      credentials: "include",
    })
   .then((response) => response.json())
   .then((data) => {
        console.log(data);
    });

Is the token a valid cookie?

According to Plug.Conn — Plug v1.15.2

The cookie value must be a binary and that the cookie value is not automatically escaped, unless signing or encryption is enabled. Therefore if you want to store values with non-alphanumeric characters, you must either sign or encrypt the cookie (see the upcoming section) or consider explicitly escaping the cookie value…

I try signing the cookie but I always get nil when fetching it using `fetch_cookies(conn, ~w(current_player))

Here’s what I do to generated a signed cookie.

def create(conn, %{"name" => name}) do
    player = Player.new(name)
    token = Auth.generate_token(player)
    conn
    |> put_resp_cookie("current_player", token,
      http_only: true,
      max_age: 864_000,
      signed: true # also try sign:true
    )
    |> render("user.json", player: player, token: token)
end

Then in my plug, I will get the token from the signed cookie then do the authentication

    token =
      fetch_cookies(conn, signed: ~w(current_player))
      |> Map.from_struct()
      |> get_in([:cookies, "current_player"])
  end

but it always return nil.

What does the network tab in your browser say when you attempt to make the request? If you see a cookie header attached to the request then you know it’s an issue with the backend. If you don’t see the cookie header in the request, it’s on the frontend.

The correct option is not signed: true, but sign: true.