Node Blueprint CLI includes production-ready JWT authentication with secure password hashing, refresh tokens, and role-based access control.
Overview
When you enable JWT authentication, the CLI generates:
Complete authentication system with signup/login/logout
Bcrypt password hashing
Access and refresh token management
Token storage in database
Role-based user system (admin/user)
Authentication middleware
Validation schemas with Zod
Authentication is optional. Select “jwt-auth” when prompted or choose “none” to skip authentication setup.
Features
JWT Tokens Access tokens for API requests and refresh tokens for session management
Bcrypt Hashing Secure password hashing with configurable salt rounds
Role-Based Access Built-in admin and user roles for authorization
Token Storage Refresh tokens stored in database with proper relations
Generated Files
Authentication adds these files to your project:
src/
├── routes/
│ └── auth-routes.ts # Auth endpoints (signup, login, logout)
├── controllers/
│ └── auth-controller.ts # Auth request handlers
├── services/
│ └── auth-services.ts # JWT generation and verification
├── validations/
│ └── auth-validations.ts # Zod schemas for validation
├── enums/
│ ├── role-enum.ts # User roles (admin, user)
│ └── token-enum.ts # Token types
└── db/schema/ # or models/ for Mongoose
└── token-schema.ts # Token storage schema
Authentication Flow
1. User Signup
Endpoint: POST /api/v1/auth/signup
Request Body:
Response:
{
"success" : true ,
"message" : "User created successfully" ,
"data" : {
"user" : {
"id" : "uuid" ,
"name" : "John Doe" ,
"email" : "[email protected] " ,
"role" : "user"
},
"accessToken" : "eyJhbGciOiJIUzI1NiIs..." ,
"refreshToken" : "eyJhbGciOiJIUzI1NiIs..."
}
}
2. User Login
Endpoint: POST /api/v1/auth/login
Request Body:
Response:
{
"success" : true ,
"message" : "Login successful" ,
"data" : {
"user" : {
"id" : "uuid" ,
"name" : "John Doe" ,
"email" : "[email protected] " ,
"role" : "user"
},
"accessToken" : "eyJhbGciOiJIUzI1NiIs..." ,
"refreshToken" : "eyJhbGciOiJIUzI1NiIs..."
}
}
3. User Logout
Endpoint: POST /api/v1/auth/logout
Headers:
Authorization: Bearer <access_token>
Response:
{
"success" : true ,
"message" : "Logout successful"
}
Token Management
Access Tokens
Short-lived tokens for API authentication
Included in Authorization header
Default expiration: 15 minutes (configurable)
Refresh Tokens
Long-lived tokens for obtaining new access tokens
Stored in database with user relation
Default expiration: 7 days (configurable)
Can be revoked by logging out
Token Types
Token Enum (src/enums/token-enum.ts):
export enum TokenEnum {
REFRESH_TOKEN = "refresh_token" ,
RESET_PASSWORD_TOKEN = "reset_password_token" ,
VERIFY_EMAIL_TOKEN = "verify_email_token"
}
Database Schema
Drizzle (PostgreSQL)
Prisma
Mongoose
Token Schema (src/db/schema/token-schema.ts):import { pgTable , text , timestamp , uuid , pgEnum } from "drizzle-orm/pg-core" ;
import { userSchema } from "./user-schema.js" ;
import { TokenEnum } from "../../enums/token-enum.js" ;
// create token enum type
const tokenEnum = pgEnum ( "token" , Object . values ( TokenEnum ) as [ string , ... string []]);
// schema definition
export const tokenSchema = pgTable ( "token" , {
id: uuid ( "id" ). primaryKey (). unique (). notNull (). defaultRandom (),
value: text ( "value" ). notNull (),
type: tokenEnum (). notNull (),
userId: uuid ( "user_id" ). notNull (). references (() => userSchema . id ),
createdAt: timestamp ( "created_at" ). notNull (). defaultNow (),
updatedAt: timestamp ( "updated_at" ). notNull (). defaultNow ()
});
Schema (prisma/schema.prisma):enum TokenEnum {
refresh_token
reset_password_token
verify_email_token
}
model Token {
id String @id @unique @default ( uuid ())
value String
type TokenEnum
userId String
user User @relation ( fields : [ userId ], references : [ id ] )
createdAt DateTime @default ( now ())
updatedAt DateTime @updatedAt
@@index ( [ userId ] )
}
Token Model (src/models/token-model.ts):import { Schema , model , Document } from "mongoose" ;
import { TokenEnum } from "../enums/token-enum.js" ;
interface IToken extends Document {
value : string ;
type : TokenEnum ;
userId : string ;
createdAt : Date ;
updatedAt : Date ;
}
const tokenSchema = new Schema < IToken >(
{
value: { type: String , required: true },
type: { type: String , enum: Object . values ( TokenEnum ), required: true },
userId: { type: String , required: true , ref: "User" }
},
{
timestamps: true
}
);
const Token = model < IToken >( "Token" , tokenSchema );
export default Token ;
User Roles
Role Enum (src/enums/role-enum.ts):
export enum RoleEnum {
ADMIN = "admin" ,
USER = "user"
}
Roles are stored in the user table and can be used for authorization:
// Example: Check if user is admin
if ( user . role === RoleEnum . ADMIN ) {
// Admin-only logic
}
Validation Schemas
Auth Validations (src/validations/auth-validations.ts):
import { z } from "zod" ;
export const signupSchema = z . object ({
name: z . string (). min ( 2 ). max ( 50 ). optional (),
email: z . string (). email (),
password: z . string (). min ( 8 ). max ( 100 )
});
export const loginSchema = z . object ({
email: z . string (). email (),
password: z . string ()
});
export type SignupInput = z . infer < typeof signupSchema >;
export type LoginInput = z . infer < typeof loginSchema >;
The default validation requires:
Minimum 8 characters
Maximum 100 characters
You can customize these requirements in auth-validations.ts: password : z . string ()
. min ( 8 , "Password must be at least 8 characters" )
. max ( 100 , "Password must be less than 100 characters" )
. regex ( / [ A-Z ] / , "Password must contain uppercase letter" )
. regex ( / [ a-z ] / , "Password must contain lowercase letter" )
. regex ( / [ 0-9 ] / , "Password must contain a number" )
. regex ( / [ ^ A-Za-z0-9 ] / , "Password must contain special character" )
JWT Service
Auth Services (src/services/auth-services.ts):
import jwt from "jsonwebtoken" ;
import { env } from "../config/env.js" ;
export const generateAccessToken = ( userId : string ) => {
return jwt . sign (
{ userId },
env . JWT_ACCESS_SECRET ,
{ expiresIn: "15m" }
);
};
export const generateRefreshToken = ( userId : string ) => {
return jwt . sign (
{ userId },
env . JWT_REFRESH_SECRET ,
{ expiresIn: "7d" }
);
};
export const verifyAccessToken = ( token : string ) => {
return jwt . verify ( token , env . JWT_ACCESS_SECRET );
};
export const verifyRefreshToken = ( token : string ) => {
return jwt . verify ( token , env . JWT_REFRESH_SECRET );
};
Environment Variables
Authentication requires these environment variables in .env:
# JWT Secrets (generate secure random strings)
JWT_ACCESS_SECRET = your-super-secret-access-key-change-this
JWT_REFRESH_SECRET = your-super-secret-refresh-key-change-this
# Optional: Token expiration times
JWT_ACCESS_EXPIRES_IN = 15m
JWT_REFRESH_EXPIRES_IN = 7d
# Bcrypt salt rounds
BCRYPT_SALT_ROUNDS = 10
Always use strong, randomly generated secrets in production. Never commit your .env file to version control.
Dependencies
{
"dependencies" : {
"jsonwebtoken" : "^9.0.2" ,
"bcrypt" : "^6.0.0" ,
"zod" : "^3.25.64"
},
"devDependencies" : {
"@types/jsonwebtoken" : "^9.0.9" ,
"@types/bcrypt" : "^5.0.2"
}
}
Security Best Practices
Passwords are hashed with bcrypt (10 salt rounds)
Original passwords never stored in database
Password field excluded from query results by default
Minimum 8 character requirement enforced
Access tokens are short-lived (15 minutes)
Refresh tokens stored in database for revocation
Separate secrets for access and refresh tokens
Tokens invalidated on logout
All auth endpoints validate input with Zod
Email addresses normalized (lowercase)
Rate limiting recommended for production
HTTPS required in production
Example Usage
Protecting Routes
Create an authentication middleware to protect routes:
import { Request , Response , NextFunction } from "express" ;
import { verifyAccessToken } from "../services/auth-services.js" ;
export const authMiddleware = async (
req : Request ,
res : Response ,
next : NextFunction
) => {
try {
const token = req . headers . authorization ?. split ( " " )[ 1 ];
if ( ! token ) {
return res . status ( 401 ). json ({
success: false ,
message: "No token provided"
});
}
const decoded = verifyAccessToken ( token );
req . user = decoded ; // Attach user to request
next ();
} catch ( error ) {
return res . status ( 401 ). json ({
success: false ,
message: "Invalid token"
});
}
};
Using Protected Routes
import { Router } from "express" ;
import { authMiddleware } from "../middlewares/auth-middleware.js" ;
import { getUserProfile } from "../controllers/user-controller.js" ;
const router = Router ();
// Protected route
router . get ( "/profile" , authMiddleware , getUserProfile );
export default router ;
Quick Start
Enable JWT Auth
Select “jwt-auth” when running the CLI npx create-node-blueprint my-app
Configure secrets
Update .env with secure JWT secrets JWT_ACCESS_SECRET = $( openssl rand -base64 32 )
JWT_REFRESH_SECRET = $( openssl rand -base64 32 )
Test authentication
# Signup
curl -X POST http://localhost:8000/api/v1/auth/signup \
-H "Content-Type: application/json" \
-d '{"email":"[email protected] ","password":"password123"}'
# Login
curl -X POST http://localhost:8000/api/v1/auth/login \
-H "Content-Type: application/json" \
-d '{"email":"[email protected] ","password":"password123"}'