Tutorial
Phoenix LiveView and Invalid CSRF token
This post was updated 27 Mar
One issue that is common to run into is a CSRF error when posting some sort of form rendered with LiveView. The issue is that a LiveView component is mounted twice. First time when the component is rendered by the initial request. Then it has the correct CSRF token. Next time, when it connects to the socket, it gets the wrong CSRF token. Or at least wrong in the sense that it doesn’t match the one in the page header.
STEP 1
So, to fix this, you need to set up the LiveView connection so it uses the same session options. Open endpoint.ex and change to:
# lib/my_app_web/endpoint.ex
@session_options [
store: :cookie,
key: "_my_app_key",
signing_salt: "4qBWyMiY"
]
socket "/live", Phoenix.Live.View.Socket, websocket: [connect_info: [session: @session_options]]
...
plug Plug.Session, @session_options
Now that I have hooked up my session to the LiveView socket connection, I can create a plug that assigns the correct CSRF token to a session cookie on every page request. I need to create a plug with the content:
# lib/my_app_web/plugs/generate_csrf.ex
defmodule MyAppWeb.GenerateCSRF do
import Plug.Conn, only: [put_session: 3]
def init(_opts), do: nil
def call(conn, _opts), do: put_session(conn, :csrf_token, Phoenix.Controller.get_csrf_token())
end
And mount that plug in the router:
# lib/my_app_web/router.ex
pipeline :browser do
...
plug :put_secure_browser_headers
plug MyAppWeb.GenerateCSRF
end
Then, in my LiveView component product_list_live.ex, I can extract it from the session argument like:
def mount(%{"csrf_token" => csrf_token} = _session, socket) do
assigns = [
conn: socket,
csrf_token: csrf_token
]
{:ok, assign(socket, assigns)}
end
And lastly, in my template, I can now use the CSRF token:
<.link href={~p"/products/#{product}"} csrf_token={@csrf_token} method="delete" data-confirm="Are you sure?">
Delete
</.link>
SUCCESS
Now you should be able to delete a product: