Nuxt 3 User Management CRUD | JWT Auth + RSA Encryption

In this tutorial, you will learn how to build a secure, production-ready REST API using Nuxt 3 (Nitro backend) with JWT authentication, RSA-encrypted passwords, and complete User Management CRUD, backed by MySQL and tested using Postman.

This tutorial shows how to build a production-ready REST API using Nuxt 3 (Nitro backend) with:

  • ๐Ÿ” RSA-encrypted passwords

  • ๐Ÿ”‘ JWT authentication

  • ๐Ÿ‘ฅ User Management CRUD

  • ๐Ÿ—„ MySQL database

  • ๐Ÿงช Full Postman testing (JSON included)

๐Ÿง  What You Will Build

A backend API that supports:

  • User registration

  • Secure login with RSA + bcrypt

  • JWT-protected routes

  • User profile

  • Full User CRUD (Create, Read, Update, Delete)

  • Logout

  • Postman testing

๐Ÿ” Authentication Flow (Simple)

  1. Client encrypts password using RSA Public Key

  2. Server decrypts using RSA Private Key

  3. Password verified with bcrypt

  4. JWT token generated

  5. JWT protects all private routes

๐Ÿ“ฆ Requirements

  • Node.js 18+

  • Nuxt 3

  • MySQL 8+

  • OpenSSL

  • Postman

1️⃣ Create Nuxt 3 Project

npx nuxi init nuxt-rsa-auth cd nuxt-rsa-auth

2️⃣ Install Backend Dependencies

npm install mysql2 bcryptjs jsonwebtoken

3️⃣ Correct Project Structure

server/ ├── api/ │ ├── auth/ │ │ ├── register.post.js │ │ ├── login.post.js │ │ ├── profile.get.js │ │ └── logout.post.js │ └── users/ │ ├── index.js ← /api/users │ └── [id].js ← /api/users/:id ├── middleware/ │ └── auth.js ├── utils/ │ ├── jwt.js │ └── rsa.js └── db/ └── mysql.js storage/ └── rsa/ ├── private.pem └── public.pem

⚠️ If this structure is wrong, routes WILL NOT work

4️⃣ Environment Variables (.env)

DB_HOST=localhost DB_USER=root DB_PASSWORD= DB_NAME=nuxt_rsa_auth JWT_SECRET=super_secret_key JWT_EXPIRES=1h

5️⃣ MySQL Database Setup

CREATE DATABASE nuxt_rsa_auth; USE nuxt_rsa_auth; CREATE TABLE users ( id INT AUTO_INCREMENT PRIMARY KEY, name VARCHAR(100), email VARCHAR(100) UNIQUE, password VARCHAR(255), role VARCHAR(20) DEFAULT 'user', created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP );

6️⃣ Generate RSA Keys

openssl genrsa -out storage/rsa/private.pem 2048 openssl rsa -in storage/rsa/private.pem -pubout -out storage/rsa/public.pem

⚠️ Never commit private.pem

7️⃣ MySQL Connection

// server/db/mysql.js import mysql from 'mysql2/promise' export const db = mysql.createPool({ host: process.env.DB_HOST, user: process.env.DB_USER, password: process.env.DB_PASSWORD, database: process.env.DB_NAME, })

8️⃣ JWT Helper

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

9️⃣ RSA Decryption Helper

// server/utils/rsa.js import fs from 'fs' import crypto from 'crypto' import path from 'path' const privateKey = fs.readFileSync( path.resolve('storage/rsa/private.pem'), 'utf8' ) export function decryptPassword(encrypted) { return crypto.privateDecrypt( { key: privateKey, padding: crypto.constants.RSA_PKCS1_PADDING, }, Buffer.from(encrypted, 'base64') ).toString() }

๐Ÿ”Ÿ Register API

// server/api/auth/register.post.js import bcrypt from 'bcryptjs' import { db } from '../../db/mysql' export default defineEventHandler(async (event) => { 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 registered successfully' } })

๐Ÿงช JSON – Register User

{ "name": "Normal User", "email": "user@test.com", "password": "12345678", "role": "user" }

1️⃣1️⃣ Login API (RSA + JWT)

// server/api/auth/login.post.js import bcrypt from 'bcryptjs' import { db } from '../../db/mysql' import { decryptPassword } from '../../utils/rsa' import { signToken } from '../../utils/jwt' export default defineEventHandler(async (event) => { const { email, password } = await readBody(event) const decrypted = decryptPassword(password) const [rows] = await db.query( 'SELECT * FROM users WHERE email=?', [email] ) if (!rows.length) { throw createError({ statusCode: 401, statusMessage: 'Invalid credentials' }) } const user = rows[0] const valid = await bcrypt.compare(decrypted, user.password) if (!valid) { throw createError({ statusCode: 401, statusMessage: 'Invalid credentials' }) } return { user: { id: user.id, email: user.email, role: user.role }, access_token: signToken(user), token_type: 'Bearer' } })

๐Ÿงช JSON – Login

{ "email": "user@test.com", "password": "RSA_ENCRYPTED_PASSWORD_BASE64" }

1️⃣2️⃣ JWT Middleware

// 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]) })

1️⃣3️⃣ Profile API

// server/api/auth/profile.get.js export default defineEventHandler((event) => { return event.context.user })

1️⃣4️⃣ User Management CRUD

๐Ÿ”น List Users – /api/users

// server/api/users/index.js import { db } from '../../db/mysql' import bcrypt from 'bcryptjs' export default defineEventHandler(async (event) => { if (event.node.req.method === 'GET') { const [rows] = await db.query( 'SELECT id, name, email, role, created_at FROM users' ) return rows } if (event.node.req.method === 'POST') { 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' } } })

๐Ÿงช JSON – Create User

{ "name": "New User", "email": "new@test.com", "password": "12345678", "role": "user" }

๐Ÿ”น Single User – /api/users/:id

// server/api/users/[id].js import { db } from '../../db/mysql' export default defineEventHandler(async (event) => { const id = event.context.params.id const method = event.node.req.method if (method === 'GET') { 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') { 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') { await db.query('DELETE FROM users WHERE id=?', [id]) return { message: 'User deleted successfully' } } })

๐Ÿงช JSON – Update User

{ "name": "Updated Name", "email": "updated@test.com", "role": "admin" }

1️⃣5️⃣ Logout API

// server/api/auth/logout.post.js export default defineEventHandler(() => { return { message: 'Logout handled on client side' } })

▶️ Run Application

npm run dev

Base URL:

http://localhost:3000/api

๐ŸŽฏ Final Result

You now have:

✅ RSA encrypted login
✅ JWT authentication
✅ Secure middleware
✅ Full User CRUD
✅ Correct Nuxt 3 routing
✅ All JSON bodies included
✅ Production-ready API

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