Skip to main content

Overview

OPS Workspace uses a role-based access control (RBAC) system to manage permissions. Each user is assigned a role that determines which applications they can access and which features are available to them.

Role Hierarchy

There are three roles in the system, each with different permission levels:

Supervisor

Highest privilegesFull access to all features including user management

Analista

Standard accessAccess to cashout operations and Operapedia

Chats

Limited accessAccess only to Operapedia for customer support

Permission Matrix

Application Access

ApplicationSupervisorAnalistaChats
Cashouts
Operapedia

Cashouts Features

FeatureSupervisorAnalistaChats
Submit cashouts
Review cashouts
View cashout rules
Edit cashout rules
Create companies
View leaderboard
View statistics
Manage team
Create users
Delete users
Change passwords
The “Manage Team” button is automatically hidden from analysts and chat agents based on their role.

How Permissions Work

Authentication Flow

1

User Login

User enters username and password at the OPS Workspace hub
2

Token Generation

Server validates credentials and generates a JWT token containing user data:
{
  "fullName": "Juan Perez",
  "username": "juan.perez",
  "role": "analista"
}
3

Role Storage

Token and user data are stored in localStorage:
localStorage.setItem('token', data.token);
localStorage.setItem('user', JSON.stringify(data.user));
4

UI Rendering

Application reads the role and shows/hides features accordingly

Role-Based UI Restrictions

The system enforces permissions at the UI level by conditionally rendering elements based on the user’s role:
// Initialize app with user role
function iniciarApp(jwtToken, userData) {
  token = jwtToken;
  currentUser = userData;
  
  // Show Manage Team button only for supervisors
  if (currentUser.role === 'supervisor') {
    manageTeamBtn.style.display = 'block';
  } else {
    manageTeamBtn.style.display = 'none';
  }
}

Application Visibility

At the hub level, applications are hidden based on role:
// Hide Cashouts app for chat agents
if (user.role === 'chats') {
  document.getElementById('card-cashouts').style.display = 'none';
}
Chat agents (chats role) cannot see or access the Cashouts application at all. It is completely hidden from the dashboard.

Role Indicators

Visual Badge System

Each role has a distinct color scheme for easy identification:

Supervisor Badge

background: rgba(94, 106, 210, 0.15);
color: #6f7bf0;
border: 1px solid rgba(94, 106, 210, 0.3);
SUPERVISOR

Analista Badge

background: rgba(46, 204, 113, 0.15);
color: #2ecc71;
border: 1px solid rgba(46, 204, 113, 0.3);
ANALISTA

Chats Badge

background: rgba(243, 156, 18, 0.15);
color: #f39c12;
border: 1px solid rgba(243, 156, 18, 0.3);
CHATS

Server-Side Authorization

JWT Token Validation

All API requests include the JWT token in the Authorization header:
async function apiFetch(endpoint, options = {}) {
  const headers = { 
    'Content-Type': 'application/json', 
    ...options.headers 
  };
  
  if (token) {
    headers['Authorization'] = `Bearer ${token}`;
  }
  
  const response = await fetch(`${API_BASE}${endpoint}`, { 
    ...options, 
    headers 
  });
  
  // Automatic session termination on auth failure
  if (response.status === 401 || response.status === 403) {
    cerrarSesion();
    throw new Error("Sesión expirada o acceso denegado");
  }
  
  return response;
}

Protected Endpoints

Certain endpoints require specific roles:
EndpointRequired RolePurpose
POST /api/userssupervisorCreate new user
DELETE /api/users/:idsupervisorDelete user
PUT /api/users/:id/passwordsupervisorChange user password
PUT /api/rules/:idsupervisorEdit cashout rules
POST /api/rulessupervisorCreate new company
If a user attempts to access a protected endpoint without proper permissions, they receive a 401 Unauthorized or 403 Forbidden response and are immediately logged out.

Auto-Authentication Features

Operator Name Lock

When submitting cashouts, the operator name field is automatically populated and locked to prevent impersonation:
// Auto-populate operator name from logged-in user
const operatorNameInput = document.getElementById('operatorName');
if (operatorNameInput) {
  operatorNameInput.value = currentUser.fullName;
  operatorNameInput.readOnly = true; // Prevent manual changes
}

Supervisor Attribution

When reviewing cashouts, the supervisor’s name is automatically attached:
await apiFetch(`/cashouts/${encodeURIComponent(revisionPendiente.row)}`, {
  method: "PATCH",
  body: JSON.stringify({
    status: revisionPendiente.estado,
    supervisorName: currentUser.fullName, // Auto-filled from token
    observacion: observacionFinal
  })
});
Users cannot impersonate other users because names are pulled directly from the authenticated JWT token, not from user input.

Session Management

Automatic Logout Scenarios

  1. Token Expiration: JWT tokens expire after a set period
  2. Invalid Token: Tampered or corrupted tokens trigger logout
  3. Permission Denied: Accessing restricted resources logs out the user
  4. Manual Logout: User clicks “Cerrar Sesión” button

Logout Process

function cerrarSesion() {
  localStorage.removeItem('token');
  localStorage.removeItem('user');
  window.location.href = '/'; // Redirect to hub
}
All logout scenarios redirect users back to the login screen at the OPS Workspace hub.

Permission Code Examples

Role-Based Feature Toggle

// Example: Show/hide company creation form
const isSupervisor = currentUser.role === 'supervisor';
if (newCompanyContainer) {
  newCompanyContainer.style.display = isSupervisor ? 'block' : 'none';
}

Role-Based Button Rendering

// Example: Edit button only for supervisors
${isSupervisor ? 
  `<button onclick="toggleEditRegla('${rule._id}')">✏️ Editar</button>` 
  : 
  ''
}

Admin Protection

// Example: Disable delete for admin user
<button 
  onclick="eliminarUsuario('${u._id}')" 
  ${u.username === 'admin' ? 'disabled' : ''}
>
  🗑️ Borrar
</button>

Security Best Practices

Always validate permissions on the server. Client-side restrictions are for UX only and can be bypassed by malicious users.
  • Never expose the JWT secret
  • Set appropriate expiration times
  • Validate tokens on every request
  • Include user role in the token payload
Log out users immediately when:
  • Token expires
  • Token is invalid
  • User accesses restricted resources
  • Multiple failed authorization attempts
  • Users cannot change their own role
  • Only supervisors can assign roles
  • Admin user cannot be deleted or demoted

Troubleshooting Permissions

  • Check the user’s role in the database
  • Verify the JWT token contains the correct role
  • Clear localStorage and re-login
  • Check for role assignment errors during user creation
  • Verify the user has a valid token in localStorage
  • Check if the token has expired
  • Confirm the user’s role has permission for the endpoint
  • Check server logs for authorization failures
  • Verify currentUser.role === 'supervisor' in console
  • Check if iniciarApp() function ran successfully
  • Inspect manageTeamBtn.style.display in DevTools
  • Confirm user was created with supervisor role (not Supervisor or SUPERVISOR)

Team Management

Learn how to create and manage user accounts

Authentication

Understand the login and token system

Build docs developers (and LLMs) love