Skip to main content

Overview

The Medical Appointments API implements a role-based access control (RBAC) system with three distinct user roles. Each role has specific permissions that control access to different endpoints and operations within the system.

Role Types

The system defines three roles in the database schema:
ADMIN
enum
Administrative users with full system access. Admins can manage users, view audit logs, and perform system-wide operations.
DOCTOR
enum
Medical professionals who create time blocks and manage their appointments. Doctors can view their schedules, create availability slots, and manage patient appointments.
PATIENT
enum
Default role for newly registered users. Patients can book appointments, view their appointment history, and manage their reservations.

Role Assignment

Roles are assigned during user registration and stored in the User model:
model User {
  id       Int     @id @default(autoincrement())
  name     String
  email    String  @unique
  password String
  role     Role    // ADMIN, DOCTOR, or PATIENT
  // ...
}

enum Role {
  ADMIN
  PATIENT
  DOCTOR
}

Default Role

All new users registering through the standard registration endpoint are automatically assigned the PATIENT role. This is configured in the authentication service at /home/daytona/workspace/source/src/services/authService.js:22.

Role-Based Permissions

Admin Permissions

Admins have unrestricted access to:
  • View all audit logs
  • Manage user accounts (suspend, activate, delete)
  • Access system-wide reports and data
  • Perform administrative operations

Doctor Permissions

Doctors can:
  • Create, update, and delete their own time blocks
  • View appointments assigned to them
  • Manage their availability schedule
  • Cannot modify time blocks created by other doctors

Patient Permissions

Patients can:
  • Create appointment reservations
  • View their own appointments
  • Update or cancel their reservations
  • Browse available time blocks from doctors

Authorization Middleware

The system enforces role-based access control using the authorizeRole middleware:
module.exports = function authorizeRole(roles = []) {
    return (req, res, next) => {
        if (!roles.includes(req.user.role)) {
            return res.status(403).json({ error: 'Access denied: insufficient role' });
        }
        next();
    };
};

Usage Example

To protect an endpoint so only admins can access it:
router.get('/audit-logs', 
  authenticate, 
  authorizeRole(['ADMIN']), 
  getAuditLogsController
);
To allow both doctors and admins:
router.post('/time-blocks', 
  authenticate, 
  authorizeRole(['DOCTOR', 'ADMIN']), 
  createTimeBlock
);
The role is extracted from the JWT token during authentication and attached to req.user.role. The middleware checks if the user’s role is included in the allowed roles array.

Role in JWT Token

When users log in, their role is encoded in the JWT token:
const token = jwt.sign(
  { id: user.id, role: user.role },
  process.env.JWT_SECRET,
  { expiresIn: '1h' }
);
This allows the API to verify user permissions without querying the database on every request.

Best Practices

  • Always use the authorizeRole middleware after the authenticate middleware
  • Specify the minimum required role for each endpoint
  • Never hardcode roles in business logic—use the middleware for consistency
  • Admins should change default patient roles to doctor roles when onboarding medical staff

Build docs developers (and LLMs) love