> ## Documentation Index
> Fetch the complete documentation index at: https://translations.mintlify.site/llms.txt
> Use this file to discover all available pages before exploring further.

> La implementación en Node.js de la API para el escenario de arquitectura de Aplicaciones móviles + API

# Implementación de la API en Node.js (Aplicaciones móviles + API)

export const AuthCodeBlock = ({filename, icon, language, highlight, children}) => {
  const [displayText, setDisplayText] = useState(children);
  const [copyText, setCopyText] = useState(children);
  const wrapperRef = React.useRef(null);
  useEffect(() => {
    let unsubscribe = null;
    function init() {
      if (!window.autorun || !window.rootStore) {
        return;
      }
      unsubscribe = window.autorun(() => {
        let processedChildrenForDisplay = children;
        let processedChildrenForCopy = children;
        for (const [key, value] of window.rootStore.variableStore.values.entries()) {
          const escapedKey = key.replaceAll(/[.*+?^${}()|[\]\\]/g, (String.raw)`\$&`);
          let displayValue = value;
          if (key === "{yourClientSecret}" && value !== "{yourClientSecret}") {
            displayValue = value.substring(0, 3) + "*****ENMASCARADO*****";
          }
          processedChildrenForDisplay = processedChildrenForDisplay.replaceAll(new RegExp(escapedKey, "g"), displayValue);
          processedChildrenForCopy = processedChildrenForCopy.replaceAll(new RegExp(escapedKey, "g"), value);
        }
        setDisplayText(processedChildrenForDisplay);
        setCopyText(processedChildrenForCopy);
      });
    }
    if (window.rootStore) {
      init();
    } else {
      window.addEventListener("adu:storeReady", init);
    }
    return () => {
      window.removeEventListener("adu:storeReady", init);
      unsubscribe?.();
    };
  }, [children]);
  useEffect(() => {
    if (!wrapperRef.current) return;
    const originalWriteText = navigator.clipboard.writeText.bind(navigator.clipboard);
    let isOverriding = false;
    const handleClick = e => {
      const button = e.target.closest('[data-testid="copy-code-button"]');
      if (!button || !wrapperRef.current.contains(button)) return;
      isOverriding = true;
      navigator.clipboard.writeText = text => {
        if (isOverriding) {
          isOverriding = false;
          navigator.clipboard.writeText = originalWriteText;
          return originalWriteText(copyText);
        }
        return originalWriteText(text);
      };
      setTimeout(() => {
        if (isOverriding) {
          isOverriding = false;
          navigator.clipboard.writeText = originalWriteText;
        }
      }, 100);
    };
    const wrapper = wrapperRef.current;
    wrapper.addEventListener('click', handleClick, true);
    return () => {
      wrapper.removeEventListener('click', handleClick, true);
      if (navigator.clipboard.writeText !== originalWriteText) {
        navigator.clipboard.writeText = originalWriteText;
      }
    };
  }, [copyText]);
  return <div ref={wrapperRef}>
      <CodeBlock filename={filename} icon={icon} language={language} lines highlight={highlight}>
        {displayText}
      </CodeBlock>
    </div>;
};

export const codeExample = `// configurar dependencias: código omitido

// Habilitar CORS: código omitido

// Crear middleware para validar el JWT
const checkJwt = jwt({
  // Proporcionar dinámicamente una clave de firma según el kid del encabezado y las claves de firma proporcionadas por el endpoint JWKS
  secret: jwksRsa.expressJwtSecret({
    cache: true,
    rateLimit: true,
    jwksRequestsPerMinute: 5,
    jwksUri: \`https://{yourDomain}/.well-known/jwks.json\`
  }),

  // Validar la audiencia y el emisor
  audience: '{YOUR_API_IDENTIFIER}', //reemplázala por la audiencia de tu API, disponible en Dashboard > APIs
  issuer: 'https://{yourDomain}/',
  algorithms: [ 'RS256' ]
});

// Habilitar el uso de middleware para analizar el cuerpo de la solicitud: código omitido

// crear el endpoint de la API de hojas de horas: código omitido
app.post('/timesheets', checkJwt, function(req, res){
  var timesheet = req.body;

  // Guardar la hoja de horas en la base de datos...

  //enviar la respuesta
  res.status(201).send(timesheet);
});
// iniciar el servidor de la API en localhost:8080: código omitido`;

Este documento forma parte del escenario de Arquitectura móvil + API y explica cómo implementar la API en Node.js. El código fuente completo de la implementación de la API en Node.js está disponible en [este repositorio de GitHub](https://github.com/auth0-samples/auth0-pnp-exampleco-timesheets/tree/master/timesheets-api/node).

Consulte el escenario para obtener más información sobre la solución implementada.

<AccordionGroup>
  <Accordion title="1 Defina los endpoints de la API">
    Esta implementación usa el [framework web Express](http://expressjs.com/) para crear una API de Node.js.

    ##### Crear un archivo package.json

    Cree una carpeta para su API, acceda a ella y ejecute `npm init`. Esto configura el archivo `package.json`.

    Deje la configuración predeterminada o cámbiela según le convenga.

    El archivo `package.json` de nuestro ejemplo es similar al siguiente:

    ```json lines theme={null}
    {
      "name": "timesheets-api",
      "version": "1.0.0",
      "description": "API used to add timesheet entries for employees and contractors",
      "main": "index.js",
      "scripts": {
        "start": "node index.js",
        "test": "echo \"Error: no test specified\" && exit 1"
      },
      "dependencies": {
        "body-parser": "^1.20.0",
        "cors": "^2.8.5",
        "express": "^4.18.0",
        "express-oauth2-jwt-bearer": "^1.6.0"
      },
      "author": "Auth0",
      "license": "MIT"
    }
    ```

    ##### Instala las dependencias

    A continuación, configura las dependencias con los siguientes módulos:

    * **express**: Este módulo añade el [framework de aplicaciones web Express](https://expressjs.com/).
    * **cors**: Este módulo añade compatibilidad con [CORS](https://en.wikipedia.org/wiki/Cross-origin_resource_sharing), lo cual es necesario porque se llama a la API desde una aplicación de una sola página que se ejecuta en un dominio diferente dentro de un navegador web.
    * **jwks-rsa**: Esta biblioteca recupera claves de firma RSA desde un endpoint de JWKS (JSON Web Key Set). Con `expressJwtSecret`, podemos generar un proveedor de secretos que proporciona la clave de firma correcta a `express-jwt` en función del `kid` del encabezado del JWT. Para obtener más información, consulta el [repositorio de GitHub de node-jwks-rsa](https://github.com/auth0/node-jwks-rsa).
    * **express-jwt**: Este módulo autentica solicitudes HTTP mediante tokens JWT en tus aplicaciones de Node.js. Proporciona varias funciones que facilitan el trabajo con JWT. Para obtener más información, consulta el [repositorio de GitHub de express-jwt](https://github.com/auth0/express-jwt).
    * **body-parser**: Este es un middleware de análisis del cuerpo de solicitudes para Node.js. Extrae todo el contenido del cuerpo de un flujo de solicitud entrante y lo expone en `req.body` de una forma más fácil de usar.

    Para instalar estas dependencias, ejecuta lo siguiente:

    ```bash wrap lines theme={null}
    npm install express cors express-jwt jwks-rsa body-parser express-jwt-authz --save
    ```

    ##### Implementa los endpoints

    Ve al directorio de tu API y crea un archivo `server.js`. Tu código debe:

    * Instalar las dependencias.
    * Implementar el endpoint o los endpoints.
    * Iniciar el servidor de la API.

    Esta es una implementación de ejemplo:

    ```javascript lines theme={null}
    const express = require('express');
    const app = express();
    const { expressjwt: jwt } = require('express-jwt');
    const jwksRsa = require('jwks-rsa');
    const cors = require('cors');
    const bodyParser = require('body-parser');

    // Habilitar CORS
    app.use(cors());

    // Habilitar el uso del middleware de análisis del cuerpo de la solicitud
    app.use(bodyParser.json());
    app.use(bodyParser.urlencoded({
      extended: true
    }));

    // Crear el endpoint de la API de hojas de tiempo
    app.post('/timesheets', function(req, res){
      res.status(201).send({message: "This is the POST /timesheets endpoint"});
    })

    // Iniciar el servidor de la API en localhost:8080
    app.listen(8080);
    ```

    Inicie su servidor de API con `node server` y haga una solicitud HTTP POST a `localhost:8080/timesheets`. Debería ver una respuesta JSON con el mensaje `This is the POST /timesheets endpoint`.

    Ahora ya tenemos nuestro endpoint, pero cualquiera puede llamarlo. Continúe con el siguiente paso para ver cómo podemos corregir esto.
  </Accordion>

  <Accordion title="2 Proteja los endpoints de la API">
    Para validar nuestro token, use la función `jwt`, proporcionada por el [middleware express-jwt](https://github.com/auth0/express-jwt#usage), y `jwks-rsa` para recuperar nuestro secreto. Las bibliotecas hacen lo siguiente:

    1. `express-jwt` decodifica el token y pasa la solicitud, el encabezado y la carga útil a `jwksRsa.expressJwtSecret`.
    2. `jwks-rsa` descarga todas las claves de firma del endpoint JWKS y comprueba si alguna coincide con el `kid` del encabezado del JWT. Si ninguna de las claves de firma coincide con el `kid` recibido, se generará un error. Si hay coincidencia, pasa la clave de firma correcta a `express-jwt`.
    3. `express-jwt` continúa con su propia lógica para validar la firma del token, la expiración, `audience` y el `issuer`.

    Los pasos que seguiremos en nuestro código son:

    * Crear la función de middleware para validar el token de acceso.
    * Habilitar el uso del middleware en nuestras rutas.

    También puede escribir código para guardar realmente la hoja de horas en una base de datos. Esta es nuestra implementación de ejemplo (se omite parte del código por brevedad):

    <AuthCodeBlock children={codeExample} language="javascript" />

    Si iniciamos nuestro servidor ahora y hacemos una solicitud HTTP POST a `localhost:8080/timesheets`, deberíamos obtener el mensaje de error `Missing or invalid token` (lo cual es correcto, ya que no enviamos un token de acceso en nuestra solicitud).

    Para probar también el caso en que funciona correctamente, necesitamos:

    * Obtener un token de acceso. Para obtener más detalles sobre cómo hacerlo, consulte: [Get an Access Token](/es/docs/secure/tokens/access-tokens/get-access-tokens).
    * Invocar la API agregando a la solicitud un encabezado `Authorization` con el valor `Bearer ACCESS_TOKEN` (donde `ACCESS_TOKEN` es el valor del token que recuperamos en el primer paso).
  </Accordion>

  <Accordion title="3 Compruebe los permisos de la aplicación">
    En este paso, agregamos la capacidad de comprobar si la aplicación tiene permisos (o alcances) y de usar nuestro endpoint para crear una hoja de horas. En particular, queremos asegurarnos de que el token tenga el scope correcto, que es `batch:upload`.

    Para ello, usamos el paquete de Node.js `express-jwt-authz`, así que agrégalo a tu proyecto:

    ```bash lines theme={null}
    npm install express-jwt-authz --save
    ```

    Ahora, agregue una llamada a `jwtAuthz(...)` en su middleware para asegurarse de que el JWT contenga un scope determinado para poder ejecutar un endpoint concreto.

    Agregamos una dependencia adicional. La biblioteca **express-jwt-authz**, que se usa junto con express-jwt, valida el [JWT](/es/docs/secure/tokens/json-web-tokens) y garantiza que incluya los permisos correctos para llamar al endpoint deseado. Para obtener más información, consulte el [repositorio de GitHub de express-jwt-authz](https://github.com/auth0/express-jwt-authz).

    Esta es nuestra implementación de ejemplo (se omite parte del código por brevedad):

    ```javascript lines theme={null}
    // establecer dependencias - parte del código omitido
    const jwtAuthz = require('express-jwt-authz');

    // Habilitar CORS - código omitido

    // Crear middleware para verificar el JWT - código omitido

    // Habilitar el uso del middleware de análisis del cuerpo de la solicitud - código omitido

    // crear el endpoint de la API de hojas de tiempo
    app.post('/timesheets', checkJwt, jwtAuthz(['create:timesheets'], { customUserKey: 'auth' }), function(req, res){
      var timesheet = req.body;

      // Guardar la hoja de tiempo en la base de datos...

      //enviar la respuesta
      res.status(201).send(timesheet);
    })

    // iniciar el servidor de la API en localhost:8080 - código omitido
    ```

    Si invocamos nuestra API con un token que no incluye este scope, deberíamos recibir el mensaje de error Forbidden con el código de estado HTTP `403`. Puedes comprobarlo eliminando este scope de tu API.
  </Accordion>

  <Accordion title="4 Determinar la identidad del usuario">
    El middleware `express-jwt` que se utiliza para validar el JWT también asigna `req.user` con la información contenida en el JWT. Si desea usar el claim `sub` para identificar al usuario de forma única, puede usar `req.user.sub`. Para la aplicación de registro de horas, queremos usar la dirección de correo electrónico del usuario como identificador único.

    ##### Crear una Action

    Primero, [cree una nueva Action](/es/docs/customize/actions/write-your-first-action) que agregará la dirección de correo electrónico del usuario al token de acceso.

    1. Vaya a [Auth0 Dashboard > Actions > Library](https://manage.auth0.com/#/actions/library) y seleccione **Build Custom**.
    2. Introduzca un **Nombre** descriptivo para su Action (por ejemplo, `Add email to access token`), seleccione el trigger **Login / Post Login** y seleccione **Create**.
    3. Busque el editor de código de Actions, copie el siguiente código JavaScript en él y seleccione **Save Draft** para guardar los cambios:

       ```text lines theme={null}
       exports.onExecutePostLogin = async (event, api) => {
         const namespace = 'https://my-app.example.com';
         api.accessToken.setCustomClaim(`${namespace}/email`, event.user.email);
       }
       ```

           <Callout icon="file-lines" color="#0EA5E9" iconType="regular">
             El `namespace` se utiliza para garantizar que el claim tenga un nombre único que no entre en conflicto con los claims estándar de OIDC ni con los servicios internos. Para obtener más información sobre las restricciones y directrices de los claims con y sin espacio de nombres, lea [Create Custom Claims](/es/docs/secure/tokens/json-web-tokens/create-custom-claims).
           </Callout>
    4. En la barra lateral del editor de código de Actions, seleccione Test (icono de reproducción) y luego seleccione **Run** para [probar su código](/es/docs/customize/actions/test-actions).
    5. Cuando la Action esté lista para publicarse, seleccione **Deploy**.

    ##### Agregar su Action al trigger Post-Login

    A continuación, agregue la Action que creó al [trigger Post-Login](https://manage.auth0.com/#/actions/triggers/post-login/). Para obtener información sobre cómo adjuntar Actions a triggers, lea [Write Your First Action](/es/docs/customize/actions/write-your-first-action).

    ##### Recuperar el identificador único

    Por último, desde su API, recupere el valor del claim desde `req.auth`. Use ese valor como identificador único del usuario para asociarlo con las entradas de registro de horas.

    ```js lines theme={null}
    app.get('/timesheets', checkJwt, jwtAuthz(['read:timesheets'], { customUserKey: 'auth' }), function(req, res) {
      var timesheet = req.body;

      // Asociar la entrada de la hoja de horas con el usuario actual
      var userId = req.auth['https://api.exampleco.com/email'];
      timesheet.user_id = userId;

      // Guardar la hoja de horas en la base de datos...

      //enviar la respuesta
      res.status(201).send(timesheet);
    });
    ```
  </Accordion>
</AccordionGroup>
