Phoenix: Unexpected InvalidCSRFTokenError when submitted via AJAX with CSRF token in header

Created on 5 Sep 2016  Â·  4Comments  Â·  Source: phoenixframework/phoenix

Environment

  • Elixir version (elixir -v): 1.3.2
  • Phoenix version (mix deps): 1.2.1
  • NodeJS version (node -v): 6.4.0
  • NPM version (npm -v): 3.10.5
  • Operating system: OSX 10.11.6

    Expected behavior

Set CSRF Token in app.html.slim

window.csrfToken = '<%= get_csrf_token() %>'

Set a remote link in projects/index.html.slim

a.card-footer-item href="#{project_path(@conn, :delete, project)}" data-method="delete" data-remote="true"
  | Delete

Register events in app.js

const remotes = document.querySelector('[data-remote=true]')
remotes && remotes.addEventListener('click', (ev) => {
  ev.preventDefault()
  submittedRemotely(ev)
})

function submittedRemotely ({target}) {
  const request = new window.Request(target.getAttribute('href'), {
    method: target.getAttribute('data-method'),
    headers: new window.Headers({'x-csrf-token': window.csrfToken})
  })
  window.fetch(request)
}

Click delete link, should not redirect and should delete resource with a status of 200

Actual behavior

Plug.CSRFProtection.InvalidCSRFTokenError at DELETE /projects/8

invalid CSRF (Cross Site Request Forgery) token, make sure all requests include a valid '_csrf_token' param or 'x-csrf-token' header

Params

  • id "8"

    Request info

  • URI: http://localhost:4000/projects/8

  • Query string:
  • Peer:127.0.0.1:50492

    Headers

  • accept */*

  • accept-encoding gzip, deflate, sdch
  • accept-language en-US,en;q=0.8,ms;q=0.6,zh-CN;q=0.4,zh;q=0.2,ar;q=0.2
  • connection keep-alive
  • host localhost:4000
  • origin http://localhost:4000
  • referer http://localhost:4000/projects
  • user-agent Mozilla/5.0 (Linux; Android 6.0; Nexus 5 Build/MRA58N) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/46.0.2490.76 Mobile Safari/537.36
  • x-csrf-token XwE1PAYuMgQVQyoUSjYhQRV6MhwmAAAA4jmrGdjPA7cp/AM7Y7ARew==

Most helpful comment

I don't know if you're still having this problem, but I looked into it.

Apparently, the problem is that Plug.CSRFProtection works with the session data stored in cookies, but fetch requests don't include these by default.

To fix it, you need to give the credentials option of your request a value of 'include' or 'same-origin' (https://developer.mozilla.org/en-US/docs/Web/API/Request/credentials), like this:

function submittedRemotely ({target}) {
  const request = new window.Request(target.getAttribute('href'), {
    method: target.getAttribute('data-method'),
    headers: new window.Headers({'x-csrf-token': window.csrfToken}),
    credentials: 'same-origin'
  })
  window.fetch(request)
}

All 4 comments

Can you please provide a simple app that reproduces the error? It would
make it much easier to figure it out.

On Monday, September 5, 2016, Imran Ismail [email protected] wrote:

Environment

  • Elixir version (elixir -v): 1.3.2
  • Phoenix version (mix deps): 1.2.1
  • NodeJS version (node -v): 6.4.0
  • NPM version (npm -v): 3.10.5
  • Operating system: OSX 10.11.6

Expected behavior

Set CSRF Token in app.html.slim

window.csrfToken = '<%= get_csrf_token() %>'

Set a remote link in projects/index.html.slim

a.card-footer-item href="#{project_path(@conn, :delete, project)}" data-method="delete" data-remote="true"
| Delete

Register events in app.js

const remotes = document.querySelector('[data-remote=true]')
remotes && remotes.addEventListener('click', (ev) => {
ev.preventDefault()
submittedRemotely(ev)
})
function submittedRemotely ({target}) {
const request = new window.Request(target.getAttribute('href'), {
method: target.getAttribute('method'),
headers: new window.Headers({'x-csrf-token': window.csrfToken})
})
window.fetch(request)
}

Click delete link, should not redirect and should delete resource with a
status of 200
Actual behavior Plug.CSRFProtection.InvalidCSRFTokenError at DELETE
/projects/8

invalid CSRF (Cross Site Request Forgery) token, make sure all requests
include a valid '_csrf_token' param or 'x-csrf-token' header
Params

  • id "8"

Request info

Headers

  • accept _/_
  • accept-encoding gzip, deflate, sdch
  • accept-language en-US,en;q=0.8,ms;q=0.6,zh-CN;q=0.4,zh;q=0.2,ar;q=0.2
  • connection keep-alive
  • host localhost:4000
  • origin http://localhost:4000
  • referer http://localhost:4000/projects
  • user-agent Mozilla/5.0 (Linux; Android 6.0; Nexus 5 Build/MRA58N)
    AppleWebKit/537.36 (KHTML, like Gecko) Chrome/46.0.2490.76 Mobile
    Safari/537.36
  • x-csrf-token XwE1PAYuMgQVQyoUSjYhQRV6MhwmAAAA4jmrGdjPA7cp/AM7Y7ARew==

—
You are receiving this because you are subscribed to this thread.
Reply to this email directly, view it on GitHub
https://github.com/phoenixframework/phoenix/issues/1902, or mute the
thread
https://github.com/notifications/unsubscribe-auth/AAAlbiI2Ctq_XCkPHba2S17bgyt5HyQFks5qm0wMgaJpZM4J0pdG
.

_José Valim_
www.plataformatec.com.br
Skype: jv.ptec
Founder and Director of R&D

Sure, just stripped some stuff. Here's the app
https://github.com/imranismail/phoenix-1902

I don't know if you're still having this problem, but I looked into it.

Apparently, the problem is that Plug.CSRFProtection works with the session data stored in cookies, but fetch requests don't include these by default.

To fix it, you need to give the credentials option of your request a value of 'include' or 'same-origin' (https://developer.mozilla.org/en-US/docs/Web/API/Request/credentials), like this:

function submittedRemotely ({target}) {
  const request = new window.Request(target.getAttribute('href'), {
    method: target.getAttribute('data-method'),
    headers: new window.Headers({'x-csrf-token': window.csrfToken}),
    credentials: 'same-origin'
  })
  window.fetch(request)
}

Thank you @sideburnsandtie! :heart: I am closing this unless @imranismail has more information.

Was this page helpful?
0 / 5 - 0 ratings