+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
Part 205 of 354

๐Ÿ”’ Authorization: Role-Based Access

Master authorization: role-based access in TypeScript with practical examples, best practices, and real-world applications ๐Ÿš€

๐Ÿš€Intermediate
25 min read

Prerequisites

  • Basic understanding of JavaScript ๐Ÿ“
  • TypeScript installation โšก
  • VS Code or preferred IDE ๐Ÿ’ป
  • Basic Node.js and Express knowledge ๐Ÿ–ฅ๏ธ

What you'll learn

  • Understand role-based authorization fundamentals ๐ŸŽฏ
  • Apply RBAC in real projects ๐Ÿ—๏ธ
  • Debug common authorization issues ๐Ÿ›
  • Write type-safe authorization code โœจ

๐ŸŽฏ Introduction

Welcome to this exciting tutorial on role-based authorization! ๐ŸŽ‰ In this guide, weโ€™ll explore how to build secure, scalable authorization systems that control who can access what in your applications.

Youโ€™ll discover how role-based access control (RBAC) can transform your TypeScript applications from security nightmares into fortress-like systems. Whether youโ€™re building APIs ๐ŸŒ, admin dashboards ๐Ÿ–ฅ๏ธ, or user management systems ๐Ÿ‘ฅ, understanding RBAC is essential for protecting your applicationโ€™s resources.

By the end of this tutorial, youโ€™ll feel confident implementing rock-solid authorization in your own projects! Letโ€™s dive in! ๐ŸŠโ€โ™‚๏ธ

๐Ÿ“š Understanding Role-Based Authorization

๐Ÿค” What is Role-Based Authorization?

Role-based authorization is like a VIP club system ๐ŸŽญ. Think of it as a bouncer at a nightclub who checks your membership card before letting you into different areas. Some people get basic access to the main floor, while others get VIP access to exclusive areas.

In TypeScript terms, RBAC controls what authenticated users can access based on their assigned roles ๐Ÿ›ก๏ธ. This means you can:

  • โœจ Control access to specific resources
  • ๐Ÿš€ Scale permissions across large teams
  • ๐Ÿ›ก๏ธ Maintain security without complexity
  • ๐Ÿ“Š Audit who accessed what and when

๐Ÿ’ก Why Use Role-Based Authorization?

Hereโ€™s why developers love RBAC:

  1. Scalability ๐Ÿ“ˆ: Manage permissions for thousands of users
  2. Maintainability ๐Ÿ”ง: Change role permissions once, affect all users
  3. Security ๐Ÿ”’: Principle of least privilege built-in
  4. Clarity ๐Ÿ‘๏ธ: Clear understanding of who can do what

Real-world example: Imagine building a hospital management system ๐Ÿฅ. With RBAC, doctors can access patient records, nurses can update vital signs, and admin staff can only view billing information.

๐Ÿ”ง Basic Syntax and Usage

๐Ÿ“ Simple Example

Letโ€™s start with a friendly authorization system:

// ๐Ÿ‘‹ Hello, secure TypeScript!
enum Role {
  ADMIN = "admin",     // ๐Ÿ‘‘ Full access
  USER = "user",       // ๐Ÿ‘ค Basic access
  GUEST = "guest"      // ๐Ÿ‘€ View only
}

// ๐ŸŽจ Creating a user type
interface User {
  id: string;
  name: string;
  email: string;
  role: Role;          // ๐ŸŽญ Their assigned role
  isActive: boolean;   // โœ… Account status
}

// ๐Ÿ›ก๏ธ Authorization decorator
function requireRole(allowedRoles: Role[]) {
  return function (target: any, propertyName: string, descriptor: PropertyDescriptor) {
    const method = descriptor.value;
    
    descriptor.value = function (...args: any[]) {
      const user: User = this.currentUser; // ๐Ÿ‘ค Get current user
      
      if (!allowedRoles.includes(user.role)) {
        throw new Error(`๐Ÿšซ Access denied. Required roles: ${allowedRoles.join(', ')}`);
      }
      
      return method.apply(this, args);
    };
  };
}

๐Ÿ’ก Explanation: The requireRole decorator checks if the current user has permission before executing any method!

๐ŸŽฏ Common Authorization Patterns

Here are patterns youโ€™ll use daily:

// ๐Ÿ—๏ธ Pattern 1: Resource-based permissions
interface Permission {
  resource: string;    // ๐Ÿ“‹ What resource (users, posts, etc.)
  action: string;      // โšก What action (read, write, delete)
  conditions?: any;    // ๐ŸŽฏ Optional conditions
}

// ๐ŸŽจ Pattern 2: Role definitions
interface RoleDefinition {
  name: Role;
  permissions: Permission[];
  description: string;
}

// ๐Ÿ”„ Pattern 3: Authorization service
class AuthorizationService {
  private rolePermissions = new Map<Role, Permission[]>();
  
  // ๐ŸŽฏ Check if user can perform action
  canAccess(user: User, resource: string, action: string): boolean {
    const permissions = this.rolePermissions.get(user.role) || [];
    return permissions.some(p => 
      p.resource === resource && p.action === action
    );
  }
}

๐Ÿ’ก Practical Examples

๐Ÿฅ Example 1: Hospital Management System

Letโ€™s build something real and important:

// ๐Ÿฅ Define hospital roles
enum HospitalRole {
  DOCTOR = "doctor",
  NURSE = "nurse", 
  ADMIN = "admin",
  PATIENT = "patient"
}

// ๐Ÿง‘โ€โš•๏ธ Hospital user
interface HospitalUser {
  id: string;
  name: string;
  role: HospitalRole;
  department?: string;  // ๐Ÿข Which department
  licenseNumber?: string; // ๐Ÿ“œ Professional license
}

// ๐Ÿ“‹ Patient record interface
interface PatientRecord {
  id: string;
  patientName: string;
  diagnosis: string;
  medications: string[];
  assignedDoctor: string;
  isConfidential: boolean; // ๐Ÿ”’ Sensitive cases
}

// ๐Ÿ›ก๏ธ Hospital authorization service
class HospitalAuth {
  private currentUser: HospitalUser;
  
  constructor(user: HospitalUser) {
    this.currentUser = user;
  }
  
  // ๐Ÿ‘€ Can read patient records?
  canReadPatientRecord(record: PatientRecord): boolean {
    switch (this.currentUser.role) {
      case HospitalRole.DOCTOR:
        return record.assignedDoctor === this.currentUser.id || !record.isConfidential;
      
      case HospitalRole.NURSE:
        return !record.isConfidential; // ๐Ÿšซ No confidential records
      
      case HospitalRole.ADMIN:
        return true; // ๐Ÿ‘‘ Full access
      
      case HospitalRole.PATIENT:
        return false; // ๐Ÿšซ Patients can't read others' records
      
      default:
        return false;
    }
  }
  
  // โœ๏ธ Can modify patient records?
  canModifyPatientRecord(record: PatientRecord): boolean {
    switch (this.currentUser.role) {
      case HospitalRole.DOCTOR:
        return record.assignedDoctor === this.currentUser.id;
      
      case HospitalRole.NURSE:
        return true; // โœ… Nurses can update vital signs
      
      case HospitalRole.ADMIN:
        return true; // ๐Ÿ‘‘ Full access
      
      default:
        return false; // ๐Ÿšซ Others can't modify
    }
  }
  
  // ๐Ÿ’Š Can prescribe medications?
  canPrescribeMedication(): boolean {
    return this.currentUser.role === HospitalRole.DOCTOR && 
           !!this.currentUser.licenseNumber; // ๐Ÿ“œ Must have license
  }
}

// ๐ŸŽฎ Let's use it!
const drSmith: HospitalUser = {
  id: "doc001",
  name: "Dr. Smith",
  role: HospitalRole.DOCTOR,
  department: "Cardiology",
  licenseNumber: "MD123456"
};

const nurseJones: HospitalUser = {
  id: "nurse001", 
  name: "Nurse Jones",
  role: HospitalRole.NURSE,
  department: "Emergency"
};

const patientRecord: PatientRecord = {
  id: "patient001",
  patientName: "John Doe",
  diagnosis: "Hypertension",
  medications: ["Lisinopril"],
  assignedDoctor: "doc001",
  isConfidential: false
};

// ๐Ÿงช Test authorization
const doctorAuth = new HospitalAuth(drSmith);
const nurseAuth = new HospitalAuth(nurseJones);

console.log("๐Ÿ‘จโ€โš•๏ธ Doctor can read:", doctorAuth.canReadPatientRecord(patientRecord)); // โœ… true
console.log("๐Ÿ‘ฉโ€โš•๏ธ Nurse can read:", nurseAuth.canReadPatientRecord(patientRecord));   // โœ… true
console.log("๐Ÿ’Š Doctor can prescribe:", doctorAuth.canPrescribeMedication());        // โœ… true
console.log("๐Ÿ’Š Nurse can prescribe:", nurseAuth.canPrescribeMedication());          // โŒ false

๐ŸŽฏ Try it yourself: Add a canViewBilling method that only allows admin and billing staff!

๐Ÿช Example 2: E-commerce Admin System

Letโ€™s make it practical for business:

// ๐Ÿ›’ E-commerce roles
enum EcommerceRole {
  SUPER_ADMIN = "super_admin",
  STORE_ADMIN = "store_admin", 
  INVENTORY_MANAGER = "inventory_manager",
  CUSTOMER_SERVICE = "customer_service",
  VIEWER = "viewer"
}

// ๐Ÿ“ฆ Product interface
interface Product {
  id: string;
  name: string;
  price: number;
  stock: number;
  category: string;
  isActive: boolean;
}

// ๐ŸŽญ Permission system
class EcommercePermissions {
  private static permissions = new Map<EcommerceRole, string[]>([
    [EcommerceRole.SUPER_ADMIN, ["*"]], // ๐ŸŒŸ Everything
    [EcommerceRole.STORE_ADMIN, [
      "products:read", "products:write", "products:delete",
      "orders:read", "orders:write", 
      "users:read"
    ]],
    [EcommerceRole.INVENTORY_MANAGER, [
      "products:read", "products:write",
      "inventory:read", "inventory:write"
    ]],
    [EcommerceRole.CUSTOMER_SERVICE, [
      "orders:read", "orders:write",
      "users:read", "returns:write"
    ]],
    [EcommerceRole.VIEWER, ["products:read", "orders:read"]]
  ]);
  
  // ๐Ÿ” Check permission
  static hasPermission(role: EcommerceRole, permission: string): boolean {
    const rolePermissions = this.permissions.get(role) || [];
    
    // ๐ŸŒŸ Super admin has everything
    if (rolePermissions.includes("*")) {
      return true;
    }
    
    return rolePermissions.includes(permission);
  }
  
  // ๐ŸŽฏ Require permission decorator
  static requirePermission(permission: string) {
    return function (target: any, propertyName: string, descriptor: PropertyDescriptor) {
      const method = descriptor.value;
      
      descriptor.value = function (...args: any[]) {
        const user = this.currentUser;
        
        if (!EcommercePermissions.hasPermission(user.role, permission)) {
          throw new Error(`๐Ÿšซ Permission denied: ${permission}`);
        }
        
        return method.apply(this, args);
      };
    };
  }
}

// ๐Ÿช Product service with authorization
class ProductService {
  currentUser: { role: EcommerceRole; name: string };
  
  constructor(user: { role: EcommerceRole; name: string }) {
    this.currentUser = user;
  }
  
  // ๐Ÿ‘€ Read products (most roles can do this)
  @EcommercePermissions.requirePermission("products:read")
  getProducts(): Product[] {
    console.log(`๐Ÿ“‹ ${this.currentUser.name} is viewing products`);
    return [
      { id: "1", name: "TypeScript Book", price: 29.99, stock: 100, category: "books", isActive: true }
    ];
  }
  
  // โœ๏ธ Create product (restricted)
  @EcommercePermissions.requirePermission("products:write")
  createProduct(product: Omit<Product, "id">): Product {
    const newProduct: Product = {
      ...product,
      id: Math.random().toString(36).substr(2, 9)
    };
    console.log(`โœจ ${this.currentUser.name} created product: ${newProduct.name}`);
    return newProduct;
  }
  
  // ๐Ÿ—‘๏ธ Delete product (very restricted)
  @EcommercePermissions.requirePermission("products:delete")
  deleteProduct(productId: string): void {
    console.log(`๐Ÿ—‘๏ธ ${this.currentUser.name} deleted product: ${productId}`);
  }
}

// ๐ŸŽฎ Test different user roles
const superAdmin = { role: EcommerceRole.SUPER_ADMIN, name: "Alice" };
const inventoryManager = { role: EcommerceRole.INVENTORY_MANAGER, name: "Bob" };
const viewer = { role: EcommerceRole.VIEWER, name: "Charlie" };

const adminService = new ProductService(superAdmin);
const inventoryService = new ProductService(inventoryManager);
const viewerService = new ProductService(viewer);

try {
  // โœ… These will work
  adminService.getProducts();
  inventoryService.getProducts();
  viewerService.getProducts();
  
  // โœ… These will work
  adminService.createProduct({ name: "New Book", price: 19.99, stock: 50, category: "books", isActive: true });
  inventoryService.createProduct({ name: "Another Book", price: 24.99, stock: 30, category: "books", isActive: true });
  
  // ๐Ÿšซ This will fail!
  viewerService.createProduct({ name: "Forbidden Book", price: 39.99, stock: 10, category: "books", isActive: true });
} catch (error) {
  console.log("โš ๏ธ Authorization failed:", error.message);
}

๐Ÿš€ Advanced Concepts

๐Ÿง™โ€โ™‚๏ธ Advanced Topic 1: Dynamic Permissions

When youโ€™re ready to level up, try this advanced pattern:

// ๐ŸŽฏ Dynamic permission system
interface DynamicPermission {
  resource: string;
  action: string;
  conditions: (user: any, resource: any) => boolean; // ๐Ÿง  Smart conditions
}

// ๐Ÿช„ Context-aware authorization
class AdvancedAuthService {
  private permissions: DynamicPermission[] = [];
  
  // ๐Ÿ“ Add dynamic permission
  addPermission(permission: DynamicPermission): void {
    this.permissions.push(permission);
  }
  
  // ๐ŸŽฏ Check with context
  canAccess(user: any, action: string, resource: any): boolean {
    return this.permissions.some(permission => 
      permission.action === action &&
      permission.resource === resource.type &&
      permission.conditions(user, resource)
    );
  }
}

// ๐ŸŽฎ Usage example
const advancedAuth = new AdvancedAuthService();

// ๐Ÿ“‹ Users can only edit their own posts
advancedAuth.addPermission({
  resource: "post",
  action: "edit",
  conditions: (user, post) => user.id === post.authorId
});

// ๐Ÿ‘ฅ Managers can edit posts in their department
advancedAuth.addPermission({
  resource: "post", 
  action: "edit",
  conditions: (user, post) => user.role === "manager" && user.department === post.department
});

๐Ÿ—๏ธ Advanced Topic 2: Hierarchical Roles

For the brave developers:

// ๐Ÿš€ Role hierarchy system
class RoleHierarchy {
  private hierarchy = new Map<Role, Role[]>([
    [Role.ADMIN, [Role.USER, Role.GUEST]], // ๐Ÿ‘‘ Admin inherits from user and guest
    [Role.USER, [Role.GUEST]]              // ๐Ÿ‘ค User inherits from guest
  ]);
  
  // ๐Ÿ” Get all inherited roles
  getInheritedRoles(role: Role): Role[] {
    const inherited = this.hierarchy.get(role) || [];
    const allRoles = [role, ...inherited];
    
    // ๐Ÿ”„ Recursively get inherited roles
    inherited.forEach(inheritedRole => {
      const deeper = this.getInheritedRoles(inheritedRole);
      allRoles.push(...deeper.filter(r => !allRoles.includes(r)));
    });
    
    return allRoles;
  }
  
  // โœ… Check if role has permission through inheritance
  hasPermissionThroughInheritance(userRole: Role, requiredRole: Role): boolean {
    const inheritedRoles = this.getInheritedRoles(userRole);
    return inheritedRoles.includes(requiredRole);
  }
}

โš ๏ธ Common Pitfalls and Solutions

๐Ÿ˜ฑ Pitfall 1: Hard-coded Permissions

// โŒ Wrong way - hard-coded everywhere!
function deleteUser(userId: string, currentUser: User) {
  if (currentUser.role !== "admin") { // ๐Ÿ’ฅ Hard-coded!
    throw new Error("Not allowed");
  }
  // Delete logic...
}

// โœ… Correct way - centralized authorization!
class UserService {
  constructor(private auth: AuthorizationService) {}
  
  @requireRole([Role.ADMIN])
  deleteUser(userId: string): void {
    console.log(`๐Ÿ—‘๏ธ Deleted user: ${userId}`);
  }
}

๐Ÿคฏ Pitfall 2: Forgetting to Check Authorization

// โŒ Dangerous - no authorization check!
async function sensitiveOperation(data: any) {
  return await database.deleteAllData(); // ๐Ÿ’ฅ Anyone can do this!
}

// โœ… Safe - always check first!
@requireRole([Role.SUPER_ADMIN])
async function sensitiveOperation(data: any) {
  console.log("๐Ÿ›ก๏ธ Authorization passed, proceeding...");
  return await database.deleteAllData(); // โœ… Only super admins!
}

๐Ÿšจ Pitfall 3: Role Information in Frontend

// โŒ Wrong - trusting the frontend!
// Frontend sends: { role: "admin" }
// Server believes it without verification ๐Ÿ’ฅ

// โœ… Correct - server-side verification!
class SecureUserService {
  async getCurrentUser(token: string): Promise<User> {
    const decoded = jwt.verify(token, SECRET_KEY); // ๐Ÿ”‘ Verify token
    return await database.users.findById(decoded.userId); // ๐Ÿ›ก๏ธ Get from DB
  }
}

๐Ÿ› ๏ธ Best Practices

  1. ๐ŸŽฏ Principle of Least Privilege: Give minimum permissions needed
  2. ๐Ÿ“ Centralized Authorization: Keep all permission logic in one place
  3. ๐Ÿ›ก๏ธ Server-side Verification: Never trust the client
  4. ๐Ÿ”‘ Secure Token Storage: Use HTTPOnly cookies or secure storage
  5. ๐Ÿ“Š Audit Everything: Log all authorization decisions
  6. ๐Ÿ”„ Regular Reviews: Periodically review user permissions
  7. ๐Ÿงช Test Authorization: Write tests for permission edge cases

๐Ÿงช Hands-On Exercise

๐ŸŽฏ Challenge: Build a School Management System

Create a type-safe school administration system:

๐Ÿ“‹ Requirements:

  • ๐ŸŽ“ Students can view their own grades and schedules
  • ๐Ÿ‘จโ€๐Ÿซ Teachers can manage their classes and grade students
  • ๐Ÿ‘‘ Principals can access everything
  • ๐Ÿ“š Librarians can manage library resources
  • ๐Ÿ‘ฅ Parents can view their childโ€™s information
  • ๐Ÿ”’ Sensitive information (disciplinary records) restricted to admin

๐Ÿš€ Bonus Points:

  • Add grade-level restrictions (elementary vs high school)
  • Implement subject-specific teacher permissions
  • Create a parent-student relationship system
  • Add audit logging for sensitive operations

๐Ÿ’ก Solution

๐Ÿ” Click to see solution
// ๐ŸŽฏ Our type-safe school system!
enum SchoolRole {
  STUDENT = "student",
  TEACHER = "teacher", 
  PRINCIPAL = "principal",
  LIBRARIAN = "librarian",
  PARENT = "parent"
}

// ๐Ÿ‘ค School user interface
interface SchoolUser {
  id: string;
  name: string;
  role: SchoolRole;
  gradeLevel?: number;     // ๐Ÿ“š For students (K-12)
  subjects?: string[];     // ๐Ÿ“– For teachers
  childrenIds?: string[];  // ๐Ÿ‘จโ€๐Ÿ‘ฉโ€๐Ÿ‘งโ€๐Ÿ‘ฆ For parents
}

// ๐Ÿ“Š Student record
interface StudentRecord {
  studentId: string;
  grades: { subject: string; grade: string; teacherId: string }[];
  schedule: { subject: string; time: string; teacherId: string }[];
  disciplinaryRecords: string[]; // ๐Ÿ”’ Sensitive
  parentIds: string[];
}

// ๐Ÿ“š Library resource
interface LibraryResource {
  id: string;
  title: string;
  type: "book" | "digital" | "equipment";
  isAvailable: boolean;
  borrowedBy?: string;
}

// ๐Ÿ›ก๏ธ School authorization service
class SchoolAuthService {
  private currentUser: SchoolUser;
  
  constructor(user: SchoolUser) {
    this.currentUser = user;
  }
  
  // ๐Ÿ“Š Can view student grades?
  canViewGrades(studentRecord: StudentRecord): boolean {
    switch (this.currentUser.role) {
      case SchoolRole.STUDENT:
        return studentRecord.studentId === this.currentUser.id;
      
      case SchoolRole.TEACHER:
        // ๐Ÿ‘จโ€๐Ÿซ Can see grades for subjects they teach
        return studentRecord.grades.some(grade => 
          this.currentUser.subjects?.includes(grade.subject)
        );
      
      case SchoolRole.PRINCIPAL:
        return true; // ๐Ÿ‘‘ Full access
      
      case SchoolRole.PARENT:
        return studentRecord.parentIds.includes(this.currentUser.id);
      
      default:
        return false;
    }
  }
  
  // โœ๏ธ Can modify grades?
  canModifyGrades(studentRecord: StudentRecord, subject: string): boolean {
    switch (this.currentUser.role) {
      case SchoolRole.TEACHER:
        return this.currentUser.subjects?.includes(subject) ?? false;
      
      case SchoolRole.PRINCIPAL:
        return true; // ๐Ÿ‘‘ Can override any grade
      
      default:
        return false;
    }
  }
  
  // ๐Ÿ”’ Can view disciplinary records?
  canViewDisciplinaryRecords(studentRecord: StudentRecord): boolean {
    switch (this.currentUser.role) {
      case SchoolRole.PRINCIPAL:
        return true; // ๐Ÿ‘‘ Full access to sensitive records
      
      case SchoolRole.TEACHER:
        // ๐Ÿค” Only if they teach the student
        return studentRecord.grades.some(grade => 
          this.currentUser.subjects?.includes(grade.subject)
        );
      
      default:
        return false; // ๐Ÿšซ Sensitive information
    }
  }
  
  // ๐Ÿ“š Can manage library resources?
  canManageLibrary(): boolean {
    return this.currentUser.role === SchoolRole.LIBRARIAN || 
           this.currentUser.role === SchoolRole.PRINCIPAL;
  }
  
  // ๐Ÿ“– Can borrow books?
  canBorrowBooks(): boolean {
    return [SchoolRole.STUDENT, SchoolRole.TEACHER].includes(this.currentUser.role);
  }
}

// ๐ŸŽ“ School management service
class SchoolService {
  constructor(private auth: SchoolAuthService) {}
  
  // ๐Ÿ“Š Get student grades
  getGrades(studentId: string): any {
    const studentRecord: StudentRecord = {
      studentId,
      grades: [
        { subject: "Math", grade: "A", teacherId: "teacher001" },
        { subject: "Science", grade: "B+", teacherId: "teacher002" }
      ],
      schedule: [],
      disciplinaryRecords: ["Late to class on 2024-01-15"],
      parentIds: ["parent001"]
    };
    
    if (!this.auth.canViewGrades(studentRecord)) {
      throw new Error("๐Ÿšซ Access denied: Cannot view grades");
    }
    
    return studentRecord.grades;
  }
  
  // โœ๏ธ Update grade
  updateGrade(studentId: string, subject: string, newGrade: string): void {
    const studentRecord: StudentRecord = {
      studentId,
      grades: [{ subject, grade: "B", teacherId: "teacher001" }],
      schedule: [],
      disciplinaryRecords: [],
      parentIds: []
    };
    
    if (!this.auth.canModifyGrades(studentRecord, subject)) {
      throw new Error(`๐Ÿšซ Access denied: Cannot modify ${subject} grades`);
    }
    
    console.log(`โœ… Updated ${subject} grade to ${newGrade} for student ${studentId}`);
  }
  
  // ๐Ÿ“š Borrow book
  borrowBook(bookId: string): void {
    if (!this.auth.canBorrowBooks()) {
      throw new Error("๐Ÿšซ Access denied: Cannot borrow books");
    }
    
    console.log(`๐Ÿ“– Book ${bookId} borrowed successfully`);
  }
}

// ๐ŸŽฎ Test the system!
const student: SchoolUser = {
  id: "student001",
  name: "Emma Smith",
  role: SchoolRole.STUDENT,
  gradeLevel: 10
};

const teacher: SchoolUser = {
  id: "teacher001", 
  name: "Mr. Johnson",
  role: SchoolRole.TEACHER,
  subjects: ["Math", "Physics"]
};

const parent: SchoolUser = {
  id: "parent001",
  name: "Mrs. Smith",
  role: SchoolRole.PARENT,
  childrenIds: ["student001"]
};

// ๐Ÿงช Test different permissions
const studentAuth = new SchoolAuthService(student);
const teacherAuth = new SchoolAuthService(teacher);
const parentAuth = new SchoolAuthService(parent);

const studentService = new SchoolService(studentAuth);
const teacherService = new SchoolService(teacherAuth);
const parentService = new SchoolService(parentAuth);

try {
  // โœ… These should work
  console.log("๐Ÿ“Š Student grades:", studentService.getGrades("student001"));
  teacherService.updateGrade("student001", "Math", "A+");
  studentService.borrowBook("book123");
  
  // ๐Ÿšซ This should fail
  studentService.updateGrade("student001", "Math", "A+");
} catch (error) {
  console.log("โš ๏ธ Authorization failed:", error.message);
}

๐ŸŽ“ Key Takeaways

Youโ€™ve learned so much! Hereโ€™s what you can now do:

  • โœ… Create role-based authorization with confidence ๐Ÿ’ช
  • โœ… Avoid common security mistakes that trip up beginners ๐Ÿ›ก๏ธ
  • โœ… Apply RBAC patterns in real projects ๐ŸŽฏ
  • โœ… Debug authorization issues like a pro ๐Ÿ›
  • โœ… Build secure applications with TypeScript! ๐Ÿš€

Remember: Security is not a feature you add later - itโ€™s a foundation you build on! ๐Ÿ—๏ธ

๐Ÿค Next Steps

Congratulations! ๐ŸŽ‰ Youโ€™ve mastered role-based authorization!

Hereโ€™s what to do next:

  1. ๐Ÿ’ป Practice with the school management exercise above
  2. ๐Ÿ—๏ธ Build a small project using RBAC (try a blog with admin/author/reader roles)
  3. ๐Ÿ“š Move on to our next tutorial: JWT Authentication Implementation
  4. ๐ŸŒŸ Share your secure applications with others!

Remember: Every security expert was once a beginner. Keep coding, keep learning, and most importantly, keep your applications secure! ๐Ÿš€


Happy coding and stay secure! ๐ŸŽ‰๐Ÿ”’โœจ