Publicado en

Autenticación y autorización en el backend

Diferencia entre autenticación (POST /login con credenciales) y autorización (GET /admin con token Bearer y control de rol) sobre fondo oscuro

Cuando entras a una oficina, el guardia comprueba tu identificación. Eso es autenticación: verificar quién eres. Cuando llegas a una sala restringida y tu tarjeta de acceso no abre la puerta, eso es autorización: verificar qué puedes hacer. Son dos mecanismos distintos que trabajan juntos en cualquier sistema que gestione usuarios y recursos.

La diferencia fundamental

Comparación entre autenticación (verificar identidad con credenciales, resultado 200 o 401) y autorización (verificar permisos por roles, resultado 200 o 403)

La confusión entre los dos conceptos es habitual porque suelen implementarse juntos, pero su función es completamente diferente:

  • Autenticación: confirma que el usuario es quien dice ser. Ocurre al hacer login. El mecanismo habitual es verificar una contraseña, aunque también puede ser un código MFA, una clave de API o un certificado.
  • Autorización: determina qué puede hacer ese usuario una vez identificado. Ocurre en cada petición a un recurso protegido. El mecanismo habitual es comprobar el rol o los permisos asociados al usuario.

El código de respuesta HTTP refleja esta distinción: un error de autenticación devuelve 401 Unauthorized (no sabemos quién eres); un error de autorización devuelve 403 Forbidden (sabemos quién eres, pero no tienes permiso).

Autenticación: cómo se verifica la identidad

El flujo básico de autenticación con contraseña tiene tres pasos: el usuario envía sus credenciales, el servidor las verifica y, si son correctas, genera algún mecanismo de identificación persistente (sesión o token) para las siguientes peticiones.

POST /api/login HTTP/1.1
Content-Type: application/json
 
{
  "email": "ana@ejemplo.com",
  "password": "mi_contraseña"
}

Si las credenciales son válidas, el servidor responde con un token o crea una sesión. Si no, responde con 401.

Cómo se almacenan las contraseñas

Las contraseñas nunca se guardan en texto plano ni cifradas (cifrado es reversible). Se almacenan como un hash irreversible: el servidor aplica una función de hash a la contraseña y guarda el resultado. Al hacer login, hashea la contraseña recibida y compara los hashes.

// Al registrar al usuario
hash = bcrypt(password, coste=12)
guardar_en_bbdd(email, hash)
 
// Al hacer login
hash_guardado = obtener_de_bbdd(email)
es_valido = bcrypt.comparar(password_recibido, hash_guardado)
 
si es_valido → generar token → 200 OK
si no         → 401 Unauthorized

El algoritmo recomendado hoy es bcrypt o Argon2. Nunca uses MD5, SHA-1 ni SHA-256 directamente para contraseñas: son algoritmos rápidos diseñados para otro propósito, lo que los hace vulnerables a ataques de fuerza bruta. bcrypt y Argon2 están diseñados para ser lentos a propósito.

Autenticación multifactor (MFA)

La autenticación de un solo factor (solo contraseña) depende de que nadie más conozca esa contraseña. MFA añade un segundo factor: algo que tienes (tu teléfono), algo que eres (huella dactilar) o un código de un solo uso (TOTP). Si el primer factor se compromete, el segundo bloquea el acceso.

Autorización: cómo se controla el acceso

Una vez identificado el usuario, cada petición a un recurso protegido necesita una comprobación: ¿tiene este usuario permiso para hacer esto?

El modelo más extendido es RBAC (Role-Based Access Control, control de acceso basado en roles). En lugar de asignar permisos individuales a cada usuario, se definen roles que agrupan permisos, y los usuarios tienen uno o varios roles.

-- Estructura básica de RBAC
CREATE TABLE roles (
  id   INT PRIMARY KEY,
  nombre VARCHAR(50)  -- 'admin', 'editor', 'viewer'
);
 
CREATE TABLE usuarios_roles (
  id_usuario INT REFERENCES usuarios(id),
  id_rol     INT REFERENCES roles(id)
);
 
CREATE TABLE permisos (
  id      INT PRIMARY KEY,
  recurso VARCHAR(50),  -- 'articulos', 'usuarios'
  accion  VARCHAR(20)   -- 'leer', 'crear', 'borrar'
);
 
CREATE TABLE roles_permisos (
  id_rol    INT REFERENCES roles(id),
  id_permiso INT REFERENCES permisos(id)
);

Con este modelo, comprobar si un usuario puede borrar artículos es una consulta a la base de datos: ¿existe algún permiso articulos:borrar asociado a algún rol del usuario?

El flujo completo

Flujo completo de autenticación y autorización en dos fases: fase 1 el cliente envía credenciales y recibe un token, fase 2 el cliente envía el token en cada petición y el servidor verifica identidad y permisos

En la práctica, las dos fases se implementan como middlewares que se ejecutan antes de llegar al controlador de la ruta:

// Middleware de autenticación
función autenticar(peticion, respuesta, siguiente):
  token = peticion.cabeceras["Authorization"]
  si no hay token → devolver 401
 
  usuario = verificar_token(token)
  si token inválido o expirado → devolver 401
 
  peticion.usuario = usuario
  siguiente()  // pasa al siguiente middleware
 
// Middleware de autorización
función autorizar(rol_requerido):
  devuelve función(peticion, respuesta, siguiente):
    si peticion.usuario.rol != rol_requerido → devolver 403
    siguiente()
 
// Uso en una ruta
GET /api/panel-admin
  → autenticar()
  → autorizar("admin")
  → controlador_panel_admin()

Buenas prácticas

  • Principio de mínimo privilegio: cada usuario debe tener solo los permisos que necesita para su función. Un editor no necesita poder borrar usuarios.
  • Nunca texto plano: las contraseñas siempre se hashean con bcrypt o Argon2 antes de guardarse.
  • HTTPS siempre: las credenciales y los tokens viajan en texto si no usas TLS. Sin HTTPS, la autenticación es inútil.
  • Mensajes de error genéricos: si el login falla, devuelve siempre el mismo mensaje («credenciales incorrectas») sin especificar si el email existe o no. Los mensajes específicos ayudan a los atacantes a enumerar usuarios.
  • Tokens con expiración: un token que nunca expira es un riesgo permanente. Define un tiempo de vida razonable y usa tokens de refresco para renovarlos sin pedir credenciales de nuevo.

Para que el servidor pueda identificar al usuario en cada petición sin repetir el proceso de login, necesita un mecanismo compacto y verificable. Uno de los más extendidos hoy es JWT: un token que lleva la identidad del usuario firmada criptográficamente.