Overview
Grupo Mecsa CMS uses Supabase Auth for user authentication, providing secure login, password management, and session handling. The authentication system integrates with the employee database to enforce role-based access control.
Authentication Flow
User enters credentials
The user provides their email and password on the login page.
Supabase authentication
Credentials are verified against Supabase Auth using the login() method.
Employee data validation
System fetches employee record from Empleados table to verify CMS access.
Permission check
Validates that user has ‘CMS’ in sistemas_acceso array or admin role.
Session creation
Session variables are set with tokens, user info, and permissions.
Login Implementation
The login process is handled in login.php using the Supabase class.
Basic Login Flow
$login = $supa -> login ( $email , $pass );
if ( isset ( $login [ 'access_token' ])) {
// Store authentication tokens
$_SESSION [ 'token' ] = $login [ 'access_token' ];
$_SESSION [ 'refresh_token' ] = $login [ 'refresh_token' ];
// Calculate token expiry
if ( isset ( $login [ 'expires_in' ])) {
$_SESSION [ 'token_expires_at' ] = time () + intval ( $login [ 'expires_in' ]);
}
// Store user identification
$_SESSION [ 'email' ] = $email ;
$_SESSION [ 'uid' ] = $login [ 'user' ][ 'id' ] ?? null ;
}
Supabase Login Method
The Supabase class provides the authentication interface:
public function login ( $email , $password ) {
$url = rtrim ( $this -> url , '/' ) . '/auth/v1/token?grant_type=password' ;
$res = $this -> _execute_auth_request ( $url , 'POST' , [
'email' => $email ,
'password' => $password
], [
"apikey: " . $this -> apiKey ,
"Content-Type: application/json"
]);
return json_decode ( $res [ 'body' ], true );
}
Employee Data Validation
After successful authentication, the system validates the employee record:
$resEmp = supabase_request ( 'GET' ,
"Empleados?id_user=eq." . $login [ 'user' ][ 'id' ] .
"&select=rol,chat_role,nombre,sistemas_acceso,activo" ,
null ,
[ 'Accept-Profile: public' ]
);
if ( $resEmp [ 'http' ] === 200 && ! empty ( $resEmp [ 'json' ])) {
$uData = $resEmp [ 'json' ][ 0 ];
$sistemas = $uData [ 'sistemas_acceso' ] ?? [];
if ( ! in_array ( 'CMS' , $sistemas ) && $email !== 'emmanuel.jarquin@grupomecsa.net' ) {
$error = "No tienes permisos de acceso para GrupoMecsaCMS." ;
session_destroy ();
} else {
$_SESSION [ 'rol' ] = $uData [ 'rol' ] ?? '' ;
$_SESSION [ 'chat_role' ] = $uData [ 'chat_role' ] ?? '' ;
$_SESSION [ 'nombre' ] = $uData [ 'nombre' ] ?? '' ;
}
}
The system checks if ‘CMS’ is present in the sistemas_acceso array. Users without this permission are denied access unless they’re the emergency admin.
Access Control Verification
The final security check ensures only authorized staff can access:
$isStaff = false ;
if ( $email === 'emmanuel.jarquin@grupomecsa.net' ) {
$isStaff = true ;
} elseif ( $fetchedUser && ! empty ( $fetchedUser [ 'activo' ])) {
$sistemas = $fetchedUser [ 'sistemas_acceso' ] ?? [];
if ( is_string ( $sistemas )) {
$sistemas = json_decode ( $sistemas , true ) ?: [];
}
$hasCmsAccess = ( is_array ( $sistemas ) &&
( in_array ( 'cms' , $sistemas ) || in_array ( 'CMS' , $sistemas )));
$isAdminRole = ( isset ( $fetchedUser [ 'rol' ]) &&
( strtolower ( $fetchedUser [ 'rol' ]) === 'administrador' ||
strtolower ( $fetchedUser [ 'rol' ]) === 'admin' ));
if ( $hasCmsAccess || $isAdminRole ) {
$isStaff = true ;
}
}
if ( ! $isStaff ) {
session_destroy ();
$error = "Acceso denegado. Esta plataforma es exclusiva para personal autorizado." ;
}
Session Management
Session Variables
The following session variables are set upon successful login:
Variable Description $_SESSION['token']JWT access token from Supabase $_SESSION['refresh_token']Token for refreshing expired sessions $_SESSION['token_expires_at']Unix timestamp of token expiration $_SESSION['email']User’s email address $_SESSION['uid']Supabase user ID $_SESSION['rol']User’s role (admin, comercial, etc.) $_SESSION['chat_role']Chat system role $_SESSION['nombre']User’s full name $_SESSION['user']Complete employee record
Session Protection
All protected pages check for active sessions:
session_start ();
if ( ! isset ( $_SESSION [ 'token' ])) {
header ( "Location: ../login.php" );
exit ;
}
The system automatically destroys sessions when:
User lacks CMS access permissions
Employee is marked as inactive (activo = false)
Authentication fails or token is invalid
Password Reset Process
Users can reset forgotten passwords through a secure recovery flow.
User requests password reset
User enters their email on the login page modal.
Recovery link generation
System generates a secure recovery link using Supabase Admin API.
Email delivery
Recovery email is sent with branded template and action link.
Password update
User clicks link, enters new password on restablecer.php.
Recovery Link Generation
$email = trim ( $_POST [ 'recover_email' ]);
$protocol = ( isset ( $_SERVER [ 'HTTPS' ]) && $_SERVER [ 'HTTPS' ] === 'on' ) ? 'https' : 'http' ;
if ( isset ( $_SERVER [ 'HTTP_X_FORWARDED_PROTO' ])) {
$protocol = $_SERVER [ 'HTTP_X_FORWARDED_PROTO' ];
}
$host = $_SERVER [ 'HTTP_HOST' ];
$request_uri = $_SERVER [ 'REQUEST_URI' ];
$base_path = rtrim ( dirname ( $request_uri ), '/ \\ ' );
$redirectTo = " $protocol :// $host$base_path /restablecer.php" ;
$actionLink = $supa -> generateRecoveryLink ( $email , $redirectTo );
Generate Recovery Link (Supabase Class)
public function generateRecoveryLink ( $email , $redirectTo = '' ) {
global $supabase_service_role ;
$url = rtrim ( $this -> url , '/' ) . '/auth/v1/admin/generate_link' ;
$data = [ 'type' => 'recovery' , 'email' => $email ];
if ( ! empty ( $redirectTo )) {
$data [ 'redirectTo' ] = $redirectTo ;
$data [ 'redirect_to' ] = $redirectTo ;
$data [ 'options' ] = [ 'redirectTo' => $redirectTo , 'redirect_to' => $redirectTo ];
}
$res = $this -> _execute_auth_request ( $url , 'POST' , $data , [
"apikey: " . ( $supabase_service_role ?? $this -> apiKey ),
"Authorization: Bearer " . ( $supabase_service_role ?? $this -> apiKey ),
"Content-Type: application/json"
]);
$result = json_decode ( $res [ 'body' ], true );
if ( $res [ 'http' ] === 200 && isset ( $result [ 'action_link' ])) {
return $result [ 'action_link' ];
}
throw new Exception ( $result [ 'error_description' ] ?? 'Admin operation failed' );
}
The generateRecoveryLink() method requires Supabase Service Role Key for admin operations. This key should be stored securely in local.supabase.php.
Password Reset Page
The restablecer.php page handles the actual password update:
if ( $_SERVER [ 'REQUEST_METHOD' ] === 'POST' ) {
$new_pass = $_POST [ 'new_password' ] ?? '' ;
$confirm_pass = $_POST [ 'confirm_password' ] ?? '' ;
$token = $_POST [ 'access_token' ] ?? $tokenFromGet ?? $_SESSION [ 'token' ] ?? '' ;
if ( strlen ( $new_pass ) < 6 ) {
$error = "La contraseña debe tener al menos 6 caracteres." ;
} elseif ( $new_pass !== $confirm_pass ) {
$error = "Las contraseñas no coinciden." ;
} elseif ( empty ( $token )) {
$error = "Sesión inválida o expirada." ;
} else {
$url = $supabase_url . '/auth/v1/user' ;
$payload = [ 'password' => $new_pass ];
$ch = curl_init ( $url );
curl_setopt ( $ch , CURLOPT_RETURNTRANSFER , true );
curl_setopt ( $ch , CURLOPT_CUSTOMREQUEST , 'PUT' );
curl_setopt ( $ch , CURLOPT_POSTFIELDS , json_encode ( $payload ));
curl_setopt ( $ch , CURLOPT_HTTPHEADER , [
'Content-Type: application/json' ,
'apikey: ' . $supabase_key ,
'Authorization: Bearer ' . $token
]);
$response = curl_exec ( $ch );
$httpCode = curl_getinfo ( $ch , CURLINFO_HTTP_CODE );
curl_close ( $ch );
if ( $httpCode === 200 ) {
$success = "¡Contraseña actualizada con éxito!" ;
header ( "refresh:2;url=login.php" );
} else {
$error = "Error al actualizar. El enlace puede haber expirado." ;
}
}
}
Forced Password Change
The system supports forcing users to change passwords on first login:
$userMetadata = $login [ 'user' ][ 'user_metadata' ] ?? [];
if ( isset ( $userMetadata [ 'requires_password_change' ]) &&
( $userMetadata [ 'requires_password_change' ] === true ||
$userMetadata [ 'requires_password_change' ] === 'true' )) {
$_SESSION [ 'force_password_change' ] = true ;
header ( "Location: pages/cambiar_password.php" );
exit ;
}
When a user has requires_password_change set to true in their user metadata, they’re redirected to the password change page immediately after login.
Security Features
Email Verification
Unverified emails are detected and users can request resend:
if ( isset ( $login [ 'error_description' ]) &&
strpos ( $login [ 'error_description' ], 'Email not confirmed' ) !== false ) {
$error = "Tu correo no ha sido verificado." ;
$_SESSION [ 'resend_email' ] = $email ;
} elseif ( isset ( $login [ 'error' ])) {
$error = "Error de autenticación: " . $login [ 'error' ];
} else {
$error = "Error de autenticación. Verifica tus credenciales." ;
}
Emergency Admin Bypass
A specific email can bypass certain restrictions:
if ( $email === 'emmanuel.jarquin@grupomecsa.net' ) {
$isStaff = true ;
}
Client Access Prevention
The system explicitly prevents non-staff access:
if ( ! $isStaff ) {
session_destroy ();
$error = "Acceso denegado. Esta plataforma es exclusiva para personal autorizado y no permite el acceso a clientes." ;
}
Best Practices
Token Refresh Implement token refresh logic using refresh_token to maintain sessions
Secure Storage Store service role keys in local.supabase.php, never in version control
Session Validation Check token expiry and validate sessions on each protected page
Error Handling Provide clear, user-friendly error messages without exposing system details