I'm Samuel FajreldinesI am a specialist in the entire JavaScript and TypeScript ecosystem (including Node.js, React, Angular and Vue.js) I am expert in AI and in creating AI integrated solutions I am expert in DevOps and Serverless Architecture (AWS, Google Cloud and Azure) I am expert in PHP and its frameworks (such as Codeigniter and Laravel). |
Samuel FajreldinesI am a specialist in the entire JavaScript and TypeScript ecosystem. I am expert in AI and in creating AI integrated solutions. I am expert in DevOps and Serverless Architecture I am expert in PHP and its frameworks.
|
In today's web development landscape, security is paramount. Implementing robust authentication mechanisms is essential to protect user data and ensure secure communication between clients and servers. One of the most popular methods for securing APIs is using JSON Web Tokens (JWT). In this comprehensive guide, we'll explore what JWT authentication is and how to implement it in TypeScript, making your web applications both secure and scalable.
JSON Web Token (JWT) is an open standard (RFC 7519) that defines a compact and self-contained way for securely transmitting information between parties as a JSON object. This information can be verified and trusted because it is digitally signed, typically using a secret or a public/private key pair.
Authorization
header.Let's dive into the step-by-step process of implementing JWT authentication in a Node.js application using TypeScript.
Initialize the Project
mkdir jwt-auth-typescript
cd jwt-auth-typescript
npm init -y
Install Dependencies
npm install express jsonwebtoken bcryptjs cors dotenv
Install TypeScript and Types
npm install --save-dev typescript @types/express @types/jsonwebtoken @types/bcryptjs @types/cors
Initialize TypeScript Configuration
npx tsc --init
jwt-auth-typescript/
├── src/
│ ├── index.ts
│ ├── routes/
│ │ └── auth.ts
│ ├── controllers/
│ │ └── authController.ts
│ ├── models/
│ └── user.ts
├── package.json
├── tsconfig.json
└── .env
tsconfig.json
)Make sure your tsconfig.json
includes:
{
"compilerOptions": {
"target": "ES6",
"module": "commonjs",
"outDir": "./dist",
"rootDir": "./src",
"strict": true
}
}
Create a .env
file in the root directory:
PORT=5000
JWT_SECRET=your_jwt_secret_key
index.ts
)import express from 'express';
import cors from 'cors';
import dotenv from 'dotenv';
import authRoutes from './routes/auth';
dotenv.config();
const app = express();
const PORT = process.env.PORT || 5000;
app.use(cors());
app.use(express.json());
app.use('/api/auth', authRoutes);
app.listen(PORT, () => {
console.log(`Server running on port ${PORT}`);
});
models/user.ts
)In a real application, you'd use a database. For simplicity, we'll use an in-memory array.
interface User {
id: number;
username: string;
password: string;
}
const users: User[] = [];
export default users;
export type { User };
routes/auth.ts
)import { Router } from 'express';
import { register, login, protectedRoute } from '../controllers/authController';
const router = Router();
router.post('/register', register);
router.post('/login', login);
router.get('/protected', protectedRoute);
export default router;
controllers/authController.ts
)import { Request, Response, NextFunction } from 'express';
import jwt from 'jsonwebtoken';
import bcrypt from 'bcryptjs';
import users, { User } from '../models/user';
const JWT_SECRET = process.env.JWT_SECRET || 'secret_key';
export const register = async (req: Request, res: Response) => {
const { username, password } = req.body;
// Check if user exists
const userExists = users.find((user) => user.username === username);
if (userExists) {
return res.status(400).json({ message: 'User already exists' });
}
// Hash password
const hashedPassword = await bcrypt.hash(password, 10);
// Create user
const newUser: User = {
id: users.length + 1,
username,
password: hashedPassword,
};
users.push(newUser);
res.status(201).json({ message: 'User registered successfully' });
};
export const login = async (req: Request, res: Response) => {
const { username, password } = req.body;
// Find user
const user = users.find((user) => user.username === username);
if (!user) {
return res.status(400).json({ message: 'Invalid credentials' });
}
// Check password
const isValid = await bcrypt.compare(password, user.password);
if (!isValid) {
return res.status(400).json({ message: 'Invalid credentials' });
}
// Sign token
const token = jwt.sign({ id: user.id, username: user.username }, JWT_SECRET, { expiresIn: '1h' });
res.json({ token });
};
export const protectedRoute = (req: Request, res: Response, next: NextFunction) => {
const authHeader = req.headers['authorization'];
const token = authHeader && authHeader.split(' ')[1];
if (!token) {
return res.sendStatus(401);
}
jwt.verify(token, JWT_SECRET, (err, user) => {
if (err) return res.sendStatus(403);
(req as any).user = user;
next();
});
};
Modify the protectedRoute
function to include a middleware that verifies the JWT before proceeding.
export const protectedRoute = [
(req: Request, res: Response, next: NextFunction) => {
// Middleware to verify JWT
const authHeader = req.headers['authorization'];
const token = authHeader && authHeader.split(' ')[1];
if (!token) {
return res.sendStatus(401);
}
jwt.verify(token, JWT_SECRET, (err, user) => {
if (err) return res.sendStatus(403);
(req as any).user = user;
next();
});
},
(req: Request, res: Response) => {
// Protected route logic
res.json({ message: 'This is a protected route', user: (req as any).user });
}
];
Compile TypeScript
npx tsc
Run the Server
node dist/index.js
Register a User
POST http://localhost:5000/api/auth/register
Content-Type: application/json
{
"username": "testuser",
"password": "password123"
}
Login
POST http://localhost:5000/api/auth/login
Content-Type: application/json
{
"username": "testuser",
"password": "password123"
}
You will receive a JWT token in the response.
Access Protected Route
GET http://localhost:5000/api/auth/protected
Authorization: Bearer <your_jwt_token>
You should receive a response indicating access is granted.
Implementing JWT authentication in TypeScript is a powerful way to secure your web applications. It offers a scalable and stateless approach to handle authentication, making it ideal for modern web architectures. By following this guide, you should have a solid foundation to integrate JWT authentication into your TypeScript projects.
If you found this guide helpful or have any questions, feel free to leave a comment below or reach out to me on Twitter.
By Samuel Fajreldines, specialist in JavaScript, TypeScript, DevOps, and Serverless Architecture.
About Me
Since I was a child, I've always wanted to be an inventor. As I grew up, I specialized in information systems, an area which I fell in love with and live around it. I am a full-stack developer and work a lot with devops, i.e., I'm a kind of "jack-of-all-trades" in IT. Wherever there is something cool or new, you'll find me exploring and learning... I am passionate about life, family, and sports. I believe that true happiness can only be achieved by balancing these pillars. I am always looking for new challenges and learning opportunities, and would love to connect with other technology professionals to explore possibilities for collaboration. If you are looking for a dedicated and committed full-stack developer with a passion for excellence, please feel free to contact me. It would be a pleasure to talk with you! |
SecurityScoreCard
Nov. 2023 - Present
New York, United States
Senior Software Engineer
I joined SecurityScorecard, a leading organization with over 400 employees, as a Senior Full Stack Software Engineer. My role spans across developing new systems, maintaining and refactoring legacy solutions, and ensuring they meet the company's high standards of performance, scalability, and reliability.
I work across the entire stack, contributing to both frontend and backend development while also collaborating directly on infrastructure-related tasks, leveraging cloud computing technologies to optimize and scale our systems. This broad scope of responsibilities allows me to ensure seamless integration between user-facing applications and underlying systems architecture.
Additionally, I collaborate closely with diverse teams across the organization, aligning technical implementation with strategic business objectives. Through my work, I aim to deliver innovative and robust solutions that enhance SecurityScorecard's offerings and support its mission to provide world-class cybersecurity insights.
Technologies Used:
Node.js Terraform React Typescript AWS Playwright and Cypress