Introduction to OAuth 2.0 and JWT in TypeScript
\n\nIn modern web development, secure user authorization is the foundation of any application. The two most popular standards — OAuth 2.0 and JWT (JSON Web Token) — allow you to implement a reliable authentication and access control system. In this article, we will break down how to combine these technologies in TypeScript to create a secure API from scratch.
\n\nThis article is intended for beginner developers who are already familiar with the basics of TypeScript and want to understand how authorization works in practice. We will not delve into complex theoretical explanations, but will focus on specific code examples.
\n\nWhat is OAuth 2.0?
\nOAuth 2.0 is an authorization protocol that allows third-party applications to obtain limited access to a user's resources without sharing their password. Imagine you want to log into a website using Google: you don't give the website your Google password, you simply allow it to get your email and name. That is OAuth 2.0.
\n\nWhat is JWT?
\nJWT (JSON Web Token) is a compact and self-contained token format that contains all the necessary information about the user in an encrypted form. The token consists of three parts: header, payload, and signature. Thanks to the signature, the server can be sure that the token has not been tampered with.
\n\n1. Project Setup and Dependency Installation
\n\nFirst, let's create a new TypeScript project. Open your terminal and run the following commands:
\n\nmkdir oauth-jwt-ts\ncd oauth-jwt-ts\nnpm init -y\nnpm install express jsonwebtoken bcrypt dotenv cors\nnpm install -D typescript ts-node @types/express @types/jsonwebtoken @types/bcrypt @types/cors @types/node nodemon\n\nCreate a tsconfig.json file with a basic configuration:
{\n "compilerOptions": {\n "target": "ES2020",\n "module": "commonjs",\n "outDir": "./dist",\n "rootDir": "./src",\n "strict": true,\n "esModuleInterop": true,\n "skipLibCheck": true,\n "forceConsistentCasingInFileNames": true\n },\n "include": ["src/**/*"],\n "exclude": ["node_modules"]\n}\n\nThe folder structure will look like this:
\n- \n
src/— source code \n src/controllers/— controllers \n src/middleware/— middleware for token verification \n src/models/— data models \n src/routes/— routes \n .env— environment variables \n
2. Implementing Basic Authentication with JWT
\n\nLet's start by creating a simple registration and login mechanism using JWT. Create a file src/config.ts to store constants:
import dotenv from 'dotenv';\ndotenv.config();\n\nexport const config = {\n jwtSecret: process.env.JWT_SECRET || 'your-secret-key',\n jwtExpiresIn: process.env.JWT_EXPIRES_IN || '15m',\n refreshTokenExpiresIn: process.env.REFRESH_TOKEN_EXPIRES_IN || '7d',\n port: process.env.PORT || 3000\n};\n\nNow let's create middleware for verifying the JWT token. File src/middleware/auth.middleware.ts:
import { Request, Response, NextFunction } from 'express';\nimport jwt from 'jsonwebtoken';\nimport { config } from '../config';\n\nexport interface AuthRequest extends Request {\n user?: { userId: string; email: string };\n}\n\nexport const authenticateToken = (req: AuthRequest, res: Response, next: NextFunction) => {\n const authHeader = req.headers['authorization'];\n const token = authHeader && authHeader.split(' ')[1]; // Bearer TOKEN\n\n if (!token) {\n return res.status(401).json({ message: 'Token not provided' });\n }\n\n try {\n const decoded = jwt.verify(token, config.jwtSecret) as { userId: string; email: string };\n req.user = decoded;\n next