diff --git a/api/plugins/fastify-jwt-authz.test.ts b/api/plugins/fastify-jwt-authz.test.ts index 7399fb3225d..9067266ae92 100644 --- a/api/plugins/fastify-jwt-authz.test.ts +++ b/api/plugins/fastify-jwt-authz.test.ts @@ -218,4 +218,75 @@ describe('fastify-jwt-authz', { only: true }, () => { assert.strictEqual(res.statusCode, 200); assert.strictEqual(resData.foo, 'bar'); }); + + it('should throw an error when there is no callback', async () => { + const fastify = Fastify(); + await fastify.register(jwtAuthz); + + fastify.get( + '/test7', + { + preHandler: function (request, _reply, done) { + request.user = { + name: 'sample', + scope: 123 + }; + + request.jwtAuthz(['baz']); + done(); + } + }, + function () { + return { foo: 'bar' }; + } + ); + + fastify.listen({ port: 0 }, function () { + fastify.server.unref(); + }); + + const res = await fastify.inject({ + method: 'GET', + url: '/test7' + }); + const resData: ErrorResponse = res.json(); + + assert.strictEqual(res.statusCode, 500); + assert.strictEqual(resData.message, 'request.user.scope must be a string'); + }); + + it('should verify user scope when there is no callback', async () => { + const fastify = Fastify(); + await fastify.register(jwtAuthz); + + fastify.get( + '/test8', + { + preHandler: function (request, _reply, done) { + request.user = { + name: 'sample', + scope: 'user manager' + }; + request.jwtAuthz(['user']); + done(); + } + }, + function () { + return { foo: 'bar' }; + } + ); + + fastify.listen({ port: 0 }, function () { + fastify.server.unref(); + }); + + const res = await fastify.inject({ + method: 'GET', + url: '/test8' + }); + const resData: { foo: string } = res.json(); + + assert.strictEqual(res.statusCode, 200); + assert.strictEqual(resData.foo, 'bar'); + }); }); diff --git a/api/plugins/fastify-jwt-authz.ts b/api/plugins/fastify-jwt-authz.ts index c7eabb60561..388d42bfb25 100644 --- a/api/plugins/fastify-jwt-authz.ts +++ b/api/plugins/fastify-jwt-authz.ts @@ -26,49 +26,41 @@ import { FastifyPluginCallback, FastifyRequest } from 'fastify'; import fp from 'fastify-plugin'; interface UserObject { - scope: string; + scope?: string; } -export interface JwtAuthz { - (scopes: string[], callback: (err?: Error) => void): Promise; +interface JwtAuthz { + (scopes: string[], callback?: (err?: Error) => void): void; } const fastifyJwtAuthz: FastifyPluginCallback = (fastify, _opts, done) => { - fastify.decorateRequest('jwtAuthz', checkScopes); + fastify.decorateRequest('jwtAuthz', jwtAuthz); - done(); + function checkScopes(user: UserObject, scopes: string[]) { + if (scopes.length === 0) return Error('Scopes cannot be empty'); - function checkScopes( - this: FastifyRequest, - scopes: string[], - callback: (err?: Error) => void - ) { - if (callback === undefined) { - return new Promise((resolve, reject) => { - void this.jwtAuthz(scopes, function (err) { - return err ? reject(err) : resolve(null); - }); - }); - } + if (!user) return Error('request.user does not exist'); - if (scopes.length === 0) { - return callback(new Error('Scopes cannot be empty')); - } - const user = this.user as UserObject; - if (!user) { - return callback(new Error('request.user does not exist')); - } - if (typeof user.scope !== 'string') { - return callback(new Error('request.user.scope must be a string')); - } + if (typeof user.scope !== 'string') + return Error('request.user.scope must be a string'); const userScopes = user.scope.split(' '); - const allowed = scopes.some(scope => { - return userScopes.indexOf(scope) !== -1; - }); + const sufficientScope = scopes.some(scope => userScopes.includes(scope)); - return callback(allowed ? undefined : new Error('Insufficient scope')); + if (!sufficientScope) return Error('Insufficient scope'); } + + function jwtAuthz( + this: FastifyRequest, + scopes: string[], + callback?: (err?: Error) => void + ) { + const err = checkScopes(this.user as UserObject, scopes); + if (callback) return callback(err); + if (err) throw err; + } + + done(); }; declare module 'fastify' {