Authentication System
In this page:
Core Concept
A typed authentication system models its core entities — a User, a Session, a JwtPayload — as explicit interfaces, so every layer that touches auth data (login handler, middleware, protected route) agrees on exactly what fields are present.
Example: Core Concept
interface User { id: string; email: string; }
interface Session { userId: string; expiresAt: Date; }
interface JwtPayload { userId: string; role: "admin" | "user"; }
const payload: JwtPayload = { userId: "1", role: "user" };
console.log(payload);
Basic Setup
A basic setup types the decoded JWT payload (interface JwtPayload { userId: string; role: admin | user }) so jwt.verify()'s return value is cast to a known, specific shape instead of any.
Example: Basic Setup
interface JwtPayload {
userId: string;
role: "admin" | "user";
}
// const decoded = jwt.verify(token, secret) as JwtPayload;
const decoded: JwtPayload = { userId: "1", role: "admin" };
console.log(decoded);
Typed Example
A typed example: an Express middleware function requireAuth(req: Request, res: Response, next: NextFunction) that attaches req.user: JwtPayload after verifying a token, with a custom Request type augmentation so req.user is recognized everywhere downstream.
Example: Typed Example
interface JwtPayload { userId: string; role: "admin" | "user"; }
function requireAuth(token: string): JwtPayload {
// In a real app: jwt.verify(token, SECRET) as JwtPayload
return { userId: "1", role: "user" };
}
console.log(requireAuth("fake-token"));
Project Usage
In a real project, typing the role field as a literal union (admin | user) rather than a plain string means an authorization check like if (req.user.role === admni) (a typo) is caught at compile time instead of silently always failing.
Example: Project Usage
type Role = "admin" | "user";
function isAdmin(role: Role): boolean {
return role === "admin"; // typo like "admni" would be a compile error
}
console.log(isAdmin("admin"));
Best Practices
Never store sensitive data like a password hash in the typed JWT payload interface — type the payload to hold only what's needed for authorization decisions, keeping the token itself small and non-sensitive.
Example: Best Practices
interface JwtPayload {
userId: string;
role: "admin" | "user";
// No passwordHash here -- keep the token small and non-sensitive.
}
const payload: JwtPayload = { userId: "1", role: "user" };
console.log(payload);
Chapter Quiz — Complete all 14 topics to unlock
0/14 topics done
Complete these topics first:
- Todo App with TypeScript
- REST API with Express + TypeScript
- React Dashboard with TypeScript
- CLI Tool with TypeScript
- Library with TypeScript
- Full Stack TypeScript App
- TypeScript Design System
- TypeScript Monorepo Project
- Authentication System
- Real-time App with Socket.io
- GraphQL API with TypeScript
- Microservices with TypeScript
- TypeScript Best Practices Review
- What to Learn Next