UNPKG

@fastify/basic-auth

Version:
1,497 lines (1,300 loc) 35.9 kB
'use strict' const { test } = require('node:test') const Fastify = require('fastify') const basicAuth = require('..') const fastifyAuth = require('@fastify/auth') test('Basic', async t => { t.plan(2) const fastify = Fastify() await fastify.register(basicAuth, { validate }) async function validate (username, password, _req, _res) { if (username !== 'user' && password !== 'pwd') { return new Error('Unauthorized') } } fastify.after(() => { fastify.route({ method: 'GET', url: '/', preHandler: fastify.basicAuth, handler: (_req, reply) => { reply.send({ hello: 'world' }) } }) }) const { statusCode, body } = await fastify.inject({ url: '/', method: 'GET', headers: { authorization: basicAuthHeader('user', 'pwd') } }) t.assert.ok(body) t.assert.strictEqual(statusCode, 200) }) test('Basic utf8: true', async t => { t.plan(2) const fastify = Fastify() fastify.register(basicAuth, { validate, utf8: true }) function validate (username, password, _req, _res, done) { if (username === 'test' && password === '123\u00A3') { done() } else { done(new Error('Unauthorized')) } } fastify.after(() => { fastify.route({ method: 'GET', url: '/', preHandler: fastify.basicAuth, handler: (_req, reply) => { reply.send({ hello: 'world' }) } }) }) const { statusCode, body } = await fastify.inject({ url: '/', method: 'GET', headers: { /** * @see https://datatracker.ietf.org/doc/html/rfc7617#section-2.1 */ authorization: 'Basic dGVzdDoxMjPCow==' } }) t.assert.ok(body) t.assert.strictEqual(statusCode, 200) }) test('Basic - 401, sending utf8 credentials base64 but utf8: false', async t => { t.plan(3) const fastify = Fastify() fastify.register(basicAuth, { validate, utf8: false }) function validate (username, password, _req, _res, done) { if (username === 'test' && password === '123\u00A3') { done() } else { done(new Error('Unauthorized')) } } fastify.after(() => { fastify.route({ method: 'GET', url: '/', preHandler: fastify.basicAuth, handler: (_req, reply) => { reply.send({ hello: 'world' }) } }) }) const { statusCode, body } = await fastify.inject({ url: '/', method: 'GET', headers: { /** * @see https://datatracker.ietf.org/doc/html/rfc7617#section-2.1 */ authorization: 'Basic dGVzdDoxMjPCow==' } }) t.assert.ok(body) t.assert.strictEqual(statusCode, 401) t.assert.deepStrictEqual(JSON.parse(body), { error: 'Unauthorized', message: 'Unauthorized', statusCode: 401 }) }) test('Basic - 401', async t => { t.plan(3) const fastify = Fastify() fastify.register(basicAuth, { validate }) function validate (username, password, _req, _res, done) { if (username === 'user' && password === 'pwd') { done() } else { done(new Error('Winter is coming')) } } fastify.after(() => { fastify.route({ method: 'GET', url: '/', preHandler: fastify.basicAuth, handler: (_req, reply) => { reply.send({ hello: 'world' }) } }) }) const { statusCode, body } = await fastify.inject({ url: '/', method: 'GET', headers: { authorization: basicAuthHeader('user', 'pwdd') } }) t.assert.ok(body) t.assert.strictEqual(statusCode, 401) t.assert.deepStrictEqual(JSON.parse(body), { error: 'Unauthorized', message: 'Winter is coming', statusCode: 401 }) }) test('Basic - Invalid Header value /1', async t => { t.plan(3) const fastify = Fastify() fastify.register(basicAuth, { validate }) function validate (username, password, _req, _res, done) { if (username === 'user' && password === 'pwd') { done() } else { done(new Error('Winter is coming')) } } fastify.after(() => { fastify.route({ method: 'GET', url: '/', preHandler: fastify.basicAuth, handler: (_req, reply) => { reply.send({ hello: 'world' }) } }) }) const { statusCode, body } = await fastify.inject({ url: '/', method: 'GET', headers: { authorization: 'Bearer ' + Buffer.from('user:pass').toString('base64') } }) t.assert.ok(body) t.assert.strictEqual(statusCode, 401) t.assert.deepStrictEqual(JSON.parse(body), { code: 'FST_BASIC_AUTH_MISSING_OR_BAD_AUTHORIZATION_HEADER', error: 'Unauthorized', message: 'Missing or bad formatted authorization header', statusCode: 401 }) }) test('Basic - Invalid Header value /2', async t => { t.plan(3) const fastify = Fastify() fastify.register(basicAuth, { validate }) function validate (username, password, _req, _res, done) { if (username === 'user' && password === 'pwd') { done() } else { done(new Error('Winter is coming')) } } fastify.after(() => { fastify.route({ method: 'GET', url: '/', preHandler: fastify.basicAuth, handler: (_req, reply) => { reply.send({ hello: 'world' }) } }) }) const { statusCode, body } = await fastify.inject({ url: '/', method: 'GET', headers: { authorization: 'Basic ' + Buffer.from('user').toString('base64') } }) t.assert.ok(body) t.assert.strictEqual(statusCode, 401) t.assert.deepStrictEqual(JSON.parse(body), { code: 'FST_BASIC_AUTH_MISSING_OR_BAD_AUTHORIZATION_HEADER', error: 'Unauthorized', message: 'Missing or bad formatted authorization header', statusCode: 401 }) }) test('Basic - Invalid Header value /3', async t => { t.plan(3) const fastify = Fastify() fastify.register(basicAuth, { validate }) function validate (username, password, _req, _res, done) { if (username === 'user' && password === 'pwd') { done() } else { done(new Error('Winter is coming')) } } fastify.after(() => { fastify.route({ method: 'GET', url: '/', preHandler: fastify.basicAuth, handler: (_req, reply) => { reply.send({ hello: 'world' }) } }) }) const { statusCode, body } = await fastify.inject({ url: '/', method: 'GET', headers: { authorization: 'Basic ' + Buffer.from('user\x00:pwd').toString('base64') } }) t.assert.ok(body) t.assert.strictEqual(statusCode, 401) t.assert.deepStrictEqual(JSON.parse(body), { code: 'FST_BASIC_AUTH_MISSING_OR_BAD_AUTHORIZATION_HEADER', error: 'Unauthorized', message: 'Missing or bad formatted authorization header', statusCode: 401 }) }) test('Basic - strictCredentials: false', async t => { t.plan(2) const fastify = Fastify() fastify.register(basicAuth, { validate, strictCredentials: false }) function validate (username, password, _req, _res, done) { if (username === 'user' && password === 'pwd') { done() } else { done(new Error('Winter is coming')) } } fastify.after(() => { fastify.route({ method: 'GET', url: '/', preHandler: fastify.basicAuth, handler: (_req, reply) => { reply.send({ hello: 'world' }) } }) }) const { statusCode, body } = await fastify.inject({ url: '/', method: 'GET', headers: { authorization: ' Basic ' + Buffer.from('user:pwd').toString('base64') } }) t.assert.ok(body) t.assert.strictEqual(statusCode, 200) }) test('Basic with promises', async t => { t.plan(2) const fastify = Fastify() fastify.register(basicAuth, { validate }) function validate (username, password, _req, _res) { if (username === 'user' && password === 'pwd') { return Promise.resolve() } else { return Promise.reject(new Error('Unauthorized')) } } fastify.after(() => { fastify.route({ method: 'GET', url: '/', preHandler: fastify.basicAuth, handler: (_req, reply) => { reply.send({ hello: 'world' }) } }) }) const { statusCode, body } = await fastify.inject({ url: '/', method: 'GET', headers: { authorization: basicAuthHeader('user', 'pwd') } }) t.assert.ok(body) t.assert.strictEqual(statusCode, 200) }) test('Basic with promises - 401', async t => { t.plan(3) const fastify = Fastify() fastify.register(basicAuth, { validate }) function validate (username, password, _req, _res) { if (username === 'user' && password === 'pwd') { return Promise.resolve() } else { return Promise.reject(new Error('Winter is coming')) } } fastify.after(() => { fastify.route({ method: 'GET', url: '/', preHandler: fastify.basicAuth, handler: (_req, reply) => { reply.send({ hello: 'world' }) } }) }) const { statusCode, body } = await fastify.inject({ url: '/', method: 'GET', headers: { authorization: basicAuthHeader('user', 'pwdd') } }) t.assert.ok(body) t.assert.strictEqual(statusCode, 401) t.assert.deepStrictEqual(JSON.parse(body), { error: 'Unauthorized', message: 'Winter is coming', statusCode: 401 }) }) test('WWW-Authenticate (authenticate: true)', async t => { t.plan(6) const fastify = Fastify() const authenticate = true fastify.register(basicAuth, { validate, authenticate, utf8: false }) function validate (username, password, _req, _res, done) { if (username === 'user' && password === 'pwd') { done() } else { done(new Error('Unauthorized')) } } fastify.after(() => { fastify.route({ method: 'GET', url: '/', preHandler: fastify.basicAuth, handler: (_req, reply) => { reply.send({ hello: 'world' }) } }) }) const res1 = await fastify.inject({ url: '/', method: 'GET' }) t.assert.ok(res1.body) t.assert.strictEqual(res1.headers['www-authenticate'], 'Basic') t.assert.strictEqual(res1.statusCode, 401) const res2 = await fastify.inject({ url: '/', method: 'GET', headers: { authorization: basicAuthHeader('user', 'pwd') } }) t.assert.ok(res2.body) t.assert.strictEqual(res2.headers['www-authenticate'], undefined) t.assert.strictEqual(res2.statusCode, 200) }) test('WWW-Authenticate (authenticate: false)', async t => { t.plan(6) const fastify = Fastify() const authenticate = false fastify.register(basicAuth, { validate, authenticate, utf8: false }) function validate (username, password, _req, _res, done) { if (username === 'user' && password === 'pwd') { done() } else { done(new Error('Unauthorized')) } } fastify.after(() => { fastify.route({ method: 'GET', url: '/', preHandler: fastify.basicAuth, handler: (_req, reply) => { reply.send({ hello: 'world' }) } }) }) const res1 = await fastify.inject({ url: '/', method: 'GET' }) t.assert.ok(res1.body) t.assert.strictEqual(res1.headers['www-authenticate'], undefined) t.assert.strictEqual(res1.statusCode, 401) const res2 = await fastify.inject({ url: '/', method: 'GET', headers: { authorization: basicAuthHeader('user', 'pwd') } }) t.assert.ok(res2.body) t.assert.strictEqual(res2.headers['www-authenticate'], undefined) t.assert.strictEqual(res2.statusCode, 200) }) test('WWW-Authenticate Realm (authenticate: {realm: "example"}, utf8: false)', async t => { t.plan(6) const fastify = Fastify() const authenticate = { realm: 'example' } fastify.register(basicAuth, { validate, authenticate, utf8: false }) function validate (username, password, _req, _res, done) { if (username === 'user' && password === 'pwd') { done() } else { done(new Error('Unauthorized')) } } fastify.after(() => { fastify.route({ method: 'GET', url: '/', preHandler: fastify.basicAuth, handler: (_req, reply) => { reply.send({ hello: 'world' }) } }) }) const res1 = await fastify.inject({ url: '/', method: 'GET' }) t.assert.ok(res1.body) t.assert.strictEqual(res1.headers['www-authenticate'], 'Basic realm="example"') t.assert.strictEqual(res1.statusCode, 401) const res2 = await fastify.inject({ url: '/', method: 'GET', headers: { authorization: basicAuthHeader('user', 'pwd') } }) t.assert.ok(res2.body) t.assert.strictEqual(res2.headers['www-authenticate'], undefined) t.assert.strictEqual(res2.statusCode, 200) }) test('WWW-Authenticate Realm (authenticate: {realm: "example"}, utf8: true)', async t => { t.plan(6) const fastify = Fastify() const authenticate = { realm: 'example' } fastify.register(basicAuth, { validate, authenticate, utf8: true }) function validate (username, password, _req, _res, done) { if (username === 'user' && password === 'pwd') { done() } else { done(new Error('Unauthorized')) } } fastify.after(() => { fastify.route({ method: 'GET', url: '/', preHandler: fastify.basicAuth, handler: (_req, reply) => { reply.send({ hello: 'world' }) } }) }) const res1 = await fastify.inject({ url: '/', method: 'GET' }) t.assert.ok(res1.body) t.assert.strictEqual(res1.headers['www-authenticate'], 'Basic realm="example", charset="UTF-8"') t.assert.strictEqual(res1.statusCode, 401) const res2 = await fastify.inject({ url: '/', method: 'GET', headers: { authorization: basicAuthHeader('user', 'pwd') } }) t.assert.ok(res2.body) t.assert.strictEqual(res2.headers['www-authenticate'], undefined) t.assert.strictEqual(res2.statusCode, 200) }) test('WWW-Authenticate Custom Header (authenticate: {realm: "example", header: "x-custom-authenticate" }, utf8: false)', async t => { t.plan(8) const fastify = Fastify() const authenticate = { realm: 'example', header: 'x-custom-authenticate' } fastify.register(basicAuth, { validate, authenticate, utf8: false }) function validate (username, password, _req, _res, done) { if (username === 'user' && password === 'pwd') { done() } else { done(new Error('Unauthorized')) } } fastify.after(() => { fastify.route({ method: 'GET', url: '/', preHandler: fastify.basicAuth, handler: (_req, reply) => { reply.send({ hello: 'world' }) } }) }) const res1 = await fastify.inject({ url: '/', method: 'GET' }) t.assert.ok(res1.body) t.assert.strictEqual(res1.headers['x-custom-authenticate'], 'Basic realm="example"') t.assert.strictEqual(res1.headers['www-authenticate'], undefined) t.assert.strictEqual(res1.statusCode, 401) const res2 = await fastify.inject({ url: '/', method: 'GET', headers: { authorization: basicAuthHeader('user', 'pwd') } }) t.assert.ok(res2.body) t.assert.strictEqual(res2.headers['x-custom-authenticate'], undefined) t.assert.strictEqual(res2.headers['www-authenticate'], undefined) t.assert.strictEqual(res2.statusCode, 200) }) test('WWW-Authenticate Custom Header (authenticate: {realm: "example", header: "x-custom-authenticate" }, utf8: true)', async t => { t.plan(8) const fastify = Fastify() const authenticate = { realm: 'example', header: 'x-custom-authenticate' } fastify.register(basicAuth, { validate, authenticate, utf8: true }) function validate (username, password, _req, _res, done) { if (username === 'user' && password === 'pwd') { done() } else { done(new Error('Unauthorized')) } } fastify.after(() => { fastify.route({ method: 'GET', url: '/', preHandler: fastify.basicAuth, handler: (_req, reply) => { reply.send({ hello: 'world' }) } }) }) const res1 = await fastify.inject({ url: '/', method: 'GET' }) t.assert.ok(res1.body) t.assert.strictEqual(res1.headers['x-custom-authenticate'], 'Basic realm="example", charset="UTF-8"') t.assert.strictEqual(res1.headers['www-authenticate'], undefined) t.assert.strictEqual(res1.statusCode, 401) const res2 = await fastify.inject({ url: '/', method: 'GET', headers: { authorization: basicAuthHeader('user', 'pwd') } }) t.assert.ok(res2.body) t.assert.strictEqual(res2.headers['x-custom-authenticate'], undefined) t.assert.strictEqual(res2.headers['www-authenticate'], undefined) t.assert.strictEqual(res2.statusCode, 200) }) test("Proxy authentication (proxyMode: true, authenticate: { realm: 'example' }, utf8: true)", async t => { t.plan(12) const fastify = Fastify() const authenticate = { realm: 'example' } fastify.register(basicAuth, { validate, authenticate, utf8: true, proxyMode: true }) function validate (username, password, _req, _res, done) { if (username === 'user' && password === 'pwd') { done() } else { done(new Error('Unauthorized')) } } fastify.after(() => { fastify.route({ method: 'GET', url: '/', preHandler: fastify.basicAuth, handler: (_req, reply) => { reply.send({ hello: 'world' }) } }) }) const res1 = await fastify.inject({ url: '/', method: 'GET' }) t.assert.ok(res1.body) t.assert.strictEqual(res1.headers['proxy-authenticate'], 'Basic realm="example", charset="UTF-8"') t.assert.strictEqual(res1.headers['www-authenticate'], undefined) t.assert.strictEqual(res1.statusCode, 407) const res2 = await fastify.inject({ url: '/', method: 'GET', headers: { authorization: basicAuthHeader('user', 'pwd') } }) t.assert.ok(res2.body) t.assert.strictEqual(res2.headers['proxy-authenticate'], 'Basic realm="example", charset="UTF-8"') t.assert.strictEqual(res2.headers['www-authenticate'], undefined) t.assert.strictEqual(res2.statusCode, 407) const res3 = await fastify.inject({ url: '/', method: 'GET', headers: { 'proxy-authorization': basicAuthHeader('user', 'pwd') } }) t.assert.ok(res3.body) t.assert.strictEqual(res3.headers['proxy-authenticate'], undefined) t.assert.strictEqual(res3.headers['www-authenticate'], undefined) t.assert.strictEqual(res3.statusCode, 200) }) test('Header option specified', async t => { t.plan(2) const fastify = Fastify() fastify.register(basicAuth, { validate, header: 'X-Forwarded-Authorization' }) function validate (username, password, _req, _res, done) { if (username === 'user' && password === 'pwd') { done() } else { done(new Error('Unauthorized')) } } fastify.after(() => { fastify.route({ method: 'GET', url: '/', preHandler: fastify.basicAuth, handler: (_req, reply) => { reply.send({ hello: 'world' }) } }) }) const { statusCode, body } = await fastify.inject({ url: '/', method: 'GET', headers: { authorization: basicAuthHeader('notuser', 'notpwd'), 'x-forwarded-authorization': basicAuthHeader('user', 'pwd') } }) t.assert.ok(body) t.assert.strictEqual(statusCode, 200) }) test('Missing validate function', async t => { t.plan(2) const fastify = Fastify() fastify.register(basicAuth) await t.assert.rejects( async () => fastify.ready(), (err) => { t.assert.strictEqual(err.message, 'Basic Auth: Missing validate function') return true } ) }) test('Hook - 401', async t => { t.plan(3) const fastify = Fastify() fastify .register(basicAuth, { validate }) function validate (username, password, _req, _res, done) { if (username === 'user' && password === 'pwd') { done() } else { done(new Error('Winter is coming')) } } fastify.after(() => { fastify.addHook('preHandler', fastify.basicAuth) fastify.route({ method: 'GET', url: '/', handler: (_req, reply) => { reply.send({ hello: 'world' }) } }) }) const { statusCode, body } = await fastify.inject({ url: '/', method: 'GET', headers: { authorization: basicAuthHeader('user', 'pwdd') } }) t.assert.ok(body) t.assert.strictEqual(statusCode, 401) t.assert.deepStrictEqual(JSON.parse(body), { error: 'Unauthorized', message: 'Winter is coming', statusCode: 401 }) }) test('With @fastify/auth - 401', async t => { t.plan(3) const fastify = Fastify() fastify .register(fastifyAuth) .register(basicAuth, { validate }) function validate (username, password, _req, _res, done) { if (username === 'user' && password === 'pwd') { done() } else { done(new Error('Winter is coming')) } } fastify.after(() => { fastify.route({ method: 'GET', url: '/', preHandler: fastify.auth([fastify.basicAuth]), handler: (_req, reply) => { reply.send({ hello: 'world' }) } }) }) const { statusCode, body } = await fastify.inject({ url: '/', method: 'GET', headers: { authorization: basicAuthHeader('user', 'pwdd') } }) t.assert.ok(body) t.assert.strictEqual(statusCode, 401) t.assert.deepStrictEqual(JSON.parse(body), { error: 'Unauthorized', message: 'Winter is coming', statusCode: 401 }) }) test('Hook with @fastify/auth- 401', async t => { t.plan(3) const fastify = Fastify() fastify .register(fastifyAuth) .register(basicAuth, { validate }) function validate (username, password, _req, _res, done) { if (username === 'user' && password === 'pwd') { done() } else { done(new Error('Winter is coming')) } } fastify.after(() => { fastify.addHook('preHandler', fastify.auth([fastify.basicAuth])) fastify.route({ method: 'GET', url: '/', handler: (_req, reply) => { reply.send({ hello: 'world' }) } }) }) const { statusCode, body } = await fastify.inject({ url: '/', method: 'GET', headers: { authorization: basicAuthHeader('user', 'pwdd') } }) t.assert.ok(body) t.assert.strictEqual(statusCode, 401) t.assert.deepStrictEqual(JSON.parse(body), { error: 'Unauthorized', message: 'Winter is coming', statusCode: 401 }) }) test('Missing header', async t => { t.plan(3) const fastify = Fastify() fastify.register(basicAuth, { validate }) function validate (username, password, _req, _res, done) { if (username === 'user' && password === 'pwd') { done() } else { done(new Error('Unauthorized')) } } fastify.after(() => { fastify.route({ method: 'GET', url: '/', preHandler: fastify.basicAuth, handler: (_req, reply) => { reply.send({ hello: 'world' }) } }) }) const { statusCode, body } = await fastify.inject({ url: '/', method: 'GET' }) t.assert.ok(body) t.assert.strictEqual(statusCode, 401) t.assert.deepStrictEqual(JSON.parse(body), { statusCode: 401, code: 'FST_BASIC_AUTH_MISSING_OR_BAD_AUTHORIZATION_HEADER', error: 'Unauthorized', message: 'Missing or bad formatted authorization header' }) }) test('Fastify context', async t => { t.plan(3) const fastify = Fastify() fastify.decorate('test', true) fastify.register(basicAuth, { validate }) function validate (username, password, _req, _res, done) { t.assert.ok(this.test) if (username === 'user' && password === 'pwd') { done() } else { done(new Error('Unauthorized')) } } fastify.after(() => { fastify.route({ method: 'GET', url: '/', preHandler: fastify.basicAuth, handler: (_req, reply) => { reply.send({ hello: 'world' }) } }) }) const { statusCode, body } = await fastify.inject({ url: '/', method: 'GET', headers: { authorization: basicAuthHeader('user', 'pwd') } }) t.assert.ok(body) t.assert.strictEqual(statusCode, 200) }) test('setErrorHandler custom error and 401', async t => { t.plan(4) const fastify = Fastify() fastify .register(fastifyAuth) .register(basicAuth, { validate }) function validate (_username, _password, _req, _res, done) { done(new Error('Winter is coming')) } fastify.after(() => { fastify.addHook('preHandler', fastify.auth([fastify.basicAuth])) fastify.route({ method: 'GET', url: '/', handler: (_req, reply) => { reply.send({ hello: 'world' }) } }) }) fastify.setErrorHandler(function (err, _req, reply) { t.assert.strictEqual(err.statusCode, 401) reply.send(err) }) const { statusCode, body } = await fastify.inject({ url: '/', method: 'GET', headers: { authorization: basicAuthHeader('user', 'pwdd') } }) t.assert.ok(body) t.assert.strictEqual(statusCode, 401) t.assert.deepStrictEqual(JSON.parse(body), { error: 'Unauthorized', message: 'Winter is coming', statusCode: 401 }) }) test('Missing header and custom error handler', async t => { t.plan(6) const fastify = Fastify() fastify.register(basicAuth, { validate }) function validate (username, password, _req, _res, done) { if (username === 'user' && password === 'pwd') { done() } else { done(new Error('Unauthorized')) } } fastify.after(() => { fastify.route({ method: 'GET', url: '/', preHandler: fastify.basicAuth, handler: (_req, reply) => { reply.send({ hello: 'world' }) } }) }) fastify.setErrorHandler(function (err, _req, reply) { t.assert.ok(err instanceof Error) t.assert.ok(err.statusCode === 401) t.assert.ok(err.code === 'FST_BASIC_AUTH_MISSING_OR_BAD_AUTHORIZATION_HEADER') reply.send(err) }) const { statusCode, body } = await fastify.inject({ url: '/', method: 'GET' }) t.assert.ok(body) t.assert.strictEqual(statusCode, 401) t.assert.deepStrictEqual(JSON.parse(body), { statusCode: 401, code: 'FST_BASIC_AUTH_MISSING_OR_BAD_AUTHORIZATION_HEADER', error: 'Unauthorized', message: 'Missing or bad formatted authorization header' }) }) test('Invalid options (authenticate)', async t => { t.plan(2) const fastify = Fastify() fastify .register(basicAuth, { validate, authenticate: 'i am invalid' }) async function validate (username, password, _req, _res) { if (username !== 'user' && password !== 'pwd') { return new Error('Unauthorized') } } await t.assert.rejects( async () => fastify.ready(), (err) => { t.assert.strictEqual(err.message, 'Basic Auth: Invalid authenticate option') return true } ) }) test('authenticate: true, utf8: true', async t => { t.plan(6) const fastify = Fastify() fastify .register(basicAuth, { validate, authenticate: true, utf8: true }) function validate (username, password, _req, _res, done) { if (username === 'user' && password === 'pwd') { done() } else { done(new Error('Unauthorized')) } } fastify.after(() => { fastify.route({ method: 'GET', url: '/', preHandler: fastify.basicAuth, handler: (_req, reply) => { reply.send({ hello: 'world' }) } }) }) let res = await fastify.inject({ url: '/', method: 'GET' }) t.assert.ok(res.body) t.assert.strictEqual(res.headers['www-authenticate'], 'Basic charset="UTF-8"') t.assert.strictEqual(res.statusCode, 401) res = await fastify.inject({ url: '/', method: 'GET', headers: { authorization: basicAuthHeader('user', 'pwd') } }) t.assert.ok(res.body) t.assert.strictEqual(res.headers['www-authenticate'], undefined) t.assert.strictEqual(res.statusCode, 200) }) test('authenticate realm: false, utf8: true', async t => { t.plan(6) const fastify = Fastify() fastify .register(basicAuth, { validate, authenticate: { realm: false }, utf8: true }) function validate (username, password, _req, _res, done) { if (username === 'user' && password === 'pwd') { done() } else { done(new Error('Unauthorized')) } } fastify.after(() => { fastify.route({ method: 'GET', url: '/', preHandler: fastify.basicAuth, handler: (_req, reply) => { reply.send({ hello: 'world' }) } }) }) let res = await fastify.inject({ url: '/', method: 'GET' }) t.assert.ok(res.body) t.assert.strictEqual(res.headers['www-authenticate'], 'Basic charset="UTF-8"') t.assert.strictEqual(res.statusCode, 401) res = await fastify.inject({ url: '/', method: 'GET', headers: { authorization: basicAuthHeader('user', 'pwd') } }) t.assert.ok(res.body) t.assert.strictEqual(res.headers['www-authenticate'], undefined) t.assert.strictEqual(res.statusCode, 200) }) test('Invalid options (authenticate realm, utf8: false)', async t => { t.plan(6) const fastify = Fastify() await fastify .register(basicAuth, { validate, authenticate: { realm: true }, utf8: false }) function validate (username, password, _req, _res, done) { if (username === 'user' && password === 'pwd') { done() } else { done(new Error('Unauthorized')) } } fastify.after(() => { fastify.route({ method: 'GET', url: '/', preHandler: fastify.basicAuth, handler: (_req, reply) => { reply.send({ hello: 'world' }) } }) }) let res = await fastify.inject({ url: '/', method: 'GET' }) t.assert.ok(res.body) t.assert.strictEqual(res.headers['www-authenticate'], 'Basic') t.assert.strictEqual(res.statusCode, 401) res = await fastify.inject({ url: '/', method: 'GET', headers: { authorization: basicAuthHeader('user', 'pwd') } }) t.assert.ok(res.body) t.assert.strictEqual(res.headers['www-authenticate'], undefined) t.assert.strictEqual(res.statusCode, 200) }) test('Invalid options (authenticate realm), utf8: true', async t => { t.plan(6) const fastify = Fastify() fastify .register(basicAuth, { validate, utf8: true, authenticate: { realm: true } }) function validate (username, password, _req, _res, done) { if (username === 'user' && password === 'pwd') { done() } else { done(new Error('Unauthorized')) } } fastify.after(() => { fastify.route({ method: 'GET', url: '/', preHandler: fastify.basicAuth, handler: (_req, reply) => { reply.send({ hello: 'world' }) } }) }) let res = await fastify.inject({ url: '/', method: 'GET' }) t.assert.ok(res.body) t.assert.strictEqual(res.headers['www-authenticate'], 'Basic charset="UTF-8"') t.assert.strictEqual(res.statusCode, 401) res = await fastify.inject({ url: '/', method: 'GET', headers: { authorization: basicAuthHeader('user', 'pwd') } }) t.assert.ok(res.body) t.assert.strictEqual(res.headers['www-authenticate'], undefined) t.assert.strictEqual(res.statusCode, 200) }) test('Invalid options (authenticate realm = undefined, utf8: false)', async t => { t.plan(6) const fastify = Fastify() fastify .register(basicAuth, { validate, authenticate: { realm: undefined }, utf8: false }) function validate (username, password, _req, _res, done) { if (username === 'user' && password === 'pwd') { done() } else { done(new Error('Unauthorized')) } } fastify.after(() => { fastify.route({ method: 'GET', url: '/', preHandler: fastify.basicAuth, handler: (_req, reply) => { reply.send({ hello: 'world' }) } }) }) let res = await fastify.inject({ url: '/', method: 'GET' }) t.assert.ok(res.body) t.assert.strictEqual(res.headers['www-authenticate'], 'Basic') t.assert.strictEqual(res.statusCode, 401) res = await fastify.inject({ url: '/', method: 'GET', headers: { authorization: basicAuthHeader('user', 'pwd') } }) t.assert.ok(res.body) t.assert.strictEqual(res.headers['www-authenticate'], undefined) t.assert.strictEqual(res.statusCode, 200) }) test('WWW-Authenticate Realm (authenticate: {realm (req) { }}, utf8: false)', async t => { t.plan(7) const fastify = Fastify() const authenticate = { realm (req) { t.assert.strictEqual(req.url, '/') return 'root' } } fastify.register(basicAuth, { validate, authenticate, utf8: false }) function validate (username, password, _req, _res, done) { if (username === 'user' && password === 'pwd') { done() } else { done(new Error('Unauthorized')) } } fastify.after(() => { fastify.route({ method: 'GET', url: '/', preHandler: fastify.basicAuth, handler: (_req, reply) => { reply.send({ hello: 'world' }) } }) }) let res = await fastify.inject({ url: '/', method: 'GET' }) t.assert.ok(res.body) t.assert.strictEqual(res.headers['www-authenticate'], 'Basic realm="root"') t.assert.strictEqual(res.statusCode, 401) res = await fastify.inject({ url: '/', method: 'GET', headers: { authorization: basicAuthHeader('user', 'pwd') } }) t.assert.ok(res.body) t.assert.strictEqual(res.headers['www-authenticate'], undefined) t.assert.strictEqual(res.statusCode, 200) }) test('WWW-Authenticate Realm (authenticate: {realm (req) { }}), utf8', async t => { t.plan(7) const fastify = Fastify() const authenticate = { realm (req) { t.assert.strictEqual(req.url, '/') return 'root' } } fastify.register(basicAuth, { validate, authenticate, utf8: true }) function validate (username, password, _req, _res, done) { if (username === 'user' && password === 'pwd') { done() } else { done(new Error('Unauthorized')) } } fastify.after(() => { fastify.route({ method: 'GET', url: '/', preHandler: fastify.basicAuth, handler: (_req, reply) => { reply.send({ hello: 'world' }) } }) }) let res = await fastify.inject({ url: '/', method: 'GET' }) t.assert.ok(res.body) t.assert.strictEqual(res.headers['www-authenticate'], 'Basic realm="root", charset="UTF-8"') t.assert.strictEqual(res.statusCode, 401) res = await fastify.inject({ url: '/', method: 'GET', headers: { authorization: basicAuthHeader('user', 'pwd') } }) t.assert.ok(res.body) t.assert.strictEqual(res.headers['www-authenticate'], undefined) t.assert.strictEqual(res.statusCode, 200) }) test('No 401 no realm', async t => { t.plan(4) const fastify = Fastify() fastify.register(basicAuth, { validate, authenticate: true }) function validate (_username, _password, _req, _res) { const err = new Error('Winter is coming') err.statusCode = 402 return Promise.reject(err) } fastify.after(() => { fastify.route({ method: 'GET', url: '/', preHandler: fastify.basicAuth, handler: (_req, reply) => { reply.send({ hello: 'world' }) } }) }) const { headers, statusCode, body } = await fastify.inject({ url: '/', method: 'GET', headers: { authorization: basicAuthHeader('user', 'pwdd') } }) t.assert.ok(body) t.assert.strictEqual(statusCode, 402) t.assert.strictEqual(headers['www-authenticate'], undefined) t.assert.deepStrictEqual(JSON.parse(body), { error: 'Payment Required', message: 'Winter is coming', statusCode: 402 }) }) function basicAuthHeader (username, password) { return 'Basic ' + Buffer.from(`${username}:${password}`).toString('base64') }