Skip to main content
The Calculus Learning Platform implements a dual-mode authentication system supporting both student and professor accounts with role-based routing.

Overview

The authentication system is built with Vue 3 on the frontend (Login.vue) and FastAPI on the backend (main.py), featuring:
  • User registration and login
  • Role-based access control (student vs professor)
  • Session persistence with localStorage
  • Special admin account handling

User Registration Flow

Frontend Registration Form

The registration form collects four fields from new users:
<!-- Login.vue -->
<input v-model="nombre" type="text" placeholder="Nombre" />
<input v-model="apellido" type="text" placeholder="Apellidos" />
<input v-model="email" type="email" placeholder="Correo electrónico" />
<input v-model="password" type="password" placeholder="Crea una contraseña" />
1

User Submits Form

When the user clicks “Registrarse”, the form validates all fields are filled.
2

API Request Sent

Data is sent to /registrar endpoint:
const respuesta = await fetch(`${urlBase}/registrar`, {
  method: "POST",
  headers: { "Content-Type": "application/json" },
  body: JSON.stringify({
    email: email.value,
    password: password.value,
    nombre: nombre.value,
    apellidos: apellido.value
  })
});
3

Database Record Created

The backend inserts the user into the usuarios table:
# main.py:124-132
query = "INSERT INTO usuarios (email, password, nombre, apellidos) VALUES (%s, %s, %s, %s)"
cursor.execute(query, (datos.email, datos.password, datos.nombre, datos.apellidos))
conexion.commit()
return {"mensaje": "Usuario creado", "exito": True}
4

User Redirected to Login

After successful registration, the mode switches to login:
// Login.vue:336-339
setTimeout(() => {
  esRegistro.value = false;
  mensaje.value = "Cuenta creada, Ahora inicia sesión.";
}, 1500);
Duplicate email registration is prevented by the database constraint. The backend returns {"mensaje": "El usuario ya existe", "exito": false} if the email already exists.

Login Mechanism

Standard Login Process

// Login.vue:281-291
const endpoint = esRegistro.value ? "/registrar" : "/login";
const respuesta = await fetch(`${urlBase}${endpoint}`, {
  method: "POST",
  headers: { "Content-Type": "application/json" },
  body: JSON.stringify({
    email: email.value,
    password: password.value
  })
});
The backend validates credentials against the database:
# main.py:142-163
@app.post("/login")
async def login(datos: UsuarioRegistro):
    cursor = conexion.cursor(cursor_factory=RealDictCursor)
    query = "SELECT email, nombre, apellidos FROM usuarios WHERE email = %s AND password = %s"
    cursor.execute(query, (datos.email, datos.password))
    usuario = cursor.fetchone()
    
    if usuario:
        return {
            "mensaje": "Login correcto",
            "exito": True,
            "usuario": usuario['email'],
            "nombre": usuario['nombre'],
            "apellidos": usuario['apellidos']
        }
    else:
        return {"mensaje": "Credenciales incorrectas", "exito": False}
Passwords are stored in plain text in the database. This is a security vulnerability that should be addressed in production by implementing password hashing (e.g., bcrypt).

Role-Based Access Control

Professor Account Detection

The system identifies professors using a hardcoded email check:
// Login.vue:298-307
const CORREO_PROFE = "[email protected]";

if (email.value.toLowerCase() === CORREO_PROFE.toLowerCase()) {
  localStorage.setItem("usuario", datos.usuario);
  mensaje.value = "Bienvenido, Profesor.";
  setTimeout(() => {
    router.push("/panel-profesor");
  }, 1000);
  return;
}

Student Account Routing

Non-professor accounts are routed to the student experience:
// Login.vue:309-331
if (!esRegistro.value) {
  localStorage.setItem("usuario", datos.usuario);
  localStorage.setItem("email", email.value);
  
  const yaVioBienvenida = localStorage.getItem(`visto_bienvenida_${datos.usuario}`);
  
  setTimeout(() => {
    if (yaVioBienvenida === 'true') {
      router.push("/foro1");  // Returning student
    } else {
      router.push("/bienvenida");  // First-time student
    }
  }, 1000);
}

Professor Role

  • Email: [email protected]
  • Route: /panel-profesor
  • Access: All student submissions
  • Capability: Provide feedback

Student Role

  • Email: Any other email
  • Route: /bienvenida or /foro1
  • Access: Own submissions only
  • Capability: Submit forum/exam responses

Session Management

localStorage Implementation

User sessions are maintained using browser localStorage:
// Login.vue:301, 312-313
localStorage.setItem("usuario", datos.usuario);
localStorage.setItem("email", email.value);

Session Keys Used

KeyPurposeExample Value
usuarioUser’s email identifier"[email protected]"
emailDuplicate email storage"[email protected]"
visto_bienvenida_{email}First-time user flag"true"
The system uses the usuario key to identify the logged-in user throughout the application. All forum and exam submissions reference this value.

Form Validation

Client-side validation ensures data integrity:
// Login.vue:239-266
function validarCampos() {
  if (esRegistro.value) {
    if (!nombre.value?.trim()) {
      mensaje.value = "Por favor ingresa tu nombre";
      tipoMensaje.value = "error";
      return false;
    }
    if (!apellido.value?.trim()) {
      mensaje.value = "Por favor ingresa tus apellidos";
      tipoMensaje.value = "error";
      return false;
    }
  }
  
  if (!email.value?.trim()) {
    mensaje.value = "Por favor ingresa tu correo electrónico";
    tipoMensaje.value = "error";
    return false;
  }
  
  if (!password.value) {
    mensaje.value = "Por favor ingresa tu contraseña";
    tipoMensaje.value = "error";
    return false;
  }
  
  return true;
}

Error Handling

The authentication system provides user-friendly error messages:
// Login.vue:294-351
const datos = await respuesta.json();

if (datos.exito) {
  tipoMensaje.value = "exito";
  mensaje.value = datos.mensaje;
  // ... handle success
} else {
  tipoMensaje.value = "error";
  mensaje.value = datos.mensaje;  // "Credenciales incorrectas" or "El usuario ya existe"
}
  • “Por favor ingresa tu correo electrónico” - Email field is empty
  • “Por favor ingresa tu contraseña” - Password field is empty
  • “Por favor ingresa tu nombre” - Name field is empty (registration only)
  • “Por favor ingresa tus apellidos” - Last name field is empty (registration only)
  • “Credenciales incorrectas” - Invalid email/password combination
  • “El usuario ya existe” - Email already registered
  • “Error conectando con el servidor” - Backend connection failure

API Endpoints

Registration Endpoint

# main.py:123-140
@app.post("/registrar")
async def registrar(datos: UsuarioRegistro):
    conexion = conectar_bd()
    if not conexion: raise HTTPException(500, "Error BD")
    try:
        cursor = conexion.cursor()
        query = "INSERT INTO usuarios (email, password, nombre, apellidos) VALUES (%s, %s, %s, %s)"
        cursor.execute(query, (datos.email, datos.password, datos.nombre, datos.apellidos))
        conexion.commit()
        return {"mensaje": "Usuario creado", "exito": True}
    except psycopg2.IntegrityError:
        conexion.rollback()
        return {"mensaje": "El usuario ya existe", "exito": False}

Login Endpoint

# main.py:142-163
@app.post("/login")
async def login(datos: UsuarioRegistro):
    conexion = conectar_bd()
    if not conexion: raise HTTPException(500, "Error BD")
    try:
        cursor = conexion.cursor(cursor_factory=RealDictCursor)
        query = "SELECT email, nombre, apellidos FROM usuarios WHERE email = %s AND password = %s"
        cursor.execute(query, (datos.email, datos.password))
        usuario = cursor.fetchone()
        
        if usuario:
            return {
                "mensaje": "Login correcto",
                "exito": True,
                "usuario": usuario['email'],
                "nombre": usuario['nombre'],
                "apellidos": usuario['apellidos']
            }
        else:
            return {"mensaje": "Credenciales incorrectas", "exito": False}
    finally:
        conexion.close()

Security Considerations

Security Vulnerabilities in Current Implementation:
  1. No Password Hashing - Passwords stored in plain text
  2. No HTTPS Enforcement - Credentials transmitted without encryption verification
  3. No Session Expiration - localStorage tokens never expire
  4. No CSRF Protection - No token-based request validation
  5. Hardcoded Admin Email - Single point of failure for professor access
For production deployment, implement:
  • Password hashing (bcrypt, argon2)
  • JWT tokens with expiration
  • HTTPS enforcement
  • Multi-factor authentication
  • Role-based permissions in database

Build docs developers (and LLMs) love