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
			| 
								 
											3 years ago
										 
									 | 
							
								'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
							 |