JWT Authentication not working using only refresh token in request header

The issue

Hi, I’ve looked into the doc and other issues, but I couldn’t find a solution to this problem.
When I go to a route on which the auth middleware is applied while using the refresh token in the header {Authorization: Bearer 567d9a538e2…}, I get the error JWT MALFORMED

When I use the normal token which is not saved in the database, the authentication works.

Is this normal, or am I doing something wrong?

Package version

Adonis 4.1.0

Node.js and npm version

NPM 6.9.0
NODE 10.15.3

Sample Code

config/auth.js

 authenticator: 'jwt',
  jwt: {
    serializer: 'lucid',
    model: 'App/Models/User',
    scheme: 'jwt',
    uid: 'email',
    password: 'password',
    options: {
      secret: Env.get('APP_KEY'),
      expiresIn:'15d' //Expires in 30 days in default mode
    }
  },

Controllers/Http/UserController.js

class UserController {

    async login({ auth, request , response }) {
        const {email, password } = request.all()
        const token = await auth.withRefreshToken().attempt(email,password)
        let user = await User.findBy('email',email)
        Object.assign(user,token)
        return response.json({'user':user})
    }

    async logout({auth, request, response}){
        const user = await auth.getUser()
        const token = auth.getAuthHeader()
        const refreshToken = request.header('refresh-token');
        await user
              .tokens()
              .where('type', 'jwt_refresh_token')
               .where('token', Encryption.decrypt(refreshToken))
               .delete()
        return response.json({ 'user':user, message: 'Logout successfully' })
    }

    async create({ request, auth, response }) {
        const query = request.get()

        let user = new User()
        user.last_name = query.last_name
        user.first_name = query.first_name
        user.email = query.email
        user.password = query.password
        await user.save()
        const token = await auth.withRefreshToken().attempt(query.email, query.password)
        Object.assign(user,token)
        return response.json({"user":user})
    }
}

For those who might be looking for the same thing as I did, I’d like to let you know you should probably use the tokens both of them.

I created a middleware to do the following things:
1- Check if the refreshToken exists and not revoked
1.1- In case the refreshtoken no longer exists or revoked, return 4O1 in order to authenticate once again
1.2- Otherwise continue to step 2
2- Check whether or not the token has expired
2.1- If so, generate a new one
2.2- If not, continue to the next middlewares or controller’s action

The issue I still have to deal with is when someone spoofs the refreshtoken. I still have to figure it out.

Nevertheless, here is the middleware I coded. Don’t forget to add it ahead of the auth middleware in the start/kernel.js file.

'use strict'
/** @typedef {import('@adonisjs/framework/src/Request')} Request */
/** @typedef {import('@adonisjs/framework/src/Response')} Response */
/** @typedef {import('@adonisjs/framework/src/View')} View */

const Database = use('Database')
const Encryption = use('Encryption')

class Token {
  /**
   * @param {object} ctx
   * @param {Request} ctx.request
   * @param {Function} next
   */
  async handle({ request, auth  }, next) {
    const refreshToken = request.header('refresh-token')
    if (refreshToken) {
      const token = await Database.from('tokens')
        .where('type', 'jwt_refresh_token')
        .where('is_revoked',0)
        .where('token', Encryption.decrypt(refreshToken))
        .first()
      
        if(!token){
          request.request.headers['authorization'] = '404'
        }else{
          console.log('This is the auth user')
          try{
            const authUser = await auth.getUser()
            console.log(authUser)
          }catch{
            const newCredentials = await auth.generateForRefreshToken(refreshToken)
            request.request.headers['authorization'] = 'Bearer '+newCredentials.token
          }
          
        }

    }
    await next()
  }
}

module.exports = Token

Hey @alaouizoubair! :wave:

I didn’t really get what you are trying to do.
The refresh token need to be used to refresh the access token when it’s expired.

You should only use the access token for authentication.