> ## 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 con SPA + API

# Implementación de API en Node.js  (SPAs + 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>;
};

<div id="nodejs-api-implementation-spas-api">
  # Implementación de la API en Node.js (SPAs + API)
</div>

Este documento forma parte del escenario de arquitectura SPA + API y explica cómo implementar la API en Node.js. Consulta el escenario para obtener más información sobre la solución implementada.

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).

<div id="step-1-define-the-api-endpoints">
  ## Paso 1. Defina los endpoints de la API
</div>

Usaremos el [Express web application framework](http://expressjs.com/) para crear nuestra API de Node.js.

<div id="create-a-packagejson-file">
  ### Cree un archivo package.json
</div>

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

Puede dejar la configuración predeterminada o cambiarla según prefiera.

El archivo `package.json` de nuestro ejemplo es el 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": {
    "test": "echo \"Error: no test specified\" && exit 1"
  },
  "repository": {
    "type": "git",
    "url": "git+https://github.com/auth0-samples/auth0-pnp-timesheets.git"
  },
  "author": "Auth0",
  "license": "MIT",
  "bugs": {
    "url": "https://github.com/auth0-samples/auth0-pnp-timesheets/issues"
  },
  "homepage": "https://github.com/auth0-samples/auth0-pnp-timesheets#readme"
}
```

<div id="install-the-dependencies">
  ### Instala las dependencias
</div>

A continuación, debemos definir nuestras dependencias. Usaremos los siguientes módulos:

* **express**: Este módulo agrega el [framework web Express](https://expressjs.com/).
* **cors**: Este módulo agrega compatibilidad para habilitar [CORS](https://en.wikipedia.org/wiki/Cross-origin_resource_sharing), lo cual es necesario porque la API se llamará desde una aplicación de una sola página que se ejecuta en un dominio distinto dentro de un navegador web.
* **jwks-rsa**: Esta biblioteca obtiene claves de firma RSA desde un endpoint de JWKS (JSON Web Key Set). Con `expressJwtSecret`, podemos generar un proveedor de secretos que proporcionará la clave de firma correcta a `express-jwt` según el `kid` del encabezado del <Tooltip tip="JSON Web Token (JWT): formato estándar de ID Token (y, a menudo, formato de Token de acceso) usado para representar claim de forma segura entre dos partes." cta="Ver glosario" href="/es/docs/glossary?term=JWT">JWT</Tooltip>. 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 te permite autenticar 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 para Node.js. Extrae toda la parte del cuerpo de un flujo de solicitud entrante y la expone en `req.body` de una forma más fácil de usar. Para obtener más información y varias alternativas, consulta el repositorio de GitHub de body-parser.

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
```

<div id="implement-the-endpoints">
  ### Implementa los endpoints
</div>

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

* Importar las dependencias.
* Implementar los endpoints.
* Iniciar el servidor de la API.

Esta es nuestra 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 registros de horas
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 párrafo para ver cómo podemos corregirlo.

<div id="step-2-secure-the-api-endpoints">
  ## Paso 2. Proteger los endpoints de la API
</div>

Para validar nuestro token, usaremos la función `jwt`, proporcionada por el [middleware express-jwt](https://github.com/auth0/express-jwt#usage), y `jwks-rsa` para obtener nuestro secreto. Las bibliotecas hacen lo siguiente:

1. `express-jwt` decodificará el token y pasará la petición, el encabezado y la carga útil a `jwksRsa.expressJwtSecret`.
2. `jwks-rsa` descargará todas las claves de firma desde el endpoint JWKS y comprobará 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, pasaremos la clave de firma correcta a `express-jwt`.
3. `express-jwt` continuará con su propia lógica para validar la firma del token, la expiración, `audience` y `issuer`.

Los pasos que seguiremos en nuestro código son:

* Crear la función de middleware para validar el <Tooltip tip="Token de acceso: credencial de autorización, en forma de cadena opaca o JWT, usada para acceder a una API." cta="Ver glosario" href="/es/docs/glossary?term=Access+Token">Token de acceso</Tooltip>.
* Habilitar el uso del middleware en nuestras rutas.

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

export const codeExample = `// establecer 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 en función del kid del encabezado y de 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}', //reemplazar con la audiencia de tu API, disponible en Dashboard > APIs
  issuer: 'https://{yourDomain}/',
  algorithms: [ 'RS256' ]
});

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

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

  // Guardar el registro 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`;

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

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

Para probar también el caso en que sí funciona, debemos hacer lo siguiente:

* Obtener un Token de acceso. Para ver más información sobre cómo hacerlo, consulta: [Obtener un Token de acceso](/es/docs/get-started/architecture-scenarios/server-application-api#get-an-access-token).
* Invocar la API agregando un encabezado `Authorization` a la solicitud con el valor `Bearer ACCESS_TOKEN` (donde `ACCESS_TOKEN` es el valor del token que recuperamos en el primer paso).

<div id="step-3-check-the-application-permissions">
  ## Paso 3. Verifique los permisos de la aplicación
</div>

En este paso, añadiremos a nuestra implementación la capacidad de comprobar si la aplicación tiene los permisos (o `scope`) necesarios para usar nuestro endpoint y crear un registro de horas. En concreto, queremos asegurarnos de que el token tenga el scope correcto, que es `batch:upload`.

Para ello, utilizaremos el paquete de Node.js `express-jwt-authz`, así que añádalo a su proyecto:

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

Ahora basta con agregar una llamada a `jwtAuthz(...)` en su middleware para garantizar que el JWT contenga un scope específico y así ejecutar un endpoint determinado.

Agregaremos una dependencia adicional. La biblioteca **express-jwt-authz**, que se utiliza junto con express-jwt, valida el [JWT](/es/docs/secure/tokens/json-web-tokens) y garantiza que tenga 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 registros de horas
app.post('/timesheets', checkJwt, jwtAuthz(['create:timesheets'], { customUserKey: 'auth' }), function(req, res){
  var timesheet = req.body;

  // Guardar el registro de horas en la base de datos...

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

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

Si invocamos nuestra API con un token que no incluye este scope, deberíamos obtener el mensaje de error Forbidden con el código de estado HTTP `403`. Puede comprobarlo quitando este scope de su API.

<div id="step-4-determine-the-user-identity">
  ## Paso 4. Determinar la identidad del usuario
</div>

El middleware `express-jwt`, que se usa para validar el JWT, también asigna a `req.auth` la información contenida en el JWT. Si desea usar el claim `sub` para identificar al usuario de forma única, puede simplemente usar `req.auth.sub`.

Sin embargo, en el caso de la aplicación de registro de horas, queremos usar la dirección de correo electrónico del usuario como identificador único.

Lo primero que debemos hacer es escribir una Rule que agregue la dirección de correo electrónico del usuario al Token de acceso. Vaya a la [sección Rules](https://manage.auth0.com/#/rules%7D) del Dashboard y haga clic en el botón **Create Rule**.

Puede darle a la Rule un nombre descriptivo, por ejemplo `Add email to Access Token`, y luego usar el siguiente código para la Rule:

```javascript lines theme={null}
function (user, context, callback) {
  const namespace = 'https://api.exampleco.com/';
  context.accessToken[namespace + 'email'] = user.email;
  callback(null, user, context);
}
```

El `namespace` se usa para garantizar que el claim tenga un nombre único y no entre en conflicto con los nombres de ninguno de los claims estándar de OIDC. Sin embargo, Auth0 admite claims personalizados con y sin espacio de nombres. Para obtener más información sobre los claims personalizados, consulta [Crear claims personalizados](/es/docs/secure/tokens/json-web-tokens/create-custom-claims).

A continuación, en tu API, puedes recuperar el valor del claim desde `req.auth` y usarlo como la identidad única del usuario, que puedes asociar a las entradas del registro de horas.

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

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

  // Guardar el registro de horas en la base de datos...

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