How to test http api that uses csurf csrf protection with mocha/chai?

579 Views Asked by At

Not a duplicate of this one

I want to know what are the minimum headers/cookies needed to set in order to test an api that uses csurf csrf protection.

In my server I have:

const app = express()

const csrfMiddleware = csurf({
    cookie: true
})

app.use(cookieParser())
app.use(csrfMiddleware)
app.get('/singup', ...)
app.post('/singup', ...)

In the test file:

const chai = require('chai')
const chaiHttp = require('chai-http')
chai.use(chaiHttp);
const request = chai.request;
const expect = chai.expect;

const cheerio = require('cheerio')
const app = "http://localhost:3000" 

function extractCsrfToken(res) {
    var $ = cheerio.load(res.text);
    return $('[name=_csrf]').val();
   }

describe('Post Endpoints', () => {
  it('should create a new user', () => {
        request(app).get('/singup').end((err, resGet)=>{
          expect(err).to.be.null;
          expect(resGet).to.have.status(200)
          const _csrf = extractCsrfToken(resGet);

          console.log(`sending request with csrf token ${_csrf}`)
          request(app)
          .post('/singup')
          .set('Cookie', `_csrf=${_csrf}`)
          .set('Connection', 'keep-alive')
          .set('Accept', 'text/html,application/xhtml+xml,application/xml;q=0.9,image/webp,*/*;q=0.8')
          .send({
              username: "teste-automatizado",
              email: '[email protected]',
              password: '12345678',
              passwordConfirm: '12345678'
      
          }).end((err2, res)=>{
            expect(err2).to.be.null;
            expect(res).to.have.status(200)
          })
          
        })   
  })
})

What I expect is the user to be created and a 200 return from the POST request which is not happening because it's failing with 403 from the middleware:

$ npm test

> [email protected] test
> mocha



  Post Endpoints
    ✔ should create a new user


  1 passing (21ms)

sending request with csrf token PDwitHYN-ttgB5zAIGCQqq9cCxgwkbXyyrMM

/home/fabio/src/project/node_modules/mocha/lib/runner.js:962
    throw err;
    ^
AssertionError: expected { Object (_events, _eventsCount, ...) } to have status code 200 but got 403
    at /home/fabio/src/project/test.js:37:33
    at Test.Request.callback (/home/fabio/src/project/node_modules/superagent/lib/node/index.js:716:12)
    at IncomingMessage.<anonymous> (/home/fabio/src/project/node_modules/superagent/lib/node/index.js:916:18)
    at IncomingMessage.emit (node:events:402:35)
    at endReadableNT (node:internal/streams/readable:1343:12)
    at processTicksAndRejections (node:internal/process/task_queues:83:21) {
  showDiff: true,
  actual: 403,
  expected: 200
}
1

There are 1 best solutions below

0
The Fool On BEST ANSWER

According to the documentation, the token is expected to be in a header named CSRF-Token.

request(app)
  .post('/singup')
  .set('CSRF-Token', _csrf)
...

Additionally, you probably need to set a Cookie header on your request. But you should use the value of the Set-Cookie header from the response of your first request.

For that, chai has options to retain the cookies. The key is to use an agent to make all requests.

const agent = chai.request.agent(app)

agent.get('/singup').end((err, res) => {
  // do some tests and get the token
  // ...
  // the agent will use the cookie from the previous request
  // so you only need to set the CSRF-Token header 
  agent.post('/singup')
    .set('CSRF-Token', _csrf)
    .send({ username: 'me', password: '123' })
    .end((err, res) => {
       // handle the post response 
    })
})

agent.close()