H
Hono•6mo ago
Rubyae

Combine JWT middleware with other middleware

I have a file made /middleware/jwt.ts:
import { Context, Next } from 'hono';
import { jwt } from 'hono/jwt';

export default function(c: Context<any, string, {}>, next: Next) {
const jwtMiddleware = jwt({
secret: process.env.JWT_SECRET!
});

return jwtMiddleware(c, next);
}
import { Context, Next } from 'hono';
import { jwt } from 'hono/jwt';

export default function(c: Context<any, string, {}>, next: Next) {
const jwtMiddleware = jwt({
secret: process.env.JWT_SECRET!
});

return jwtMiddleware(c, next);
}
Now I'm trying to make a different middleware that checks if the user has certain roles but for this I require the jwtPayload provided by the above middleware now I can do the following:
app.post('/', jwt, requireRoles([UserRole.Moderator, UserRole.Manager]), async (c) => {});
app.post('/', jwt, requireRoles([UserRole.Moderator, UserRole.Manager]), async (c) => {});
but it's kind of obvious that if you need to use requireRoles, jwt is also used so I'd like to use the jwt middleware inside the requireRoles middleware. Is this not possible or how should I do this? Because with everything I try I can't seem to make it work 🤔
3 Replies
Nico
Nico•6mo ago
Usually you can spread an array of middleware to "extend" middleware. But since yours requires parameters this should work as well.
import { createMiddleware } from 'hono/factory'
// https://hono.dev/helpers/factory#createmiddleware

const jwtMiddleware = createMiddleware((c, next) => {
const jwt = jwt({
secret: process.env.JWT_SECRET!
});

return jwt(c, next);
});


const rolesMiddleware = (roles) => createMiddleware((c, next) => {
// check roles
await next()
});


const requireRoles = (roles) => {
const customMiddleware = [jwt, rolesMiddleware(roles)]

return ...customMiddleware
}

export default requireRoles
import { createMiddleware } from 'hono/factory'
// https://hono.dev/helpers/factory#createmiddleware

const jwtMiddleware = createMiddleware((c, next) => {
const jwt = jwt({
secret: process.env.JWT_SECRET!
});

return jwt(c, next);
});


const rolesMiddleware = (roles) => createMiddleware((c, next) => {
// check roles
await next()
});


const requireRoles = (roles) => {
const customMiddleware = [jwt, rolesMiddleware(roles)]

return ...customMiddleware
}

export default requireRoles
I didn't test this, it's written in Discord so forgive any mistakes
Rubyae
Rubyae•6mo ago
No worries, I've adjusted your code so that it works but then I'm getting handler is not a function, I'm really not sure why things aren't working Update, I did find a solution that does work, not exactly what I was looking for though, what I did now is the following:
const requireRoles = (roles: Array<UserRole> = []) => createMiddleware(async(c, next) => {
const jwtPayload = c.get('jwtPayload');

if(roles.length) {
const query = await db.select().from(users).where(eq(users.id, jwtPayload.id));
if(roles.length > 0 && !roles.includes(query[0].role as UserRole)) {
console.log('lolzz')
return c.text('Unauthorized', 403);
}
}
await next();
})

export const authorize = (roles: Array<UserRole> = []) => [jwt, requireRoles(roles)];
const requireRoles = (roles: Array<UserRole> = []) => createMiddleware(async(c, next) => {
const jwtPayload = c.get('jwtPayload');

if(roles.length) {
const query = await db.select().from(users).where(eq(users.id, jwtPayload.id));
if(roles.length > 0 && !roles.includes(query[0].role as UserRole)) {
console.log('lolzz')
return c.text('Unauthorized', 403);
}
}
await next();
})

export const authorize = (roles: Array<UserRole> = []) => [jwt, requireRoles(roles)];
and then I can use the following for my route:
app.post('/', ...authorize(), async (c) => {});
app.post('/', ...authorize([UserRole.Moderator, UserRole.Manager]), async (c) => {});
app.post('/', ...authorize(), async (c) => {});
app.post('/', ...authorize([UserRole.Moderator, UserRole.Manager]), async (c) => {});
Nico
Nico•6mo ago
I had a feeling you might have to spread them directly I wasn’t sure if spreading them on the return would work Generally when I merge too I spread them on the route
Want results from more Discord servers?
Add your server