Skip to content

Commit 5402518

Browse files
authored
Support customization of authorize header (#165)
* Support customization of authenticate response header * Add typing for custom authenticate header * Add tests for custom Authenticate header * Document authenticate header name setting
1 parent 414e410 commit 5402518

5 files changed

Lines changed: 126 additions & 10 deletions

File tree

README.md

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -205,6 +205,18 @@ fastify.register(require('@fastify/basic-auth'), {
205205
})
206206
```
207207

208+
The `authenticate` object can also have an optional `header` key allowing to customise the name of the header used instead of the default `WWW-Authenticate`:
209+
210+
```js
211+
fastify.register(require('@fastify/basic-auth'), {
212+
validate,
213+
authenticate: {
214+
header: 'Proxy-Authenticate' // Proxy-Authenticate: Basic
215+
}
216+
})
217+
```
218+
219+
208220
### `header` String (optional)
209221

210222
The `header` option specifies the header name to get credentials from for validation.

index.js

Lines changed: 11 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -132,7 +132,7 @@ async function fastifyBasicAuth (fastify, opts) {
132132
if (err.statusCode === 401) {
133133
const header = authenticateHeader(req)
134134
if (header) {
135-
reply.header('WWW-Authenticate', header)
135+
reply.header(...header)
136136
}
137137
}
138138
next(err)
@@ -144,28 +144,30 @@ async function fastifyBasicAuth (fastify, opts) {
144144
}
145145

146146
function getAuthenticateHeader (authenticate, useUtf8) {
147+
const defaultHeaderName = 'WWW-Authenticate'
147148
if (!authenticate) return () => false
148149
if (authenticate === true) {
149150
return useUtf8
150-
? () => 'Basic charset="UTF-8"'
151-
: () => 'Basic'
151+
? () => [defaultHeaderName, 'Basic charset="UTF-8"']
152+
: () => [defaultHeaderName, 'Basic']
152153
}
153154
if (typeof authenticate === 'object') {
154155
const realm = authenticate.realm
156+
const headerName = authenticate.header || defaultHeaderName
155157
switch (typeof realm) {
156158
case 'undefined':
157159
case 'boolean':
158160
return useUtf8
159-
? () => 'Basic charset="UTF-8"'
160-
: () => 'Basic'
161+
? () => [headerName, 'Basic charset="UTF-8"']
162+
: () => [headerName, 'Basic']
161163
case 'string':
162164
return useUtf8
163-
? () => `Basic realm="${realm}", charset="UTF-8"`
164-
: () => `Basic realm="${realm}"`
165+
? () => [headerName, `Basic realm="${realm}", charset="UTF-8"`]
166+
: () => [headerName, `Basic realm="${realm}"`]
165167
case 'function':
166168
return useUtf8
167-
? (req) => `Basic realm="${realm(req)}", charset="UTF-8"`
168-
: (req) => `Basic realm="${realm(req)}"`
169+
? (req) => [headerName, `Basic realm="${realm(req)}", charset="UTF-8"`]
170+
: (req) => [headerName, `Basic realm="${realm(req)}"`]
169171
}
170172
}
171173

test/index.test.js

Lines changed: 96 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -589,6 +589,102 @@ test('WWW-Authenticate Realm (authenticate: {realm: "example"}, utf8: true)', as
589589
t.assert.strictEqual(res2.statusCode, 200)
590590
})
591591

592+
test('WWW-Authenticate Custom Header (authenticate: {realm: "example", header: "x-custom-authenticate" }, utf8: false)', async t => {
593+
t.plan(8)
594+
595+
const fastify = Fastify()
596+
const authenticate = { realm: 'example', header: 'x-custom-authenticate' }
597+
fastify.register(basicAuth, { validate, authenticate, utf8: false })
598+
599+
function validate (username, password, _req, _res, done) {
600+
if (username === 'user' && password === 'pwd') {
601+
done()
602+
} else {
603+
done(new Error('Unauthorized'))
604+
}
605+
}
606+
607+
fastify.after(() => {
608+
fastify.route({
609+
method: 'GET',
610+
url: '/',
611+
preHandler: fastify.basicAuth,
612+
handler: (_req, reply) => {
613+
reply.send({ hello: 'world' })
614+
}
615+
})
616+
})
617+
618+
const res1 = await fastify.inject({
619+
url: '/',
620+
method: 'GET'
621+
})
622+
t.assert.ok(res1.body)
623+
t.assert.strictEqual(res1.headers['x-custom-authenticate'], 'Basic realm="example"')
624+
t.assert.strictEqual(res1.headers['www-authenticate'], undefined)
625+
t.assert.strictEqual(res1.statusCode, 401)
626+
627+
const res2 = await fastify.inject({
628+
url: '/',
629+
method: 'GET',
630+
headers: {
631+
authorization: basicAuthHeader('user', 'pwd')
632+
}
633+
})
634+
t.assert.ok(res2.body)
635+
t.assert.strictEqual(res2.headers['x-custom-authenticate'], undefined)
636+
t.assert.strictEqual(res2.headers['www-authenticate'], undefined)
637+
t.assert.strictEqual(res2.statusCode, 200)
638+
})
639+
640+
test('WWW-Authenticate Custom Header (authenticate: {realm: "example", header: "x-custom-authenticate" }, utf8: true)', async t => {
641+
t.plan(8)
642+
643+
const fastify = Fastify()
644+
const authenticate = { realm: 'example', header: 'x-custom-authenticate' }
645+
fastify.register(basicAuth, { validate, authenticate, utf8: true })
646+
647+
function validate (username, password, _req, _res, done) {
648+
if (username === 'user' && password === 'pwd') {
649+
done()
650+
} else {
651+
done(new Error('Unauthorized'))
652+
}
653+
}
654+
655+
fastify.after(() => {
656+
fastify.route({
657+
method: 'GET',
658+
url: '/',
659+
preHandler: fastify.basicAuth,
660+
handler: (_req, reply) => {
661+
reply.send({ hello: 'world' })
662+
}
663+
})
664+
})
665+
666+
const res1 = await fastify.inject({
667+
url: '/',
668+
method: 'GET'
669+
})
670+
t.assert.ok(res1.body)
671+
t.assert.strictEqual(res1.headers['x-custom-authenticate'], 'Basic realm="example", charset="UTF-8"')
672+
t.assert.strictEqual(res1.headers['www-authenticate'], undefined)
673+
t.assert.strictEqual(res1.statusCode, 401)
674+
675+
const res2 = await fastify.inject({
676+
url: '/',
677+
method: 'GET',
678+
headers: {
679+
authorization: basicAuthHeader('user', 'pwd')
680+
}
681+
})
682+
t.assert.ok(res2.body)
683+
t.assert.strictEqual(res2.headers['x-custom-authenticate'], undefined)
684+
t.assert.strictEqual(res2.headers['www-authenticate'], undefined)
685+
t.assert.strictEqual(res2.statusCode, 200)
686+
})
687+
592688
test('Header option specified', async t => {
593689
t.plan(2)
594690

types/index.d.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -28,7 +28,7 @@ declare namespace fastifyBasicAuth {
2828
reply: FastifyReply,
2929
done: (err?: Error) => void
3030
): void | Promise<void | Error>;
31-
authenticate?: boolean | { realm: string | ((req: FastifyRequest) => string) };
31+
authenticate?: boolean | { realm?: string | ((req: FastifyRequest) => string); header?: string };
3232
header?: string;
3333
strictCredentials?: boolean | undefined;
3434
utf8?: boolean | undefined;

types/index.test-d.ts

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -69,6 +69,12 @@ app.register(fastifyBasicAuth, {
6969
}
7070
})
7171

72+
// authenticate with custom header
73+
app.register(fastifyBasicAuth, {
74+
validate: () => {},
75+
authenticate: { header: 'x-custom-authenticate' }
76+
})
77+
7278
app.register(fastifyBasicAuth, {
7379
validate: () => {},
7480
strictCredentials: true

0 commit comments

Comments
 (0)