Another possibility is to pass the CSRF token as a cookie accessible to JS. Your SPA would have to perform a request so that the cookie is set, grab the value of the cookie, and include it in the request params.
In any case, your SPA has to get the token somehow (in a way that an attacker on another domain could not), and then pass it as a parameter when a making the request. Phoenix will check the validity of the CSRF token against the session.
Not exactly. Your SPA should not set any CSRF cookie. It should get the token (possibly set by the backend as a cookie, or passed in any way that is not available from other domains, possibly even an endpoint that sets CORS so that browsers cannot make requests to it from other domains than yours), and pass it as a request parameter when performing POST or PUT requests, like the one for logging in.
Basically CSRF protection is about making sure that the user is not “tricked” to perform some action from a malicious other website. It is necessary whenever authentication/authorization is based on cookies.
It works by setting a secret token in each session, and passing the same token to the form, so that, upon form submission, the app can check if the token in the session corresponds to the one posted explicitly as a param. An attacker’s website does not know the user’s token, so it cannot build a correct form or API request.
Rewinding a bit, if CSRF and mitigation is clear, in your case (single page app + API) you have a few options:
If your SPA is the only web client that should call the API, you could skip CSRF protection on the API endpoints, implement the login as an API endpoint, and use CORS headers to only allow API requests from your domain or other authorized ones. Traditionally, this approach had some problems with some plugins not enforcing CORS (old versions of Flash), but nowadays it might be a sensible approach, depending on your threat model. A login endpoint is typically not a good target for CSRF, as the user still has to provide credentials that an attacker cannot fake. Other endpoints might be though, for example an endpoint to delete a resource.
Alternatively, if other clients have to be able to use the API from other domains, you could use an API authentication mechanism that does not rely on cookies. Not relying on cookies for auth means that CSRF is not a feasible attack. In this case, the client would acquire an auth token and use it when performing requests. The client is responsible of keeping the auth token secure, so protection agains XSS for a web client like your SPA becomes even more critical. Some kind of token-based authentication is anyway necessary if clients of your API are not necessarily websites.
In any case, make sure you understand the implications, and decide on your specific use case.