Skip to main content

Overview

The T1 Component Library API uses JWT (JSON Web Tokens) for secure, stateless authentication. Tokens are issued upon successful registration or login and must be included in the Authorization header for protected endpoints.
Tokens are valid for 7 days by default. This duration is configurable via the JWT_EXPIRES_IN environment variable.

How Authentication Works

The authentication system follows a standard JWT flow:
1

Register or Login

Users register via POST /api/auth/register or login via POST /api/auth/login. Both endpoints return a JWT token upon success.
2

Receive JWT Token

The server generates a signed JWT token containing the user’s ID and email. This token is returned in the response.
3

Store Token Securely

The client application stores the token securely (e.g., in localStorage, sessionStorage, or httpOnly cookies).
4

Include Token in Requests

For protected endpoints, the client includes the token in the Authorization header using the Bearer scheme.
5

Token Verification

The server’s authentication middleware validates the token, verifies it hasn’t expired, and attaches the user to the request.

Token Structure

JWT tokens are generated using the jsonwebtoken library and contain the following payload:
// Token payload (from auth.service.ts:33-36)
{
  id: user._id,        // User's MongoDB ID
  email: user.email    // User's email address
}
The token is signed using the JWT_SECRET environment variable and expires based on JWT_EXPIRES_IN (default: 7d).

Token Generation

Tokens are generated in the auth service:
// Source: server/src/services/auth.service.ts:25-38
const generateToken = (user: IUser): string => {
  const jwtSecret = process.env.JWT_SECRET;
  const jwtExpiresIn = process.env.JWT_EXPIRES_IN || '7d';

  if (!jwtSecret) {
    throw new Error('JWT_SECRET no está configurado');
  }

  return jwt.sign(
    { id: user._id, email: user.email },
    jwtSecret,
    { expiresIn: jwtExpiresIn }
  );
};

Obtaining a Token

There are two ways to obtain a JWT token:

1. Register a New User

POST /api/auth/register
Request Body:
{
  "nombre": "Jane Smith",
  "email": "[email protected]",
  "password": "mypassword123"
}
Response (201):
{
  "success": true,
  "message": "Usuario registrado exitosamente",
  "data": {
    "user": {
      "id": "507f1f77bcf86cd799439011",
      "nombre": "Jane Smith",
      "email": "[email protected]"
    },
    "token": "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJpZCI6IjYzN2Y..."
  }
}

2. Login with Existing Credentials

POST /api/auth/login
Request Body:
{
  "email": "[email protected]",
  "password": "mypassword123"
}
Response (200):
{
  "success": true,
  "message": "Login exitoso",
  "data": {
    "user": {
      "id": "507f1f77bcf86cd799439011",
      "nombre": "Jane Smith",
      "email": "[email protected]"
    },
    "token": "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJpZCI6IjYzN2Y..."
  }
}
Passwords are hashed using bcrypt with a salt factor of 10 before being stored in the database. See server/src/models/User.model.ts:34-40 for the implementation.

Using Tokens in Requests

Once you have a token, include it in the Authorization header of your requests using the Bearer authentication scheme:
Authorization: Bearer <your-jwt-token>

Example: Authenticated Request

curl http://localhost:3001/api/components/export \
  -H "Authorization: Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9..."

JavaScript/Fetch Example

const token = localStorage.getItem('authToken');

fetch('http://localhost:3001/api/components/export', {
  headers: {
    'Authorization': `Bearer ${token}`,
    'Content-Type': 'application/json'
  }
})
  .then(response => response.json())
  .then(data => console.log(data))
  .catch(error => console.error('Error:', error));

Axios Example

import axios from 'axios';

const token = localStorage.getItem('authToken');

axios.get('http://localhost:3001/api/components/export', {
  headers: {
    'Authorization': `Bearer ${token}`
  }
})
  .then(response => console.log(response.data))
  .catch(error => console.error('Error:', error));

Authentication Middleware

The authentication middleware validates tokens for protected routes:
// Source: server/src/middlewares/auth.middleware.ts:7-50
export const authMiddleware = async (
  req: AuthRequest,
  res: Response,
  next: NextFunction
): Promise<void> => {
  try {
    // 1. Extract token from Authorization header
    const authHeader = req.headers.authorization;

    if (!authHeader || !authHeader.startsWith('Bearer ')) {
      res.status(401).json({
        success: false,
        message: 'Token de autenticación no proporcionado'
      });
      return;
    }

    // 2. Verify token
    const token = authHeader.split(' ')[1];
    const jwtSecret = process.env.JWT_SECRET;
    const decoded = jwt.verify(token, jwtSecret) as JwtPayload;
    
    // 3. Find user from token payload
    const user = await User.findById(decoded.id);

    if (!user) {
      res.status(401).json({
        success: false,
        message: 'Usuario no encontrado'
      });
      return;
    }

    // 4. Attach user to request and proceed
    req.user = user;
    next();
  } catch (error) {
    res.status(401).json({
      success: false,
      message: 'Token inválido o expirado'
    });
  }
};

Protected vs Public Endpoints

The API has both public and protected endpoints:

Public Endpoints (No Token Required)

  • POST /api/auth/register - Register a new user
  • POST /api/auth/login - Login with credentials
  • POST /api/components/track - Track component interaction
  • GET /api/components/stats - Get usage statistics
  • GET /api/health - Health check

Protected Endpoints (Token Required)

  • GET /api/components/export/view - View paginated tracking data
  • GET /api/components/export - Export all tracking data
Protected routes are defined in server/src/routes/components.routes.ts:13-14 using the authMiddleware.

Token Expiration

Tokens expire after the duration specified in JWT_EXPIRES_IN (default: 7 days).

Handling Expired Tokens

When a token expires, the API returns a 401 Unauthorized response:
{
  "success": false,
  "message": "Token inválido o expirado"
}
Your application should:
  1. Detect the 401 status code
  2. Clear the stored token
  3. Redirect the user to the login page
  4. Prompt the user to log in again

Example Error Handler

axios.interceptors.response.use(
  response => response,
  error => {
    if (error.response?.status === 401) {
      // Token expired or invalid
      localStorage.removeItem('authToken');
      window.location.href = '/login';
    }
    return Promise.reject(error);
  }
);

Security Best Practices

Never expose your JWT_SECRET in client-side code or version control. Store it securely in environment variables on the server.

Recommendations

  1. Store Tokens Securely
    • Use httpOnly cookies for web applications to prevent XSS attacks
    • If using localStorage, be aware of XSS vulnerabilities
    • Never store tokens in URL parameters
  2. Use HTTPS in Production
    • Always transmit tokens over HTTPS to prevent interception
    • The API supports CORS for secure cross-origin requests
  3. Implement Token Refresh
    • Consider implementing a refresh token mechanism for long-lived sessions
    • Prompt users to re-authenticate before performing sensitive operations
  4. Validate Input
    • All authentication endpoints use express-validator for input validation
    • Passwords must be at least 6 characters (see User.model.ts:24)
  5. Monitor Failed Attempts
    • Log failed authentication attempts for security monitoring
    • The API uses Morgan for HTTP request logging

Common Authentication Errors

StatusError MessageCauseSolution
401Token de autenticación no proporcionadoMissing Authorization headerInclude Authorization: Bearer <token> header
401Token inválido o expiradoInvalid or expired JWTLogin again to obtain a new token
401Usuario no encontradoUser was deleted after token was issuedRegister or login again
401Credenciales inválidasWrong email or passwordCheck credentials and try again
400El email ya está registradoEmail already existsUse a different email or login instead

Authentication Flow Diagram

Here’s a visual representation of the authentication flow:
┌─────────┐                 ┌─────────┐                 ┌──────────┐
│ Client  │                 │   API   │                 │ Database │
└────┬────┘                 └────┬────┘                 └────┬─────┘
     │                           │                           │
     │  POST /auth/register      │                           │
     ├──────────────────────────>│                           │
     │  {email, password, name}  │                           │
     │                           │  Check if email exists    │
     │                           ├──────────────────────────>│
     │                           │                           │
     │                           │  Hash password (bcrypt)   │
     │                           │  Save user                │
     │                           ├──────────────────────────>│
     │                           │                           │
     │                           │  Generate JWT token       │
     │                           │  (with user ID & email)   │
     │                           │                           │
     │  Return user + token      │                           │
     │<──────────────────────────┤                           │
     │                           │                           │
     │  Store token              │                           │
     │                           │                           │
     │  GET /components/export   │                           │
     │  Authorization: Bearer... │                           │
     ├──────────────────────────>│                           │
     │                           │                           │
     │                           │  Verify JWT signature     │
     │                           │  Check expiration         │
     │                           │  Extract user ID          │
     │                           │                           │
     │                           │  Find user by ID          │
     │                           ├──────────────────────────>│
     │                           │<──────────────────────────┤
     │                           │                           │
     │  Return protected data    │                           │
     │<──────────────────────────┤                           │
     │                           │                           │

Next Steps

Tracking Endpoints

Learn how to track component interactions

Export Endpoints

Explore data export capabilities

Build docs developers (and LLMs) love