Skip to content

Commit 7593a79

Browse files
committed
Improve support for custom error handlers
1 parent 5a13535 commit 7593a79

File tree

4 files changed

+124
-4
lines changed

4 files changed

+124
-4
lines changed

README.md

Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -97,6 +97,26 @@ fastify.after(() => {
9797
})
9898
```
9999

100+
### Custom error handler
101+
102+
On failed authentication, *fastify-basic-auth* will call the Fastify
103+
[generic error
104+
handler](https://www.fastify.io/docs/latest/Server/#seterrorhandler) with an error.
105+
*fastify-basic-auth* sets the `err.statusCode` property to `401`.
106+
107+
In order to properly `401` errors:
108+
109+
```js
110+
fastify.setErrorHandler(function (err, req, reply) {
111+
if (err.statusCode === 401) {
112+
// this was unauthorized! Display the correct page/message.
113+
reply.send({ was: 'unauthorized' })
114+
return
115+
}
116+
reply.send(err)
117+
})
118+
```
119+
100120
## Options
101121

102122
### `validate` <Function> (required)

index.js

Lines changed: 6 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@
22

33
const fp = require('fastify-plugin')
44
const auth = require('basic-auth')
5+
const { Unauthorized } = require('http-errors')
56

67
function basicPlugin (fastify, opts, next) {
78
if (typeof opts.validate !== 'function') {
@@ -19,7 +20,7 @@ function basicPlugin (fastify, opts, next) {
1920
}
2021
var credentials = auth(req)
2122
if (credentials == null) {
22-
done(new Error('Missing or bad formatted authorization header'))
23+
done(new Unauthorized('Missing or bad formatted authorization header'))
2324
} else {
2425
var result = validate(credentials.name, credentials.pass, req, reply, done)
2526
if (result && typeof result.then === 'function') {
@@ -29,7 +30,10 @@ function basicPlugin (fastify, opts, next) {
2930

3031
function done (err) {
3132
if (err !== undefined) {
32-
reply.code(401)
33+
// We set the status code to be 401 if it is not set
34+
if (!err.statusCode) {
35+
err.statusCode = 401
36+
}
3337
next(err)
3438
} else {
3539
next()

package.json

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -31,6 +31,7 @@
3131
},
3232
"dependencies": {
3333
"basic-auth": "^2.0.0",
34-
"fastify-plugin": "^1.0.1"
34+
"fastify-plugin": "^1.0.1",
35+
"http-errors": "^1.7.2"
3536
}
3637
}

test.js

Lines changed: 96 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@ const { test } = require('tap')
44
const Fastify = require('fastify')
55
const basicAuth = require('./index')
66
const fastifyAuth = require('fastify-auth')
7+
const { Unauthorized } = require('http-errors')
78

89
test('Basic', t => {
910
t.plan(2)
@@ -384,7 +385,7 @@ test('Hook with fastify-auth- 401', t => {
384385
})
385386

386387
test('Missing header', t => {
387-
t.plan(2)
388+
t.plan(3)
388389

389390
const fastify = Fastify()
390391
fastify.register(basicAuth, { validate })
@@ -414,6 +415,11 @@ test('Missing header', t => {
414415
}, (err, res) => {
415416
t.error(err)
416417
t.strictEqual(res.statusCode, 401)
418+
t.deepEqual(JSON.parse(res.payload), {
419+
statusCode: 401,
420+
error: 'Unauthorized',
421+
message: 'Missing or bad formatted authorization header'
422+
})
417423
})
418424
})
419425

@@ -456,6 +462,95 @@ test('Fastify context', t => {
456462
})
457463
})
458464

465+
test('setErrorHandler custom error and 401', t => {
466+
t.plan(4)
467+
468+
const fastify = Fastify()
469+
fastify
470+
.register(fastifyAuth)
471+
.register(basicAuth, { validate })
472+
473+
function validate (username, password, req, res, done) {
474+
done(new Error('Winter is coming'))
475+
}
476+
477+
fastify.after(() => {
478+
fastify.addHook('preHandler', fastify.auth([fastify.basicAuth]))
479+
fastify.route({
480+
method: 'GET',
481+
url: '/',
482+
handler: (req, reply) => {
483+
reply.send({ hello: 'world' })
484+
}
485+
})
486+
})
487+
488+
fastify.setErrorHandler(function (err, req, reply) {
489+
t.strictEqual(err.statusCode, 401)
490+
reply.send(err)
491+
})
492+
493+
fastify.inject({
494+
url: '/',
495+
method: 'GET',
496+
headers: {
497+
authorization: basicAuthHeader('user', 'pwdd')
498+
}
499+
}, (err, res) => {
500+
t.error(err)
501+
t.strictEqual(res.statusCode, 401)
502+
t.deepEqual(JSON.parse(res.payload), {
503+
error: 'Unauthorized',
504+
message: 'Winter is coming',
505+
statusCode: 401
506+
})
507+
})
508+
})
509+
510+
test('Missing header and custom error handler', t => {
511+
t.plan(4)
512+
513+
const fastify = Fastify()
514+
fastify.register(basicAuth, { validate })
515+
516+
function validate (username, password, req, res, done) {
517+
if (username === 'user' && password === 'pwd') {
518+
done()
519+
} else {
520+
done(new Error('Unauthorized'))
521+
}
522+
}
523+
524+
fastify.after(() => {
525+
fastify.route({
526+
method: 'GET',
527+
url: '/',
528+
beforeHandler: fastify.basicAuth,
529+
handler: (req, reply) => {
530+
reply.send({ hello: 'world' })
531+
}
532+
})
533+
})
534+
535+
fastify.setErrorHandler(function (err, req, reply) {
536+
t.ok(err instanceof Unauthorized)
537+
reply.send(err)
538+
})
539+
540+
fastify.inject({
541+
url: '/',
542+
method: 'GET'
543+
}, (err, res) => {
544+
t.error(err)
545+
t.strictEqual(res.statusCode, 401)
546+
t.deepEqual(JSON.parse(res.payload), {
547+
statusCode: 401,
548+
error: 'Unauthorized',
549+
message: 'Missing or bad formatted authorization header'
550+
})
551+
})
552+
})
553+
459554
function basicAuthHeader (username, password) {
460555
return 'Basic ' + Buffer.from(`${username}:${password}`).toString('base64')
461556
}

0 commit comments

Comments
 (0)