Next-auth: Testing log-in or sign-up with mocha / chai

Created on 4 May 2018  路  6Comments  路  Source: nextauthjs/next-auth

Hi! I am making some tests using mocha and chai for the app that I am building using next-auth.
I have to make some tests for the API, as anon and as a logged user.
The method we are using is by sending the log-in token.

I would love to know how to get the log-in token so i can test my API as different users.
Is there any examples of how to test it properly?

bug enhancement question

Most helpful comment

Hi Ian! Thanks for your reply
So I was able to make tests on mocha, chai using the chai-http pluggin and using an agent to store the cookie and getting a crsfToken.

According to the core code, you have an AJAX response available if functions.signIn is available.

/*
   * Enable /auth/signin routes if signIn() function is passed
   */
  if (functions.signIn) {
    expressApp.post(`${pathPrefix}/signin`, (req, res) => {
      // Passes all supplied credentials to the signIn function
      functions.signIn({
        form: req.body,
        req: req
      })
      .then(user => {
        if (user) {
          // If signIn() returns a user, sign in as them
          req.logIn(user, (err) => {
            if (err) return res.redirect(`${pathPrefix}/error?action=signin&type=credentials`)
            if (req.xhr) {
              // If AJAX request (from client with JS), return JSON response
              return res.json({success: true})
            } else {
              // If normal form POST (from client without JS) return redirect
              return res.redirect(`${pathPrefix}/callback?action=signin&service=credentials`)
            }
          })
        } else {
          // If no user object is returned, bounce back to the sign in page
          return res.redirect(`${pathPrefix}`)
        }
      })
      .catch(err => {
        return res.redirect(`${pathPrefix}/error?action=signin&type=credentials`)
      })
    })
  }

(there is also AJAX response for functions.sendSignInEmail but I think you still have make a GET to the url with the token to login).

In our app, we don't have signIn available in prod, but I decided to make it available for testing purposes,

signIn: async ({ form, req }) => {
    return new Promise((resolve, reject) => {
      if (process.env.NODE_ENV === 'test') {
        // log.debug('TEST env - SignIn available')
        return User.get({ email: form.email })
          .then((user) => {
            // console.log('User: ' + user)
            if (!user) return resolve(null)
            return resolve(user)
          })
      } else {
        log.error('No TEST env. SignIn method not allowed')
        return reject(new Error('Not allowed'))
      }
    })
  }

So In my mocha - chai - chai-http test I did this:

let newUser1 = null
let csrfToken = null
let newAdmin = null
let agent = null
describe('/api/v1.0/users', () => {
  before(async () => {
    await require('../../../main')
    await User.remove({})
    newUser1 = await (new User(sampleUser1)).save()
    newAdmin = await (new User(sampleAdmin)).save()
  })

  describe('As Logged user', () => {
    before(async () => {
      // Log In as aser
      agent = await chai.request.agent('http://localhost:3000')
    })
    it('should get csrfToken', async () => {
      await agent.get('/auth/csrf')
        .then((res) => {
          expect(res).to.have.status(OK)
          expect(res.body).to.be.a('object')
          expect(res.body).to.have.property('csrfToken')
          csrfToken = res.body.csrfToken
        })
        .catch((err) => {
          throw err
        })
    })
    it('should log in', async () => {
      await agent.post('/auth/signin')
        .set('X-CSRF-TOKEN', csrfToken)
        .set('X-Requested-With', 'XMLHttpRequest')
        .set('Content-Type', 'application/x-www-form-urlencoded')
        .send({ email: newUser1.email })
        .then((res) => {
          expect(res).to.have.status(OK)
          expect(res).to.have.header('content-type', 'application/json; charset=utf-8')
          expect(res.body).to.be.a('object')
          expect(res.body).to.have.property('success')
          expect(res.body.success).to.be.equal(true)
        })
        .catch((err) => {
          throw err
        })
    })
    it('the session should have a user object', async () => {
      await agent.get('/auth/session')
        .set('X-CSRF-TOKEN', csrfToken)
        .set('X-Requested-With', 'XMLHttpRequest')
        .set('Content-Type', 'application/x-www-form-urlencoded')
        .then((res) => {
          expect(res).to.have.status(OK)
          expect(res).to.have.header('content-type', 'application/json; charset=utf-8')
          expect(res.body).to.be.a('object')
          expect(res.body.user).to.be.a('object')
          expect(res.body.user).to.have.property('name')
        })
        .catch((err) => {
          throw err
        })
    })
})

So yeah.. all i do then is making new tests using the same agent and the correct headers to send the csrfToken (cause if not, i get a 403 FORBIDDEN because of csrf token misssing or mismatch). Then, if i want to switch user I just drop the agent and make a new one.

I guess it would be nice to implement JSON responses, like the one you send when req.xhr is available (an AJAX request), for testing purposes maybe. {'success': true} is OK, but if it fails I would like an JSON response too, maybe some other data attached to the success == true event would be awesome too, like the user who just signed in. And also, AJAX responses available for /logout too, for example, would be awesome

All 6 comments

Hi! Hmm not right now, sorry - but there should be and I appreciate the request.

Will look into it and rustle up some tests.

Hi Ian! Thanks for your reply
So I was able to make tests on mocha, chai using the chai-http pluggin and using an agent to store the cookie and getting a crsfToken.

According to the core code, you have an AJAX response available if functions.signIn is available.

/*
   * Enable /auth/signin routes if signIn() function is passed
   */
  if (functions.signIn) {
    expressApp.post(`${pathPrefix}/signin`, (req, res) => {
      // Passes all supplied credentials to the signIn function
      functions.signIn({
        form: req.body,
        req: req
      })
      .then(user => {
        if (user) {
          // If signIn() returns a user, sign in as them
          req.logIn(user, (err) => {
            if (err) return res.redirect(`${pathPrefix}/error?action=signin&type=credentials`)
            if (req.xhr) {
              // If AJAX request (from client with JS), return JSON response
              return res.json({success: true})
            } else {
              // If normal form POST (from client without JS) return redirect
              return res.redirect(`${pathPrefix}/callback?action=signin&service=credentials`)
            }
          })
        } else {
          // If no user object is returned, bounce back to the sign in page
          return res.redirect(`${pathPrefix}`)
        }
      })
      .catch(err => {
        return res.redirect(`${pathPrefix}/error?action=signin&type=credentials`)
      })
    })
  }

(there is also AJAX response for functions.sendSignInEmail but I think you still have make a GET to the url with the token to login).

In our app, we don't have signIn available in prod, but I decided to make it available for testing purposes,

signIn: async ({ form, req }) => {
    return new Promise((resolve, reject) => {
      if (process.env.NODE_ENV === 'test') {
        // log.debug('TEST env - SignIn available')
        return User.get({ email: form.email })
          .then((user) => {
            // console.log('User: ' + user)
            if (!user) return resolve(null)
            return resolve(user)
          })
      } else {
        log.error('No TEST env. SignIn method not allowed')
        return reject(new Error('Not allowed'))
      }
    })
  }

So In my mocha - chai - chai-http test I did this:

let newUser1 = null
let csrfToken = null
let newAdmin = null
let agent = null
describe('/api/v1.0/users', () => {
  before(async () => {
    await require('../../../main')
    await User.remove({})
    newUser1 = await (new User(sampleUser1)).save()
    newAdmin = await (new User(sampleAdmin)).save()
  })

  describe('As Logged user', () => {
    before(async () => {
      // Log In as aser
      agent = await chai.request.agent('http://localhost:3000')
    })
    it('should get csrfToken', async () => {
      await agent.get('/auth/csrf')
        .then((res) => {
          expect(res).to.have.status(OK)
          expect(res.body).to.be.a('object')
          expect(res.body).to.have.property('csrfToken')
          csrfToken = res.body.csrfToken
        })
        .catch((err) => {
          throw err
        })
    })
    it('should log in', async () => {
      await agent.post('/auth/signin')
        .set('X-CSRF-TOKEN', csrfToken)
        .set('X-Requested-With', 'XMLHttpRequest')
        .set('Content-Type', 'application/x-www-form-urlencoded')
        .send({ email: newUser1.email })
        .then((res) => {
          expect(res).to.have.status(OK)
          expect(res).to.have.header('content-type', 'application/json; charset=utf-8')
          expect(res.body).to.be.a('object')
          expect(res.body).to.have.property('success')
          expect(res.body.success).to.be.equal(true)
        })
        .catch((err) => {
          throw err
        })
    })
    it('the session should have a user object', async () => {
      await agent.get('/auth/session')
        .set('X-CSRF-TOKEN', csrfToken)
        .set('X-Requested-With', 'XMLHttpRequest')
        .set('Content-Type', 'application/x-www-form-urlencoded')
        .then((res) => {
          expect(res).to.have.status(OK)
          expect(res).to.have.header('content-type', 'application/json; charset=utf-8')
          expect(res.body).to.be.a('object')
          expect(res.body.user).to.be.a('object')
          expect(res.body.user).to.have.property('name')
        })
        .catch((err) => {
          throw err
        })
    })
})

So yeah.. all i do then is making new tests using the same agent and the correct headers to send the csrfToken (cause if not, i get a 403 FORBIDDEN because of csrf token misssing or mismatch). Then, if i want to switch user I just drop the agent and make a new one.

I guess it would be nice to implement JSON responses, like the one you send when req.xhr is available (an AJAX request), for testing purposes maybe. {'success': true} is OK, but if it fails I would like an JSON response too, maybe some other data attached to the success == true event would be awesome too, like the user who just signed in. And also, AJAX responses available for /logout too, for example, would be awesome

Sorry I haven't had time to look at it earlier, thanks for this - it's very useful!

(I'll try and provide a more useful update soon. :-)

Hey! @mayankparihar1988
Yeah, set( ) should set a header in your POST request.
I dont know why its not working.. try to use this ones:

.set('X-CSRF-TOKEN', retrivedToken)
.set('X-Requested-With', 'XMLHttpRequest')

Hi @guillecro thanks, Its correct way to send X-CSRF-TOKEN using

.set('X-CSRF-TOKEN', retrivedToken)
.set('X-Requested-With', 'XMLHttpRequest')

In Header some time we also need to send cookies along with X-XSRF_TOKEN. (Depend on the backend in my case SAP HANA required cookies along with X-CSRF-TOKEN)

.set('X-CSRF-TOKEN', retrivedToken)
.set('X-Requested-With', 'XMLHttpRequest') // No Requried but ok to send.
.set('Cookie', cookiesToSend)

Then it worked for me!

Thanks/Cheers,
Mayank

Thanks for these. Ugh, I can't believe I've ignored automating the testing this long. Embarrassing.

On it.

Was this page helpful?
0 / 5 - 0 ratings

Related issues

alephart picture alephart  路  3Comments

ghoshnirmalya picture ghoshnirmalya  路  3Comments

iaincollins picture iaincollins  路  3Comments

iaincollins picture iaincollins  路  3Comments

benoror picture benoror  路  3Comments