Skip to main content

Overview

DAF Backend implements a unique JWT-based authentication system that embeds database credentials directly in the token. This approach provides database-level security and audit trails, as each user connects to PostgreSQL with their own credentials.
Key Concept: Unlike traditional APIs with a single database user, DAF Backend creates database connections using credentials from the JWT token. This means authentication happens at both the API and database levels.

Authentication flow

The authentication process differs between POS and E-commerce systems:

POS system flow

1

User sends credentials

Client sends PostgreSQL database username and password to /api/pos/auth/login
2

API validates with database

Server attempts to connect to PostgreSQL using provided credentials
3

JWT token generated

If connection succeeds, a JWT token is created with credentials embedded
4

Subsequent requests use token

Client sends JWT in Authorization header, API extracts credentials and creates database connection

POS authentication endpoint

Endpoint: POST /api/pos/auth/loginRequest body:
{
  "user": "database_username",
  "password": "database_password"
}
Success response (200):
{
  "message": "Login exitoso",
  "token": "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJ1c3VhcmlvIjoicG9zX3VzZXIiLCJwYXNzd29yZCI6InBvc19wYXNzd29yZCIsInJvbGUiOiJ1c3VhcmlvIiwiaWF0IjoxNzA5NTU2MDAwLCJleHAiOjE3MDk2NDI0MDB9.signature",
  "role": "usuario"
}
Error response (401):
{
  "message": "Credenciales incorrectas o error de conexión",
  "detail": "password authentication failed for user \"pos_user\""
}

JWT token structure

The JWT tokens have different payloads for POS and E-commerce systems:
{
  "usuario": "pos_user",        // PostgreSQL username
  "password": "pos_password",   // PostgreSQL password
  "role": "usuario",             // User's database role
  "iat": 1709556000,              // Issued at timestamp
  "exp": 1709642400               // Expiration timestamp
}
Security Consideration: POS tokens contain database credentials. While encrypted, ensure tokens are transmitted only over HTTPS in production and stored securely on the client.

Using JWT tokens in requests

Once authenticated, include the JWT token in the Authorization header of all API requests:
curl http://localhost:3000/api/pos/cliente \
  -H "Authorization: Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9..."

Authentication middleware

The API uses middleware to verify JWT tokens on protected routes:

POS authentication middleware

Located in src/middlewares/pos.auth.middleware.js:
src/middlewares/pos.auth.middleware.js:4-33
const verifyToken = (req, res, next) => {
  const authHeader = req.headers['authorization'];

  if (!authHeader) {
    return res.status(401).json({
      message: 'Token no proporcionado',
    });
  }

  const token = authHeader.split(' ')[1]; // Authorization: Bearer {TOKEN}

  if (!token) {
    return res.status(401).json({
      message: 'Token inválido',
    });
  }

  try {
    const decoded = jwt.verify(token, process.env.JWT_SECRET);

    // 🔒 Se mantiene compatibilidad con todo lo existente
    req.user = decoded;
    next();
    
  } catch (error) {
    return res.status(401).json({
      message: 'Token inválido o expirado',
    });
  }
};
The middleware:
  1. Extracts token from Authorization: Bearer <token> header
  2. Verifies token signature using JWT_SECRET
  3. Decodes token and attaches payload to req.user
  4. Rejects invalid or expired tokens with 401 status

Credential-based database connections

The unique aspect of DAF Backend is how it uses JWT credentials to create database connections:

POS connection strategy

For POS endpoints, each request creates a new database connection using credentials from the JWT:
src/controllers/pos.cliente.controller.js:6-9
const connectFromJWT = (req) => {
    const { usuario, password } = req.user;
    return getConnectionWithCredentials(usuario, password);
};
The getConnectionWithCredentials function creates a connection pool:
src/config/db_pos.js:4-12
function getConnectionWithCredentials(user, password) {
  return new Pool({
    host: process.env.POS_HOST,
    port: process.env.POS_PORT,
    database: process.env.POS_NAME,
    user,
    password,
  });
}
Connection Lifecycle: Each POS request creates a pool, executes queries, then closes the pool in the finally block. This ensures proper cleanup.

E-commerce connection strategy

E-commerce uses a traditional shared connection pool:
src/config/db_ecom.js:4-14
const pool = new Pool({
  host: process.env.EC_HOST,
  port: process.env.EC_PORT,
  database: process.env.EC_NAME,
  user: process.env.EC_USER,
  password: process.env.EC_PASSWORD,
});

const getConnection = () => {
  return pool;
};
All e-commerce requests share this pool, as user identity is tracked at the application level via the JWT token.

POS login implementation

Here’s how the POS login controller validates credentials by attempting a database connection:
src/controllers/pos.auth.controller.js:4-58
const login = async (req, res) => {
  const { user, password } = req.body;

  if (!user || !password) {
    return res.status(400).json({
      message: 'user y password son requeridos',
    });
  }

  try {
    // Intentamos conectar a la BD con las credenciales que manda Postman
    pool = getConnectionWithCredentials(user, password);

    // Verificamos todos los roles a los que pertenece el usuario
    const result = await pool.query(`
      SELECT r.rolname
      FROM pg_roles r
      JOIN pg_auth_members m ON r.oid = m.roleid
      JOIN pg_roles u ON u.oid = m.member
      WHERE u.rolname = (SELECT CURRENT_USER)
    `);

    // Extraemos todos los nombres de roles en un array
    const role = result.rows[0] ? result.rows[0].rolname : 'usuario';

    const token = jwt.sign(
      {
        usuario: user,
        password: password,
        role: role,
      },
      process.env.JWT_SECRET,
      {
        expiresIn: process.env.JWT_EXPIRES_IN,
      }
    );

    return res.status(200).json({
      message: 'Login exitoso',
      token,
      role,
    });

  } catch (error) {
    return res.status(401).json({
      message: 'Credenciales incorrectas o error de conexión',
      detail: error.message
    });

  } finally {
    if (pool) {
      await pool.end();
    }
  }
};
If the database credentials are incorrect, the connection will fail and return a 401 error. This validates authentication at the database level.

Token expiration

Tokens expire after the duration specified in JWT_EXPIRES_IN environment variable (default: 24 hours). When a token expires:
  1. The middleware will return: {"message": "Token inválido o expirado"}
  2. The client must re-authenticate by calling the login endpoint again
  3. A new token will be issued with a fresh expiration
Implement token refresh logic in your frontend to automatically re-authenticate users before their tokens expire.

Protected routes

Routes are protected by adding the verifyToken middleware:
const { verifyToken } = require('../middlewares/pos.auth.middleware.js');

router.get('/', verifyToken, clienteController.getAll);
router.post('/', verifyToken, clienteController.create);
router.get('/:id', verifyToken, clienteController.getByID);

Best practices

Secure token storage

Store JWT tokens securely on the client (e.g., httpOnly cookies, secure localStorage)

HTTPS only

Always use HTTPS in production to protect tokens in transit

Token rotation

Implement token refresh before expiration for seamless user experience

Database permissions

Grant POS database users only the permissions they need (principle of least privilege)

Common authentication errors

The Authorization header is missing from the request.Solution: Include the header with format Authorization: Bearer <token>
The token format is incorrect or the Bearer prefix is missing.Solution: Ensure token follows format Bearer <token> in Authorization header
The JWT signature is invalid or the token has expired.Solutions:
  • Token may have expired - login again to get a new token
  • JWT_SECRET may have changed on the server
  • Token may be corrupted - request a new token
For POS login: The database username/password is incorrect.Solutions:
  • Verify the PostgreSQL user exists: SELECT * FROM pg_roles WHERE rolname = 'username';
  • Check password is correct
  • Ensure user has CONNECT privilege on the database

Next steps

Architecture

Understand how authentication fits into the overall architecture

Database

Learn more about connection pooling and database setup

Error Handling

Handle authentication errors gracefully

POS API Reference

Explore all authenticated POS endpoints

Build docs developers (and LLMs) love