Saltar al contenido principal

Caso Real: Portal del Conductor — Albaranes con QR para Camiones con VB.NET y Vue 3

Web para conductores de camión de COVAP: login con la matrícula, albaranes desde ayer con QR al PDF, edición y devolución con documento nuevo vía orquestación del ERP, y panel de administración. API REST en VB.NET + SPA en Vue 3, Oracle en solo lectura y tokens opacos con PBKDF2.

Fran Cobos 10 min de lectura 1985 palabras

Tabla de contenidos

Resumen

Desarrollé el Portal del Conductor para COVAP: una aplicación web donde cada conductor de camión accede con su matrícula, consulta sus albaranes (decas) desde ayer en adelante y abre el PDF de cada uno escaneando su código QR. Desde la ficha puede editar el albarán o hacer una devolución (ambos generan un documento nuevo), y los administradores gestionan conductores desde un panel admin con alta masiva. Todo con una API REST en VB.NET y una SPA en Vue 3, Oracle en solo lectura y autenticación por tokens opacos. Login con rate limiting y anti-enumeración, 13 endpoints propios y despliegue en IIS sin CORS (front en / + API en /api).


El problema

Los conductores necesitaban sus albaranes del día sin depender de papel ni de llamadas a oficina. La situación de partida era:

  • Albarán en papel — el conductor dependía del documento físico para cada entrega
  • Llamadas a oficina — cualquier duda sobre un albarán implicaba llamar por teléfono
  • Sin autonomía — no había forma de consultar “qué tengo asignado hoy” desde el móvil
  • Devoluciones lentas — corregir un albarán o devolver mercancía era un trámite manual
  • Gestión de accesos dispersa — altas y bloqueos de conductores sin una herramienta central

Lo que me pidieron: “Queremos que cada conductor entre con su matrícula, vea sus albaranes del día, abra el PDF con el QR y pueda corregir o devolver desde el móvil. Y un panel para gestionar las cuentas.”


La arquitectura que elegí

┌──────────────────────────────────────────────────────────────────┐
│  NAVEGADOR (móvil u ordenador)                                   │
│  ┌─────────────────────────────────────────────────────────────┐ │
│  │  SPA VUE 3 + VUE ROUTER 4 + VITE 6                        │ │
│  │                                                             │ │
│  │  /login → /albaranes → /deca/{id} → /admin → /cuenta       │ │
│  │  Tarjetas con QR (qrcode.vue) + buscador + paginación      │ │
│  │  Token + matrícula + rol en localStorage                   │ │
│  └──────────────────────┬──────────────────────────────────────┘ │
└─────────────────────────┼────────────────────────────────────────┘
                          │ HTTP /api + Authorization: Bearer
                          ▼
┌──────────────────────────────────────────────────────────────────┐
│  IIS (mismo dominio → sin CORS)                                  │
│  front en /  +  API en /api                                     │
│  ┌─────────────────────────────────────────────────────────────┐ │
│  │  API REST — ASP.NET WEB API 2 (VB.NET, .NET 4.7.2)         │ │
│  │  AuthController · DecasController · AdminController         │ │
│  │  FiltroToken (Bearer) · FiltroAdmin (rol) · FiltroExcepciones│ │
│  └──────┬──────────────────────────────┬────────────────────────┘ │
└─────────┼──────────────────────────────┼──────────────────────────┘
          │ solo EXECUTE sobre SPs       │ solo lectura (OPENQUERY
          │ (login mínimo privilegio)    │ dentro del SP, nunca
          ▼                              │ desde .NET)
┌────────────────────┐     ┌─────────────┴─────────────────────────┐
│  SQL SERVER DB_APP │     │  ERP ORACLE (solo lectura)            │
│  Usuarios, tokens  │     │  T_DOCUMENTOCONTROL + LINEAS          │
│  y overlays de     │     │                                       │
│  edición/devolución│     │  Documento nuevo vía ORQUESTACIÓN     │
└────────────────────┘     │  ORCH_INSERT_DOC (Basic Auth, ~1 s)   │
                           └───────────────────────────────────────┘

Separación radical: el front no toca la base de datos

La API es la única que accede a datos. El front es una SPA que consume HTTP con Authorization: Bearer. Esto me permitió desplegar ambas piezas de forma independiente bajo el mismo IIS y eliminar CORS por completo.

Oracle solo se lee, y solo desde un stored procedure

Regla de oro del proyecto: el código .NET jamás consulta Oracle directamente. Toda lectura de albaranes pasa por un SP de DB_APP que usa OPENQUERY contra el linked server. Si mañana cambia el ERP, solo se tocan los SPs.

Los documentos nuevos los crea la orquestación del ERP

Editar o devolver no modifica Oracle: se llama a ORCH_INSERT_DOC, que genera el QR + PDF nuevos, y el vínculo (original → documento nuevo) se guarda en DB_APP. El albarán original se conserva siempre.


Autenticación con tokens opacos (adiós Session)

Me aparté conscientemente de la convención COVAP (WebForms + Session) y usé tokens opacos Bearer:

  • Token de 256 bits, caducidad de 60 minutos con renovación deslizante; en BD solo vive su hash SHA-256 (clsToken.MINUTOS = 60)
  • Contraseñas PBKDF2 con 100.000 iteraciones, formato iteraciones:saltHex:hashHex, mínimo 8 caracteres
  • Rate limiting en memoria: 5 fallos por matrícula en 15 minutos → HTTP 429
  • Anti-enumeración: el login verifica siempre un hash PBKDF2 (real o señuelo) en tiempo constante, para no revelar si la matrícula existe o está activa
  • La matrícula del token manda: la autorización usa siempre la matrícula de la sesión validada, nunca la que envíe el cliente

En el front, el guard del router lo deja claro:

// router/index.js — guard de autenticación y de admin
router.beforeEach((to) => {
  const { token, esAdmin } = auth.estado();
  if (to.meta.requiereAuth && !token) return '/login';
  if (to.meta.requiereAdmin && !esAdmin) return '/albaranes';
});

Sin token → /login. Ruta admin sin rol → de vuelta al listado. Simple y sin fisuras.


Edición y devolución sin escribir en Oracle

Desde la ficha del albarán (/deca/{ID_DOCO_CRIPTED}), el conductor puede:

  1. Editar — matrícula, fecha, origen, destino, cantidad, kg y descripción. El resto de campos se heredan del original.
  2. Devolver — intercambia origen/destino y permite editar descripción, cantidad y kg. Una devolución no se vuelve a editar.

En ambos casos se genera un QR y PDF nuevos, la app vuelve al listado y resalta la tarjeta del documento nuevo (etiqueta “nuevo”, salto a su página y scroll hasta ella). Las versiones previas siguen disponibles en “Ver versiones anteriores”.

Validaciones estrictas: fecha de ayer en adelante, matrícula en mayúsculas con formato, cantidad y kg enteros, descripción de 200 caracteres como máximo. Las Observaciones no se editan. Y si se cambia la matrícula, el albarán se reasigna (deja de verse con la original).

Detalle técnico: la URL y el QR usan ID_DOCO_CRIPTED (hex), porque el ID_DOCO real contiene | y +, no aptos para URLs.


Panel de administración con protecciones anti-bloqueo

El rol admin (EsAdmin = 1) accede a /admin: alta individual y masiva (pegando varias matrículas con una contraseña común), activar/desactivar, marcar o quitar admin, resetear contraseñas y eliminar cuentas. Desactivar revoca los tokens; eliminar borra también sus tokens.

Lo interesante son las protecciones contra el auto-bloqueo, en código y en base de datos:

  • Un admin no puede quitarse el rol, desactivarse ni eliminarse (AUTO_ADMIN, AUTO_BAJA, AUTO_ELIMINAR)
  • Siempre debe quedar al menos un administrador activo (ULTIMO_ADMIN)
  • Sin rol admin, cualquier api/admin/* responde 403

Problemas reales que encontré (y cómo los resolví)

1. El ERP es de solo lectura y no acepta escrituras

No había forma de modificar un albarán en Oracle directamente.

Solución: patrón overlay. La edición/devolución llama a la orquestación ORCH_INSERT_DOC (síncrona, ~1 s; si falla, no se guarda nada) y el resultado se persiste en T_DecasEdicion / T_DecasDevolucion. Oracle intacto, trazabilidad total.

2. El identificador del albarán no sirve para URLs

El ID_DOCO (KCOO|DCTO|DOCO|NUM) contiene | y +, que rompen rutas y QRs.

Solución: usar ID_DOCO_CRIPTED (hex) en rutas, QR y API. Para el documento nuevo, el DOCO_ENCRIPTADO que devuelve la orquestación.

3. Un camión no debe ver albaranes ajenos bajo ningún concepto

Bastaría manipular un parámetro para espiar otra matrícula.

Solución: la autorización usa siempre la matrícula del token validado por FiltroToken. Lo que envíe el cliente se ignora. El listado, además, está fijado a “desde ayer en adelante”.

4. El login no debe revelar si una matrícula existe

Un atacante podría enumerar matrículas válidas midiendo tiempos o respuestas.

Solución: verificación PBKDF2 siempre (hash real o señuelo) en tiempo constante + rate limiting de 5 fallos/15 min por matrícula → 429. Mismo coste, misma respuesta.

5. El proxy/WAF solo permite GET y POST

PATCH y DELETE no atraviesan la infraestructura de producción.

Solución: todas las operaciones mutantes (actualizar, eliminar, cambiar contraseña) usan POST. Decisión documentada y aplicada en los 13 endpoints.

6. Recuar permisos sin romper nada al re-desplegar SPs

Los GRANT EXECUTE del login de mínimo privilegio se pierden si un SP se recrea con DROP + CREATE.

Solución: todos los SPs se definen con CREATE OR ALTER, que conserva los permisos. Y documentado: si algún día se usa DROP, hay que re-ejecutar el script 009.


Funcionalidades clave

MóduloFuncionalidadDetalle
AccesoRegistro + login por matrículaNormalizada a mayúsculas, contraseña mín. 8
ListadoAlbaranes desde ayerBuscador + paginación en cliente (6/12/24)
TarjetaQR por albaránCodifica la URL del visor externo del PDF
DetalleFicha + PDFPDF embebido en base64 bajo demanda
Edición7 campos editablesGenera QR + PDF nuevos vía orquestación
DevoluciónOrigen/destino invertidosNo re-editable, con su propio documento
VersionesHistorial completoModal Original + cada modificación
ResaltadoTarjeta “nuevo”Salto de página + scroll tras guardar
AdminGestión de conductoresAlta masiva, roles, reseteo, borrado
CuentaCambio de contraseñaVerificando la actual
API13 endpointsCódigos de error propios en JSON

Despliegue y seguridad

  • Servidor: IIS con front en / y API en /api (mismo dominio, sin CORS), fallback SPA a index.html
  • Cabeceras: X-Content-Type-Options, X-Frame-Options: DENY, Referrer-Policy, Strict-Transport-Security, Content-Security-Policy y Permissions-Policy en front y API
  • Mínimo privilegio: el login usuario_api solo tiene EXECUTE sobre los SPs; sin acceso directo a tablas ni sa
  • Secretos como plantilla: connectionStrings.config y orquestacion.config se versionan como template, con los valores reales solo en servidor
  • Desarrollo: Visual Studio (IIS Express :52794) + Vite (:5173) con proxy /api; VITE_URL_ALBARAN configurable para el visor del PDF
  • Base de datos: 11 scripts SQL en orden (001…011); el primer admin se marca con el UPDATE comentado en 007

Números del proyecto

MétricaValor
BackendVB.NET + ASP.NET Web API 2 (.NET 4.7.2)
FrontendVue 3 + Vue Router 4 + Vite 6
Controladores3 (Auth, Decas, Admin)
Endpoints13 (todos mutantes en POST)
Tablas DB_APP4 (usuarios, tokens, ediciones, devoluciones)
Scripts SQL11 (SPs con CREATE OR ALTER)
Vistas SPA6 (Login, Registro, Albaranes, Detalle, Admin, Cuenta)
TokenOpaco 256 bits, SHA-256 en BD, 60 min deslizantes
Hash contraseñaPBKDF2 100.000 iteraciones
Rate limiting5 fallos / 15 min → 429
Orquestación ERPSíncrona (~1 s), sin escritura directa en Oracle
Tiempo de desarrollo2 meses

Lo que aprendí

  1. El token opaco simplifica todo — frente a Session, un Bearer con hash SHA-256 y renovación deslizante es más fácil de razonar, de revocar y de desplegar en granja.
  2. La autorización debe salir del token, nunca del cliente — una sola regla (“la matrícula del token manda”) elimina familias enteras de vulnerabilidades IDOR.
  3. El hash señuelo es barato y eficaz — verificar PBKDF2 siempre, exista o no el usuario, cierra la enumeración sin coste perceptible.
  4. CREATE OR ALTER protege los permisos — un detalle de SQL Server que evita romper el mínimo privilegio en cada despliegue.
  5. POST-only no es un hack, es compatibilidad — adaptarse al proxy/WAF desde el diseño evita sorpresas en producción.
  6. Las protecciones anti-bloqueo se ponen el día uno — impedir el auto-borrado del último admin parece obvio hasta que alguien lo hace.

¿Necesitas algo similar?

Si tu empresa necesita un portal para conductores o personal de campo con accesos por identificador propio, documentos con QR, integración de solo lectura con un ERP existente o un panel de administración con roles, hablemos.

Más casos reales:

Fran Cobos

Fran Cobos

Desarrollador Full Stack especializado en IA aplicada, automatización y desarrollo web. Escribo sobre herramientas, tutoriales y casos reales para programadores.

¿Necesitas desarrollo a medida?

Apps web, IA, módulos ERP — cuéntame tu proyecto.