This incoming conn:
:plug_session => %{
"_csrf_token" => "PHh4OxAsCycMPG0UDQwHMnQzIy85LwQwH24ZA_sogt4yfFum5GLBTNqJ",
"session_cookie" => "zpu3CvCqVrzWiZJIGQ7/USVwL3jlbR3RzFSvhF3nTefFgSJoc142lxltxxRwBvpk4BI3pL7UcbkjZnaqayGYJw==",
"user_id" => 5
},
...
req_headers: [
...
{"x-csrf-token", "PHh4OxAsCycMPG0UDQwHMnQzIy85LwQwH24ZA_sogt4yfFum5GLBTNqJ"}
]
Gives a Plug.CSRFProtection.InvalidCSRFTokenError
if the route is piped through the :protect_from_forgery
plug.
Here’s the controller code that generates the CSRF:
csrf_token = get_csrf_token()
conn
|> put_session("_csrf_token", csrf_token)
|> put_resp_header("csrf_token", csrf_token)
|> render(...)
The front-end (which is a SPA) then takes the CSRF from the response header and stores it, and includes it in subsequent POST requests. That’s my understanding of how CSRFs are meant to be used.
A few people on Slack tried to help troubleshoot but we didn’t get anywhere.
What conditions need to be met in order for a CSRF token to be considered “valid”? I dug into the code of the various Plug modules (all the way down to Plug.Conn.Crypto), and while I was able to somewhat reason about the flow, I wasn’t able to identify the problem because the functions return simply :error
, without any detailed reasons/explanations.
I should note that I’m using a custom, Postgres-backed session store. Not sure if that’s relevant though.