Launch Week Day 5: Introducing Reachability for PHP.Learn More
Socket
Book a DemoSign in
Socket

@fastify/auth

Package Overview
Dependencies
Maintainers
19
Versions
19
Alerts
File Explorer

Advanced tools

Socket logo

Install Socket

Detect and block malicious and high-risk dependencies

Install

@fastify/auth - npm Package Compare versions

Comparing version
3.0.2
to
4.0.0
+146
test/example-async.js
'use strict'
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-async' })
fastify.register(require('../auth'))
fastify.register(routes)
fastify.decorate('verifyJWTandLevel', verifyJWTandLevel)
fastify.decorate('verifyUserAndPassword', verifyUserAndPassword)
function verifyJWTandLevel (request, reply) {
const jwt = this.jwt
const level = this.level['authdb-async']
if (request.body && request.body.failureWithReply) {
reply.code(401).send({ error: 'Unauthorized' })
return Promise.reject(new Error())
}
if (!request.raw.headers.auth) {
return Promise.reject(new Error('Missing token header'))
}
return new Promise(function (resolve, reject) {
jwt.verify(request.raw.headers.auth, function (err, decoded) {
if (err) { return reject(err) };
resolve(decoded)
})
}).then(function (decoded) {
return level.get(decoded.user)
.then(function (password) {
if (!password || password !== decoded.password) {
throw new Error('Token not valid')
}
})
}).catch(function (error) {
request.log.error(error)
throw new Error('Token not valid')
})
}
function verifyUserAndPassword (request, reply, done) {
const level = this.level['authdb-async']
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()
}
}
async function routes (fastify) {
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-async'].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.verifyJWTandLevel]),
handler: (req, reply) => {
req.log.info('Auth route')
reply.send({ hello: 'world' })
}
})
fastify.route({
method: 'POST',
url: '/auth-multiple',
preHandler: fastify.auth([
fastify.verifyJWTandLevel,
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({ port: 3000, host: '0.0.0.0' }, err => {
if (err) throw err
})
}
module.exports = build
'use strict'
const Fastify = require('fastify')
function build (opts) {
const fastify = Fastify(opts)
fastify
.register(require('../auth'))
.after(routes)
fastify.decorate('verifyNumber', verifyNumber)
fastify.decorate('verifyOdd', verifyOdd)
fastify.decorate('verifyBig', verifyBig)
function verifyNumber (request, reply, done) {
const n = request.body.n
if (typeof (n) !== 'number') {
request.number = false
return done(new Error('type of `n` is not `number`'))
}
request.number = true
return done()
}
function verifyOdd (request, reply, done) {
const n = request.body.n
if (typeof (n) !== 'number' || n % 2 === 0) {
request.odd = false
return done(new Error('`n` is not odd'))
}
request.odd = true
return done()
}
function verifyBig (request, reply, done) {
const n = request.body.n
if (typeof (n) !== 'number' || n < 100) {
request.big = false
return done(new Error('`n` is not big'))
}
request.big = true
return done()
}
function routes () {
fastify.route({
method: 'GET',
url: '/',
handler: (req, reply) => {
reply.send({ hello: 'world' })
}
})
fastify.route({
method: 'POST',
url: '/checkand',
preHandler: fastify.auth([fastify.verifyNumber, fastify.verifyOdd], { relation: 'and' }),
handler: (req, reply) => {
req.log.info('Auth route')
reply.send({ hello: 'world' })
}
})
fastify.route({
method: 'POST',
url: '/checkor',
preHandler: fastify.auth([fastify.verifyOdd, fastify.verifyBig]),
handler: (req, reply) => {
req.log.info('Auth route')
reply.send({ hello: 'world' })
}
})
fastify.route({
method: 'POST',
url: '/singleor',
preHandler: fastify.auth([fastify.verifyOdd]),
handler: (req, reply) => {
req.log.info('Auth route')
reply.send({ hello: 'world' })
}
})
fastify.route({
method: 'POST',
url: '/singleand',
preHandler: fastify.auth([fastify.verifyOdd], { relation: 'and' }),
handler: (req, reply) => {
req.log.info('Auth route')
reply.send({ hello: 'world' })
}
})
fastify.route({
method: 'POST',
url: '/run-all-or',
preHandler: fastify.auth([fastify.verifyOdd, fastify.verifyBig, fastify.verifyNumber], { run: 'all' }),
handler: (req, reply) => {
req.log.info('Auth route')
reply.send({
odd: req.odd,
big: req.big,
number: req.number
})
}
})
fastify.route({
method: 'POST',
url: '/run-all-and',
preHandler: fastify.auth([fastify.verifyOdd, fastify.verifyBig, fastify.verifyNumber], { run: 'all', relation: 'and' }),
handler: (req, reply) => {
req.log.info('Auth route')
reply.send({
odd: req.odd,
big: req.big,
number: req.number
})
}
})
}
return fastify
}
if (require.main === module) {
const fastify = build({
logger: {
level: 'info'
}
})
fastify.listen({ port: 3000, host: '0.0.0.0' }, err => {
if (err) throw err
})
}
module.exports = build
'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({ port: 3000 }, err => {
if (err) throw err
})
}
module.exports = build
+1
-0

@@ -17,2 +17,3 @@ name: CI

with:
license-check: true
lint: true
+10
-11
{
"name": "@fastify/auth",
"version": "3.0.2",
"version": "4.0.0",
"description": "Run multiple auth functions in Fastify",

@@ -33,17 +33,16 @@ "repository": {

"devDependencies": {
"@fastify/jwt": "^6.0.0",
"@fastify/jwt": "^6.3.2",
"@fastify/leveldb": "^5.0.1",
"@fastify/type-provider-json-schema-to-ts": "^1.0.0",
"@fastify/type-provider-typebox": "^1.0.0",
"@types/node": "^18.0.0",
"fastify": "^4.0.0-rc.3",
"pre-commit": "^1.2.2",
"@fastify/pre-commit": "^2.0.2",
"@fastify/type-provider-json-schema-to-ts": "^2.1.1",
"@fastify/type-provider-typebox": "^2.3.0",
"@types/node": "^18.7.14",
"fastify": "^4.5.3",
"rimraf": "^3.0.2",
"standard": "^17.0.0",
"tap": "^16.0.0",
"tsd": "^0.21.0",
"typescript": "^4.0.2"
"tap": "^16.3.0",
"tsd": "^0.23.0"
},
"dependencies": {
"fastify-plugin": "^3.0.0",
"fastify-plugin": "^4.0.0",
"reusify": "^1.0.4"

@@ -50,0 +49,0 @@ },

@@ -8,3 +8,3 @@ # @fastify/auth

This module does not provide an authentication strategy, but it provides a very fast utility to handle authentication (and multiple strategies) in your routes, without adding overhead.
Check out a complete example [here](https://github.com/fastify/fastify-auth/blob/master/example.js).
Check out a complete example [here](test/example.js).

@@ -76,3 +76,3 @@ ## Install

```
_For more examples, please check [`example-composited.js`](example-composited.js)_
_For more examples, please check [`example-composited.js`](test/example-composited.js)_

@@ -110,3 +110,3 @@ This plugin support `callback` and `Promise` returned by the functions. Note that an `async` function **does not have** to call the `done` parameter, otherwise the route handler to which the auth methods are linked to [might be called multiple times](https://www.fastify.io/docs/latest/Hooks/#respond-to-a-request-from-a-hook):

Keep in mind that route definition should either be done as [a plugin](https://github.com/fastify/fastify/blob/master/docs/Plugins.md) or within an `.after()` callback.
For a complete example implementation, see [example.js](example.js).
For a complete example implementation, see [example.js](test/example.js).

@@ -113,0 +113,0 @@ `@fastify/auth` will run all your authentication methods and your request will continue if at least one succeeds, otherwise it will return an error to the client.

@@ -6,3 +6,3 @@ 'use strict'

const rimraf = require('rimraf')
const build = require('../example-async')
const build = require('./example-async')

@@ -9,0 +9,0 @@ let fastify = null

@@ -5,3 +5,3 @@ 'use strict'

const test = t.test
const build = require('../example-composited')
const build = require('./example-composited')

@@ -8,0 +8,0 @@ let fastify = null

@@ -6,3 +6,3 @@ 'use strict'

const rimraf = require('rimraf')
const build = require('../example')
const build = require('./example')

@@ -9,0 +9,0 @@ let fastify = null

Sorry, the diff of this file is not supported yet

'use strict'
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-async' })
fastify.register(require('./auth'))
fastify.register(routes)
fastify.decorate('verifyJWTandLevel', verifyJWTandLevel)
fastify.decorate('verifyUserAndPassword', verifyUserAndPassword)
function verifyJWTandLevel (request, reply) {
const jwt = this.jwt
const level = this.level['authdb-async']
if (request.body && request.body.failureWithReply) {
reply.code(401).send({ error: 'Unauthorized' })
return Promise.reject(new Error())
}
if (!request.raw.headers.auth) {
return Promise.reject(new Error('Missing token header'))
}
return new Promise(function (resolve, reject) {
jwt.verify(request.raw.headers.auth, function (err, decoded) {
if (err) { return reject(err) };
resolve(decoded)
})
}).then(function (decoded) {
return level.get(decoded.user)
.then(function (password) {
if (!password || password !== decoded.password) {
throw new Error('Token not valid')
}
})
}).catch(function (error) {
request.log.error(error)
throw new Error('Token not valid')
})
}
function verifyUserAndPassword (request, reply, done) {
const level = this.level['authdb-async']
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()
}
}
async function routes (fastify) {
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-async'].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.verifyJWTandLevel]),
handler: (req, reply) => {
req.log.info('Auth route')
reply.send({ hello: 'world' })
}
})
fastify.route({
method: 'POST',
url: '/auth-multiple',
preHandler: fastify.auth([
fastify.verifyJWTandLevel,
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({ port: 3000, host: '0.0.0.0' }, err => {
if (err) throw err
})
}
module.exports = build
'use strict'
const Fastify = require('fastify')
function build (opts) {
const fastify = Fastify(opts)
fastify
.register(require('./auth'))
.after(routes)
fastify.decorate('verifyNumber', verifyNumber)
fastify.decorate('verifyOdd', verifyOdd)
fastify.decorate('verifyBig', verifyBig)
function verifyNumber (request, reply, done) {
const n = request.body.n
if (typeof (n) !== 'number') {
request.number = false
return done(new Error('type of `n` is not `number`'))
}
request.number = true
return done()
}
function verifyOdd (request, reply, done) {
const n = request.body.n
if (typeof (n) !== 'number' || n % 2 === 0) {
request.odd = false
return done(new Error('`n` is not odd'))
}
request.odd = true
return done()
}
function verifyBig (request, reply, done) {
const n = request.body.n
if (typeof (n) !== 'number' || n < 100) {
request.big = false
return done(new Error('`n` is not big'))
}
request.big = true
return done()
}
function routes () {
fastify.route({
method: 'GET',
url: '/',
handler: (req, reply) => {
reply.send({ hello: 'world' })
}
})
fastify.route({
method: 'POST',
url: '/checkand',
preHandler: fastify.auth([fastify.verifyNumber, fastify.verifyOdd], { relation: 'and' }),
handler: (req, reply) => {
req.log.info('Auth route')
reply.send({ hello: 'world' })
}
})
fastify.route({
method: 'POST',
url: '/checkor',
preHandler: fastify.auth([fastify.verifyOdd, fastify.verifyBig]),
handler: (req, reply) => {
req.log.info('Auth route')
reply.send({ hello: 'world' })
}
})
fastify.route({
method: 'POST',
url: '/singleor',
preHandler: fastify.auth([fastify.verifyOdd]),
handler: (req, reply) => {
req.log.info('Auth route')
reply.send({ hello: 'world' })
}
})
fastify.route({
method: 'POST',
url: '/singleand',
preHandler: fastify.auth([fastify.verifyOdd], { relation: 'and' }),
handler: (req, reply) => {
req.log.info('Auth route')
reply.send({ hello: 'world' })
}
})
fastify.route({
method: 'POST',
url: '/run-all-or',
preHandler: fastify.auth([fastify.verifyOdd, fastify.verifyBig, fastify.verifyNumber], { run: 'all' }),
handler: (req, reply) => {
req.log.info('Auth route')
reply.send({
odd: req.odd,
big: req.big,
number: req.number
})
}
})
fastify.route({
method: 'POST',
url: '/run-all-and',
preHandler: fastify.auth([fastify.verifyOdd, fastify.verifyBig, fastify.verifyNumber], { run: 'all', relation: 'and' }),
handler: (req, reply) => {
req.log.info('Auth route')
reply.send({
odd: req.odd,
big: req.big,
number: req.number
})
}
})
}
return fastify
}
if (require.main === module) {
const fastify = build({
logger: {
level: 'info'
}
})
fastify.listen({ port: 3000, host: '0.0.0.0' }, err => {
if (err) throw err
})
}
module.exports = build
'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({ port: 3000 }, err => {
if (err) throw err
})
}
module.exports = build