Building Role-Based Permissions in Nuxt 3 REST API with JWT and RSA Encryption

In this tutorial, you'll learn how to enhance your Nuxt 3 backend REST API by adding role-based permission control to your User Management CRUD operations. We'll use JWT for authentication, RSA for secure password encryption, and MySQL as our database.

This guide assumes you already have a Nuxt 3 project with JWT authentication, RSA encrypted password login, and basic CRUD for users.

Why Role-Based Permissions?

In real-world applications, not every authenticated user should have the same access rights. For example:

  • Admins can create, update, and delete users.

  • Regular users can only view their own profile or a list of users.

Role-based permissions help you control access securely and efficiently.

Step 1: Include User Role in JWT Token

Make sure your JWT payload includes the user role to check it on protected routes.

// server/utils/jwt.js import jwt from 'jsonwebtoken' export function signToken(user) { return jwt.sign( { id: user.id, email: user.email, role: user.role }, // Role added here process.env.JWT_SECRET, { expiresIn: process.env.JWT_EXPIRES } ) } export function verifyToken(token) { return jwt.verify(token, process.env.JWT_SECRET) }

Step 2: Protect Routes with JWT Middleware

Your middleware should extract and verify the JWT token, and attach the decoded user object (with role) to the request context.

// server/middleware/auth.js import { verifyToken } from '../utils/jwt' export default defineEventHandler((event) => { const publicRoutes = ['/api/auth/register', '/api/auth/login'] if (publicRoutes.some(r => event.node.req.url.startsWith(r))) return const auth = getHeader(event, 'authorization') if (!auth || !auth.startsWith('Bearer ')) { throw createError({ statusCode: 401, statusMessage: 'Token required' }) } event.context.user = verifyToken(auth.split(' ')[1]) })

Step 3: Create a Role Check Helper

A simple utility function to check if a user is an admin.

// server/utils/authorization.js export function checkAdminRole(user) { if (!user || user.role !== 'admin') { throw createError({ statusCode: 403, statusMessage: 'Forbidden: Admins only' }) } }

Step 4: Secure User Management Routes

List and Create Users — /api/users

// server/api/users/index.js import { db } from '../../db/mysql' import bcrypt from 'bcryptjs' import { checkAdminRole } from '../../utils/authorization' export default defineEventHandler(async (event) => { const method = event.node.req.method const user = event.context.user if (method === 'GET') { // Anyone logged in can list users const [rows] = await db.query( 'SELECT id, name, email, role, created_at FROM users' ) return rows } if (method === 'POST') { // Only admins can create new users checkAdminRole(user) const { name, email, password, role = 'user' } = await readBody(event) const hash = await bcrypt.hash(password, 10) await db.query( 'INSERT INTO users (name, email, password, role) VALUES (?, ?, ?, ?)', [name, email, hash, role] ) return { message: 'User created successfully' } } throw createError({ statusCode: 405, statusMessage: 'Method Not Allowed' }) })

View, Update, and Delete a Single User — /api/users/:id

// server/api/users/[id].js import { db } from '../../db/mysql' import { checkAdminRole } from '../../utils/authorization' export default defineEventHandler(async (event) => { const id = event.context.params.id const method = event.node.req.method const user = event.context.user if (method === 'GET') { // Anyone logged in can view user details const [rows] = await db.query( 'SELECT id, name, email, role FROM users WHERE id=?', [id] ) if (!rows.length) { throw createError({ statusCode: 404, statusMessage: 'User not found' }) } return rows[0] } if (method === 'PUT') { // Only admins can update user info checkAdminRole(user) const { name, email, role } = await readBody(event) await db.query( 'UPDATE users SET name=?, email=?, role=? WHERE id=?', [name, email, role, id] ) return { message: 'User updated successfully' } } if (method === 'DELETE') { // Only admins can delete users checkAdminRole(user) await db.query('DELETE FROM users WHERE id=?', [id]) return { message: 'User deleted successfully' } } throw createError({ statusCode: 405, statusMessage: 'Method Not Allowed' }) })

Step 5: Test with Postman

EndpointMethodHeaderBody (JSON)Access
/api/usersGETBearer <token>NoneAuthenticated users
/api/usersPOSTBearer <token>{ "name": "...", "email": "...", "password": "...", "role": "user/admin" }Admin only
/api/users/:idGETBearer <token>NoneAuthenticated users
/api/users/:idPUTBearer <token>{ "name": "...", "email": "...", "role": "user/admin" }Admin only
/api/users/:idDELETEBearer <token>NoneAdmin only

Conclusion

Adding role-based permissions enhances your app’s security by restricting sensitive operations to authorized users only.

You’ve learned how to:

  • Include user roles in JWT tokens

  • Enforce role checks in your API routes

  • Protect create, update, and delete operations to admins only

  • Keep read operations open to all authenticated users

This is a scalable and maintainable way to secure your API, ready for real-world use.

Want the full source code?
Download the complete Nuxt 3 JWT Authentication with RSA example from my GitHub repo here.

Souy Soeng

Souy Soeng

Hi there 👋, I’m Soeng Souy (StarCode Kh)
-------------------------------------------
🌱 I’m currently creating a sample Laravel and React Vue Livewire
👯 I’m looking to collaborate on open-source PHP & JavaScript projects
💬 Ask me about Laravel, MySQL, or Flutter
⚡ Fun fact: I love turning ☕️ into code!

Post a Comment

CAN FEEDBACK
close