← Back to TypeScript Course | Chapter 28: Real World Projects | Lesson 9 of 14

Authentication System

TypeScript can model authentication requests, user identities, sessions, and authorization decisions. Strong types help separate public user data from sensitive credentials.

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

typescript
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

typescript
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

typescript
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

typescript
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

typescript
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);

Login to run this code

C/C++/Java/PHP execution requires a free account. Your code is saved — you'll land right back in the editor after logging in.