You can not select more than 25 topics
			Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
		
		
		
		
		
			
		
			
				
					
					
						
							180 lines
						
					
					
						
							4.5 KiB
						
					
					
				
			
		
		
	
	
							180 lines
						
					
					
						
							4.5 KiB
						
					
					
				'use strict'
 | 
						|
/*
 | 
						|
Register a user:
 | 
						|
 | 
						|
    curl -i 'http://127.0.0.1:3000/register' -H 'content-type: application/json' --data '{"user": "myuser","password":"mypass"}'
 | 
						|
Will return:
 | 
						|
    {"token":"YOUR_JWT_TOKEN"}
 | 
						|
 | 
						|
The application then:
 | 
						|
1. generates a JWT token (from 'supersecret') and adds to the response headers
 | 
						|
1. inserts user in the leveldb
 | 
						|
 | 
						|
Check it's all working by using one or the other auth mechanisms:
 | 
						|
1. Auth using username and password (you can also use JWT on this endpoint)
 | 
						|
    curl 'http://127.0.0.1:3000/auth-multiple' -H 'content-type: application/json' --data '{"user": "myuser","password":"mypass"}'
 | 
						|
    {"hello":"world"}
 | 
						|
 | 
						|
1. Auth using JWT token
 | 
						|
    curl -i 'http://127.0.0.1:3000/auth' -H 'content-type: application/json' -H "auth: YOUR_JWT_TOKEN"
 | 
						|
 */
 | 
						|
 | 
						|
const Fastify = require('fastify')
 | 
						|
 | 
						|
function build (opts) {
 | 
						|
  const fastify = Fastify(opts)
 | 
						|
 | 
						|
  fastify.register(require('fastify-jwt'), { secret: 'supersecret' })
 | 
						|
  fastify.register(require('fastify-leveldb'), { name: 'authdb' })
 | 
						|
  fastify.register(require('./auth')) // just 'fastify-auth' IRL
 | 
						|
  fastify.after(routes)
 | 
						|
 | 
						|
  fastify.decorate('verifyJWTandLevelDB', verifyJWTandLevelDB)
 | 
						|
  fastify.decorate('verifyUserAndPassword', verifyUserAndPassword)
 | 
						|
 | 
						|
  function verifyJWTandLevelDB (request, reply, done) {
 | 
						|
    const jwt = this.jwt
 | 
						|
    const level = this.level.authdb
 | 
						|
 | 
						|
    if (request.body && request.body.failureWithReply) {
 | 
						|
      reply.code(401).send({ error: 'Unauthorized' })
 | 
						|
      return done(new Error())
 | 
						|
    }
 | 
						|
 | 
						|
    if (!request.raw.headers.auth) {
 | 
						|
      return done(new Error('Missing token header'))
 | 
						|
    }
 | 
						|
 | 
						|
    jwt.verify(request.raw.headers.auth, onVerify)
 | 
						|
 | 
						|
    function onVerify (err, decoded) {
 | 
						|
      if (err || !decoded.user || !decoded.password) {
 | 
						|
        return done(new Error('Token not valid'))
 | 
						|
      }
 | 
						|
 | 
						|
      level.get(decoded.user, onUser)
 | 
						|
 | 
						|
      function onUser (err, password) {
 | 
						|
        if (err) {
 | 
						|
          if (err.notFound) {
 | 
						|
            return done(new Error('Token not valid'))
 | 
						|
          }
 | 
						|
          return done(err)
 | 
						|
        }
 | 
						|
 | 
						|
        if (!password || password !== decoded.password) {
 | 
						|
          return done(new Error('Token not valid'))
 | 
						|
        }
 | 
						|
 | 
						|
        done()
 | 
						|
      }
 | 
						|
    }
 | 
						|
  }
 | 
						|
 | 
						|
  function verifyUserAndPassword (request, reply, done) {
 | 
						|
    const level = this.level.authdb
 | 
						|
 | 
						|
    if (!request.body || !request.body.user) {
 | 
						|
      return done(new Error('Missing user in request body'))
 | 
						|
    }
 | 
						|
 | 
						|
    level.get(request.body.user, onUser)
 | 
						|
 | 
						|
    function onUser (err, password) {
 | 
						|
      if (err) {
 | 
						|
        if (err.notFound) {
 | 
						|
          return done(new Error('Password not valid'))
 | 
						|
        }
 | 
						|
        return done(err)
 | 
						|
      }
 | 
						|
 | 
						|
      if (!password || password !== request.body.password) {
 | 
						|
        return done(new Error('Password not valid'))
 | 
						|
      }
 | 
						|
 | 
						|
      done()
 | 
						|
    }
 | 
						|
  }
 | 
						|
 | 
						|
  function routes () {
 | 
						|
    fastify.route({
 | 
						|
      method: 'POST',
 | 
						|
      url: '/register',
 | 
						|
      schema: {
 | 
						|
        body: {
 | 
						|
          type: 'object',
 | 
						|
          properties: {
 | 
						|
            user: { type: 'string' },
 | 
						|
            password: { type: 'string' }
 | 
						|
          },
 | 
						|
          required: ['user', 'password']
 | 
						|
        }
 | 
						|
      },
 | 
						|
      handler: (req, reply) => {
 | 
						|
        req.log.info('Creating new user')
 | 
						|
        fastify.level.authdb.put(req.body.user, req.body.password, onPut)
 | 
						|
 | 
						|
        function onPut (err) {
 | 
						|
          if (err) return reply.send(err)
 | 
						|
          fastify.jwt.sign(req.body, onToken)
 | 
						|
        }
 | 
						|
 | 
						|
        function onToken (err, token) {
 | 
						|
          if (err) return reply.send(err)
 | 
						|
          req.log.info('User created')
 | 
						|
          reply.send({ token })
 | 
						|
        }
 | 
						|
      }
 | 
						|
    })
 | 
						|
 | 
						|
    fastify.route({
 | 
						|
      method: 'GET',
 | 
						|
      url: '/no-auth',
 | 
						|
      handler: (req, reply) => {
 | 
						|
        req.log.info('Auth free route')
 | 
						|
        reply.send({ hello: 'world' })
 | 
						|
      }
 | 
						|
    })
 | 
						|
 | 
						|
    fastify.route({
 | 
						|
      method: 'GET',
 | 
						|
      url: '/auth',
 | 
						|
      preHandler: fastify.auth([fastify.verifyJWTandLevelDB]),
 | 
						|
      handler: (req, reply) => {
 | 
						|
        req.log.info('Auth route')
 | 
						|
        reply.send({ hello: 'world' })
 | 
						|
      }
 | 
						|
    })
 | 
						|
 | 
						|
    fastify.route({
 | 
						|
      method: 'POST',
 | 
						|
      url: '/auth-multiple',
 | 
						|
      preHandler: fastify.auth([
 | 
						|
        // Only one of these has to pass
 | 
						|
        fastify.verifyJWTandLevelDB,
 | 
						|
        fastify.verifyUserAndPassword
 | 
						|
      ]),
 | 
						|
      handler: (req, reply) => {
 | 
						|
        req.log.info('Auth route')
 | 
						|
        reply.send({ hello: 'world' })
 | 
						|
      }
 | 
						|
    })
 | 
						|
  }
 | 
						|
 | 
						|
  return fastify
 | 
						|
}
 | 
						|
 | 
						|
if (require.main === module) {
 | 
						|
  const fastify = build({
 | 
						|
    logger: {
 | 
						|
      level: 'info'
 | 
						|
    }
 | 
						|
  })
 | 
						|
  fastify.listen(3000, err => {
 | 
						|
    if (err) throw err
 | 
						|
    console.log(`Server listening at http://localhost:${fastify.server.address().port}`)
 | 
						|
  })
 | 
						|
}
 | 
						|
 | 
						|
module.exports = build
 |