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

> Configuración de API y SPA para el escenario de arquitectura de SPA + API

# Configuración de API y SPA (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>;
};

En esta sección veremos cómo implementar una API para este caso.

<Callout icon="file-lines" color="#0EA5E9" iconType="regular">
  Para simplificar, la implementación se centrará únicamente en la autenticación y la autorización. Como verá en los ejemplos, la entrada del registro de horas estará codificada de forma fija y la API no persistirá esa entrada. En su lugar, simplemente devolverá parte de la información.
</Callout>

<div id="define-the-api-endpoints">
  ## Definir los endpoints de la API
</div>

Primero debemos definir los endpoints de nuestra API.

<Card title="¿Qué es un endpoint de API?">
  Un **endpoint de API** es una URL única que representa un objeto. Para interactuar con este objeto, debe hacer que su aplicación apunte a esa URL. Por ejemplo, si tuviera una API que pudiera devolver pedidos o clientes, podría configurar dos endpoints: `/orders` y `/customers`. Su aplicación interactuaría con estos endpoints mediante distintos métodos HTTP; por ejemplo, `POST /orders` podría crear un nuevo pedido o `GET /orders` podría recuperar los datos de uno o varios pedidos.
</Card>

Para esta implementación, solo definiremos 2 endpoints: uno para recuperar una lista de todos los registros de horas de un empleado y otro que permitirá a un empleado crear una nueva entrada de registro de horas.

Una solicitud `HTTP GET` al endpoint `/timesheets` permitirá que un usuario recupere sus registros de horas, y una solicitud `HTTP POST` al endpoint `/timesheets` permitirá que un usuario añada un nuevo registro de horas.

**Consulte la implementación en** [**Node.js**](/es/docs/get-started/architecture-scenarios/spa-api/api-implementation-nodejs#1-define-the-api-endpoints).

<div id="secure-the-endpoints">
  ### Protege los endpoints
</div>

Cuando una API recibe una solicitud con un bearer <Tooltip tip="Token de acceso: credencial de autorización, en forma de una cadena opaca o un JWT, utilizada para acceder a una API." cta="Ver glosario" href="/es/docs/glossary?term=Access+Token">Token de acceso</Tooltip> en el encabezado, lo primero es validar el token. Esto consiste en una serie de pasos y, si alguno de ellos falla, la solicitud debe rechazarse con el mensaje de error `Missing or invalid token` para la aplicación que realiza la llamada.

Las validaciones que debe realizar la API son:

* Comprobar que el <Tooltip tip="JSON Web Token (JWT): formato estándar de ID Token (y, a menudo, formato de Token de acceso) utilizado para representar claims de forma segura entre dos partes." cta="Ver glosario" href="/es/docs/glossary?term=JWT">JWT</Tooltip> tenga un formato válido
* Verificar la firma
* Validar los claims estándar

<Callout icon="file-lines" color="#0EA5E9" iconType="regular">
  [JWT.io](https://jwt.io/) proporciona una lista de bibliotecas que pueden realizar la mayor parte del trabajo por ti: analizar el JWT y verificar la firma y los claims.
</Callout>

Parte del proceso de validación también consiste en comprobar los permisos de la aplicación (alcances), pero esto se abordará por separado en el siguiente apartado de este documento.

Para obtener más información sobre cómo validar tokens de acceso, consulta [Validar tokens de acceso](/es/docs/secure/tokens/access-tokens/validate-access-tokens).

**Consulta la implementación en** [**Node.js**](/es/docs/get-started/architecture-scenarios/spa-api/api-implementation-nodejs#2-secure-the-api-endpoints).

<div id="check-the-applications-permissions">
  ### Comprueba los permisos de la aplicación
</div>

En este punto, ya hemos verificado que el JWT es válido. El último paso es verificar que la aplicación tenga los permisos necesarios para acceder a los recursos protegidos.

Para ello, la API debe comprobar los [alcances](/es/docs/get-started/apis/scopes) del JWT decodificado. La claim forma parte de la carga útil y contiene una lista de cadenas separadas por espacios.

**Consulta la implementación en** [**Node.js**](/es/docs/get-started/architecture-scenarios/spa-api/api-implementation-nodejs#3-check-the-client-permissions).

<div id="determine-user-identity">
  ### Determinar la identidad del usuario
</div>

Para ambos endpoints (obtener la lista de registros de horas y agregar un nuevo registro de horas), tendremos que determinar la identidad del usuario.

Al obtener la lista de registros de horas, esto nos permite asegurarnos de devolver solo los registros de horas que pertenecen al usuario que realiza la solicitud. Al agregar un nuevo registro de horas, nos permite asegurarnos de que el registro de horas quede asociado al usuario que realiza la solicitud.

Uno de los claim estándar de un JWT es el claim `sub`, que identifica a la entidad principal a la que se refiere el claim. En el caso del flujo de Implicit Grant, este claim contendrá la identidad del usuario, que será el identificador único del usuario de Auth0. Puede usarlo para asociar cualquier información en sistemas externos con un usuario en particular.

También puede usar un claim personalizado para agregar otro atributo del usuario, como su correo electrónico, al Token de acceso y usarlo para identificar al usuario de forma única.

**Consulte la implementación en** [**Node.js**](/es/docs/get-started/architecture-scenarios/spa-api/api-implementation-nodejs#4-determine-the-user-identity).

<div id="implement-the-spa">
  ## Implementa la SPA
</div>

En esta sección veremos cómo implementar una SPA para este caso.

<div id="authorize-the-user">
  ### Autorizar al usuario
</div>

Para autorizar al usuario, usaremos la [librería auth0.js](/es/docs/libraries/auth0js). Puede inicializar una nueva instancia de la aplicación de Auth0 de la siguiente manera:

export const codeExample = `var auth0 = new auth0.WebAuth({
  clientID: '{yourClientId}',
  domain: '{yourDomain}',
  responseType: 'token id_token',
  audience: 'YOUR_API_IDENTIFIER',
  redirectUri: '{https://yourApp/callback}',
  scope: 'openid profile read:timesheets create:timesheets'
});`;

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

Debes proporcionar los siguientes valores de configuración:

* **clientID**: El valor de tu <Tooltip tip="ID de cliente: Valor de identificación otorgado por Auth0 a tu recurso registrado." cta="Ver glosario" href="/es/docs/glossary?term=Client+Id">ID de cliente</Tooltip> de Auth0. Puedes obtenerlo en la sección Configuración de tu aplicación en el [Dashboard](https://manage.auth0.com/#/applications%7D).
* **domain**: El valor de tu dominio de Auth0. Puedes obtenerlo en la sección Configuración de tu aplicación en el [Dashboard](https://manage.auth0.com/#/applications%7D).
* **responseType**: Indica el flujo de autenticación que se va a usar. Para una SPA que usa el **Flujo implícito**, debes establecerlo en `token id_token`. La parte `token` hace que el flujo devuelva un Token de acceso en el fragmento de la URL, mientras que la parte `id_token` hace que el flujo también devuelva un <Tooltip tip="ID Token: Credencial destinada al propio cliente, en lugar de para acceder a un recurso." cta="Ver glosario" href="/es/docs/glossary?term=ID+Token">ID Token</Tooltip>.
* **<Tooltip tip="Audiencia: Identificador único de la audiencia de un token emitido. Se denomina aud en un token; su valor contiene el ID de una aplicación (ID de cliente) para un ID Token o de una API (Identificador de API) para un Token de acceso." cta="Ver glosario" href="/es/docs/glossary?term=audience">audience</Tooltip>**: El valor del identificador de tu API. Puedes obtenerlo en la [Configuración de tu API](https://manage.auth0.com/#/apis%7D) en el Dashboard.
* **redirectUri**: La URL a la que Auth0 debe redirigir después de que el usuario se haya autenticado.
* **scope**: Los [alcances](/es/docs/get-started/apis/scopes) que determinan la información que se devolverá en el ID Token y el Token de acceso. Un scope de `openid profile` devolverá toda la información del perfil de usuario en el ID Token. También debes solicitar los alcances necesarios para llamar a la API; en este caso, los scopes `read:timesheets create:timesheets`. Esto garantizará que el Token de acceso tenga esos alcances.

Para iniciar el flujo de autenticación, puedes llamar al método `authorize()`:

```js lines theme={null}
auth0.authorize();
```

Después de la autenticación, Auth0 redirigirá al **redirectUri** que especificaste al configurar la nueva instancia de la aplicación de Auth0. En este punto, deberás llamar al método `parseHash()`, que analiza un fragmento hash de una URL para extraer el resultado de una respuesta de autenticación de Auth0.

El contenido del objeto authResult devuelto por parseHash depende de los parámetros de autenticación que se hayan usado. Puede incluir lo siguiente:

* **idToken**: Un JWT de ID Token que contiene información del perfil de usuario
* **accessToken**: Un Token de acceso para la API, especificado por la **audience**.
* **expiresIn**: Una cadena que contiene el tiempo de expiración (en segundos) del Token de acceso.

Determina cuál es el mejor lugar para [almacenar los tokens](/es/docs/secure/security-guidance/data-security/token-storage). Si tu aplicación de página única tiene un servidor backend, los tokens deben manejarse del lado del servidor mediante el [Flujo de código de autorización](/es/docs/get-started/authentication-and-authorization-flow/authorization-code-flow) o el [Flujo de código de autorización con Proof Key for Code Exchange (PKCE)](/es/docs/get-started/authentication-and-authorization-flow/authorization-code-flow-with-pkce).

Si tienes una aplicación de página única (SPA) sin un servidor backend correspondiente, tu SPA debe solicitar nuevos tokens al iniciar sesión y almacenarlos en memoria sin persistencia. Para hacer llamadas a la API, tu SPA usará entonces la copia en memoria del token.

Para ver un ejemplo de cómo gestionar sesiones en las SPA, consulta la sección [Handle Authentication Tokens](/es/docs/quickstart/spa/vanillajs#handle-authentication-tokens) del [Quickstart de JavaScript para aplicaciones de página única](/es/docs/quickstart/spa/vanillajs).

**Consulte la implementación en** [**Angular 2**](/es/docs/get-started/architecture-scenarios/spa-api/spa-implementation-angular2#2-authorize-the-user).

<div id="get-the-user-profile">
  ### Obtener el perfil de usuario
</div>

<Card title="Extraer información del token">
  En esta sección se muestra cómo obtener la información del usuario mediante el Token de acceso y el [/userinfo endpoint](https://auth0.com/docs/api/authentication#get-user-info). Para evitar esta llamada a la API, puedes simplemente decodificar el ID Token [con una biblioteca](https://jwt.io/#libraries-io) (asegúrate de validarlo primero). Si necesitas información adicional del usuario, considera usar [la Management API](https://auth0.com/docs/api/management/v2#!/Users/get_users_by_id) desde tu backend.
</Card>

Se puede llamar al método `client.userInfo` pasando el valor devuelto de `authResult.accessToken` para obtener la información del perfil del usuario. Esto hará una solicitud al [/userinfo endpoint](https://auth0.com/docs/api/authentication#get-user-info) y devolverá el objeto `user`, que contiene la información del usuario, como en el siguiente ejemplo:

```json lines theme={null}
{
    "email_verified": "false",
    "email": "test@example.com",
    "clientID": "AAAABBBBCCCCDDDDEEEEFFFFGGGGHHHH",
    "updated_at": "2017-02-07T20:50:33.563Z",
    "name": "tester9@example.com",
    "picture": "https://gravatar.com/avatar/example.png",
    "user_id": "auth0|123456789012345678901234",
    "nickname": "tester9",
    "created_at": "2017-01-20T20:06:05.008Z",
    "sub": "auth0|123456789012345678901234"
}
```

Puede acceder a cualquiera de estas propiedades en la función callback que se pasa al llamar a la función `userInfo`:

```javascript lines theme={null}
const accessToken = authResult.accessToken;

auth0.client.userInfo(accessToken, (err, profile) => {
  if (profile) {
    // Obtener el apodo y la imagen de perfil del usuario
    var nickname = profile.nickname;
    var picture = profile.picture;
  }
});
```

**Consulte la implementación en** [**Angular 2**](/es/docs/get-started/architecture-scenarios/spa-api/spa-implementation-angular2#3-get-the-user-profile).

<div id="display-ui-elements-conditionally-based-on-scope">
  ### Mostrar elementos de la interfaz de usuario de forma condicional según el scope
</div>

Según el `scope` del usuario, puede que desee mostrar u ocultar determinados elementos de la interfaz de usuario. Para determinar el scope emitido para un usuario, deberá almacenar el scope que se solicitó inicialmente durante el proceso de autorización. Cuando se autoriza a un usuario, el `scope` también se devuelve en `authResult`.

Si el `scope` en `authResult` está vacío, significa que se concedieron todos los alcances solicitados. Si el `scope` en `authResult` no está vacío, significa que se concedió un conjunto diferente de alcances y que debe usar los de `authResult.scope`.

**Consulte la implementación en** [**Angular 2**](/es/docs/get-started/architecture-scenarios/spa-api/spa-implementation-angular2#4-display-ui-elements-conditionally-based-on-scope).

<div id="call-the-api">
  ### Llama a la API
</div>

Para acceder a recursos protegidos de tu API, debes incluir el Token de acceso del usuario autenticado en las solicitudes que envíes. Esto se logra enviando el Token de acceso en un encabezado `Authorization` con el esquema `Bearer`.

**Consulta la implementación en** [**Angular 2**](/es/docs/get-started/architecture-scenarios/spa-api/spa-implementation-angular2#5-call-the-api).

<div id="renew-the-access-token">
  ### Renovar el Token de acceso
</div>

Como medida de seguridad, se recomienda mantener corta la duración del Token de acceso de un usuario. Cuando crea una API en el <Tooltip tip="Auth0 Dashboard: el principal producto de Auth0 para configurar sus servicios." cta="Ver glosario" href="/es/docs/glossary?term=Auth0+dashboard">Auth0 Dashboard</Tooltip>, la duración predeterminada es de `7200` segundos (2 horas), aunque esto puede configurarse para cada API.

Una vez que caduca, un Token de acceso ya no puede usarse para acceder a una API. Para volver a obtener acceso, es necesario obtener un nuevo Token de acceso.

Para obtener un nuevo Token de acceso, puede repetir el flujo de autenticación utilizado para obtener el Token de acceso inicial. En una SPA, esto no es lo ideal, ya que quizá no quiera redirigir al usuario para apartarlo de su tarea actual y que vuelva a completar el flujo de autenticación.

En casos como este, puede usar la [Autenticación silenciosa](/es/docs/authenticate/login/configure-silent-authentication). La autenticación silenciosa le permite realizar un flujo de autenticación en el que Auth0 solo responde con redireccionamientos y nunca con una página de inicio de sesión. Sin embargo, esto requiere que el usuario ya haya iniciado sesión mediante [inicio de sesión único (SSO)](/es/docs/authenticate/single-sign-on).

**Consulte la implementación en** [**Angular 2**](/es/docs/get-started/architecture-scenarios/spa-api/spa-implementation-angular2#6-renew-the-access-token).
