Skip to main content

Documentation Index

Fetch the complete documentation index at: https://mintlify.com/nelrondon/backend-proyecto-estructuras/llms.txt

Use this file to discover all available pages before exploring further.

The Estructuras Backend API uses JSON Web Tokens (JWTs) to authenticate users across requests. When a user logs in successfully, the server signs a token containing the user’s ID, stores it in an HTTP-only cookie, and expects that cookie to be present on every subsequent request to a protected route. This approach keeps tokens out of JavaScript-accessible storage and relies on the browser’s built-in cookie security model.

Token Creation

The createAccessToken function lives in src/libs/jwt.js and wraps jwt.sign in a Promise, making it compatible with async/await throughout the codebase.
import { SECRET_KEY } from "../config.js";
import jwt from "jsonwebtoken";

export const createAccessToken = async (payload) => {
  return new Promise((resolve, reject) => {
    jwt.sign(payload, SECRET_KEY, { expiresIn: "7d" }, (err, token) => {
      if (err) {
        reject(err);
      } else {
        resolve(token);
      }
    });
  });
};
Key details:
  • The payload is the data you want to embed in the token — typically { id: user.id }.
  • SECRET_KEY is loaded from environment variables via src/config.js.
  • Tokens expire after 7 days (expiresIn: "7d"). After expiry the token is cryptographically invalid and the client must log in again.
  • The function returns a Promise<string>, so callers await it and receive the signed token string.
SECRET_KEY must be a long, cryptographically random string — at least 32 random bytes encoded as hex or base64. Never commit it to source control. Set it via an environment variable in your deployment platform (e.g. a Vercel environment secret or a .env file that is listed in .gitignore).

Token Verification

verifyToken mirrors createAccessToken by wrapping jwt.verify in a Promise. It decodes and validates a raw token string, resolving with the decoded payload or rejecting if the token is invalid or expired.
export const verifyToken = async (token) => {
  return new Promise((resolve, reject) => {
    jwt.verify(token, SECRET_KEY, (err, payload) => {
      if (err) {
        reject(err);
      } else {
        resolve(payload);
      }
    });
  });
};
The resolved payload contains all fields that were passed to createAccessToken plus standard JWT claims such as iat (issued at) and exp (expiration timestamp). After creating a token (typically in the login or register controller), the server sets it as an HTTP-only cookie:
const token = await createAccessToken({ id: userSaved.id });

res.cookie("token", token, {
  httpOnly: true,
  secure: true,
  sameSite: "none",
  maxAge: 7 * 24 * 60 * 60 * 1000, // 7 days in milliseconds
});
SettingValuePurpose
httpOnlytruePrevents client-side JavaScript from reading the cookie, blocking XSS-based token theft
securetrueEnsures the cookie is only transmitted over HTTPS
sameSite"none"Allows the cookie to be sent on cross-site requests (required because the frontend and backend are on different origins)
maxAge604800000 msCookie expires after 7 days, matching the JWT’s own expiry
sameSite: 'none' requires secure: true. If your server is not running over HTTPS in production, the browser will silently drop the cookie and all authenticated requests will fail with 401 Unauthorized. Always deploy behind HTTPS.

The authRequired Middleware

The authRequired middleware in src/middlewares/validateToken.js guards any route that requires an authenticated user. It reads the token cookie, verifies it using jwt.verify with a callback, and either attaches the decoded user to the request or terminates the request with an error response.
import jwt from "jsonwebtoken";
import { SECRET_KEY } from "../config.js";

export const authRequired = (req, res, next) => {
  const { token } = req.cookies;

  if (!token) {
    return res.status(401).json({ message: "No Token, Unauthorized" });
  }

  jwt.verify(token, SECRET_KEY, (err, user) => {
    if (err) {
      return res.status(403).json({ message: "Invalid Token" });
    }
    req.user = user;
    next();
  });
};

Response codes

ScenarioHTTP StatusBody
No token cookie present401 Unauthorized{ "message": "No Token, Unauthorized" }
Token present but invalid or expired403 Forbidden{ "message": "Invalid Token" }
Token validCalls next(), populates req.user

Using req.user in protected handlers

Once authRequired calls next(), the decoded JWT payload is available as req.user in every downstream handler on that route. Because createAccessToken is always called with { id: user.id }, you can reliably read the authenticated user’s database ID from req.user.id:
import { authRequired } from "../middlewares/validateToken.js";

router.get("/profile", authRequired, async (req, res) => {
  // req.user is the decoded JWT payload: { id, iat, exp }
  const user = await User.findById(req.user.id);

  if (!user) {
    return res.status(404).json({ message: "User not found" });
  }

  res.json(user);
});
Apply authRequired as route-level middleware rather than globally so that public endpoints like POST /api/register and POST /api/login remain accessible without a token.

End-to-End Authentication Flow

// After verifying credentials...
const token = await createAccessToken({ id: userSaved.id });

res.cookie("token", token, {
  httpOnly: true,
  secure: true,
  sameSite: "none",
  maxAge: 7 * 24 * 60 * 60 * 1000,
});

res.json({ id: userSaved.id, username: userSaved.username });

Build docs developers (and LLMs) love