Skip to main content

Overview

The API uses JWT (JSON Web Tokens) issued by Azure Active Directory to authenticate requests. Each token contains claims about the user’s identity and permissions.

Authorization Header Format

All authenticated requests must include a Bearer token in the Authorization header:
Authorization: Bearer eyJ0eXAiOiJKV1QiLCJhbGciOiJSUzI1NiIsIng1dCI6...
The token must be sent with every request to protected endpoints.

Token Structure

A JWT consists of three parts separated by dots:
header.payload.signature
Contains metadata about the token:
{
  "typ": "JWT",
  "alg": "RS256",
  "kid": "..."
}

Payload (Claims)

Contains user identity and token metadata:
{
  "aud": "api://12345678-1234-1234-1234-123456789abc",
  "iss": "https://login.microsoftonline.com/<tenant-id>/v2.0",
  "iat": 1234567890,
  "exp": 1234571490,
  "oid": "a1b2c3d4-e5f6-7890-abcd-ef1234567890",
  "upn": "[email protected]",
  "preferred_username": "[email protected]",
  "name": "Juan Pérez"
}

Signature

Cryptographic signature that validates the token hasn’t been tampered with.

Important Token Claims

The API uses the following claims from Azure AD tokens:

oid

Object ID - Unique identifier for the user in Azure AD. Used as the primary user identifier.

upn

User Principal Name - The user’s email address. Primary source for user email.

preferred_username

Preferred Username - Alternative email claim, used as fallback if upn is not present.

name

Display Name - The user’s full name as configured in Azure AD.

aud

Audience - The intended recipient of the token (must match AZURE_AUDIENCE).

iss

Issuer - The authority that issued the token (Azure AD).

exp

Expiration - Timestamp when the token expires (Unix timestamp).

iat

Issued At - Timestamp when the token was issued (Unix timestamp).

Token Verification with express-oauth2-jwt-bearer

The API uses the express-oauth2-jwt-bearer library (v1.7.1) to verify tokens.

Installation

npm install express-oauth2-jwt-bearer

Configuration

From src/middleware/authMiddleware.js:13-19:
src/middleware/authMiddleware.js
const checkJwtBase = auth({
    // El 'issuer' (emisor) que firmó el token debe ser Microsoft.
    issuerBaseURL: process.env.AZURE_ISSUER_BASE_URL, 
    
    // La 'audience' (audiencia) para la que fue emitido el token debe ser tu API.
    audience: process.env.AZURE_AUDIENCE,
});
The auth() function creates a middleware that automatically:
  • Downloads Azure AD’s public keys (JWKS)
  • Verifies the token signature
  • Validates issuer and audience
  • Checks token expiration

The authenticateAndExtract Middleware

This middleware combines JWT verification with user identity extraction.

Full Implementation

From src/middleware/authMiddleware.js:22-56:
src/middleware/authMiddleware.js
export const authenticateAndExtract = (req, res, next) => {
    // Primero, se ejecuta el middleware de verificación de JWT.
    checkJwtBase(req, res, async (err) => {
        // Si hay un error de JWT (token expirado, inválido, etc.), lo manejamos y salimos.
        if (err) {
            console.error("Error de autenticación JWT:", err.message);
            //Si el error es por token inválido o expirado, responder con 401
            if (err.name === 'InvalidTokenError' || err.code === 'credentials_required') {
                return res.status(401).json({ 
                    message: "Acceso Denegado. Token inválido, expirado o faltante.",
                    code: "invalid_auth_token"
                });
            }
            //Para otros errores, pasa al 500
            return next(err);
        }

        // Si checkJwt fue exitoso, el payload del token está en req.auth.payload
        if (req.auth && req.auth.payload) {
            const userEmail = req.auth.payload.upn || req.auth.payload.preferred_username;
            const name = req.auth.payload.name;
            
            console.log(`Autenticación Exitosa: ${userEmail}`);
            
            // Se adjunta la identidad al objeto que el controlador espera.
            req.userIdentity = {
                email: userEmail,
                azureId: req.auth.payload.oid // El ID de Objeto único de Azure
            };
        }
        
        console.log("-> Solicitud entrante a ruta protegida.");
        next(); 
    });
};

What It Does

1

Verify JWT

Calls checkJwtBase to validate the token signature, issuer, audience, and expiration.
2

Handle Errors

If verification fails:
  • Returns 401 for invalid/expired/missing tokens
  • Returns 500 for other errors
3

Extract User Identity

On successful verification:
  • Extracts email from upn or preferred_username claim
  • Extracts Azure Object ID from oid claim
  • Attaches identity to req.userIdentity
4

Continue to Next Middleware

Calls next() to proceed to the next middleware or route handler.

Request Object After Authentication

After successful authentication, the request object contains:
req.auth = {
  payload: {
    oid: "a1b2c3d4-e5f6-7890-abcd-ef1234567890",
    upn: "[email protected]",
    preferred_username: "[email protected]",
    name: "Juan Pérez",
    // ... other claims
  }
}

req.userIdentity = {
  email: "[email protected]",
  azureId: "a1b2c3d4-e5f6-7890-abcd-ef1234567890"
}

Usage in Routes

Apply the middleware to protect routes:

Example: Employee Routes

From src/routes/empleadoRoutes.js:10-13:
src/routes/empleadoRoutes.js
const router = express.Router();
router.use(authenticateAndExtract)
router.use(cargarUsuario);

router.get('/profile', empleadoController.getEmpleado);
All routes in this router automatically require a valid JWT token.

Error Responses

401 - Unauthorized

Returned when token validation fails:
{
  "message": "Acceso Denegado. Token inválido, expirado o faltante.",
  "code": "invalid_auth_token"
}
Common causes:
  • Token is expired
  • Token signature is invalid
  • Missing Authorization header
  • Invalid token format
  • Token audience doesn’t match API configuration

500 - Internal Server Error

Returned for unexpected errors during token verification:
{
  "message": "Internal server error"
}

Testing Authentication

Using cURL

curl -X GET https://api.example.com/api/empleados/profile \
  -H "Authorization: Bearer YOUR_JWT_TOKEN"

Using Postman

1

Set Authorization Type

Select Bearer Token from the Authorization type dropdown.
2

Paste Your Token

Paste your Azure AD JWT token in the Token field.
3

Send Request

Send the request to any protected endpoint.

Using JavaScript (Fetch)

const response = await fetch('https://api.example.com/api/empleados/profile', {
  headers: {
    'Authorization': `Bearer ${accessToken}`,
    'Content-Type': 'application/json'
  }
});

const data = await response.json();

Token Expiration

Azure AD tokens typically expire after 1 hour. Client applications must:
  1. Detect expiration: Handle 401 responses
  2. Refresh token: Use refresh tokens to obtain new access tokens
  3. Retry request: Retry the original request with the new token
Never store tokens in client-side code or version control. Use secure storage mechanisms like HttpOnly cookies or secure storage APIs.

Best Practices

Always Use HTTPS

Tokens must only be transmitted over encrypted connections to prevent interception.

Validate on Every Request

Never skip token validation, even for seemingly “internal” requests.

Handle Expiration Gracefully

Implement automatic token refresh in client applications.

Log Authentication Failures

Monitor failed authentication attempts for security analysis.

Next Steps

Azure AD Setup

Configure Azure Active Directory for your API

Roles & Permissions

Implement role-based access control

Build docs developers (and LLMs) love