Saltearse al contenido

Guía de Integración Keyless

A un alto nivel, hay tres pasos a seguir para integrar Cuentas Keyless.

  1. Configurar tu integración OpenID con tu IdP. En este paso, la dApp se registrará con el IdP de elección (ej. Google) y recibirá un client_id
  2. Instalar el SDK de TypeScript de Aptos.
  3. Integrar soporte de Cuenta Keyless en tu cliente de aplicación
    1. Configurar el flujo "Iniciar Sesión con [Idp]" para tu usuario.
    2. Instanciar la KeylessAccount del usuario
    3. Firmar y enviar transacciones a través de la KeylessAccount.

Puedes encontrar una aplicación de ejemplo demostrando integración básica de Keyless con Google en el repositorio aptos-keyless-example. Sigue las direcciones en el README para comenzar con el ejemplo. Para instrucciones más detalladas sobre keyless, por favor lee el resto de esta guía de integración.

  1. Paso 1. Configurar tu integración OpenID con tu IdP

    El primer paso es configurar la configuración con tu(s) IdP(s).

    Sigue las instrucciones aquí

  2. Paso 2. Instalar el SDK de TypeScript de Aptos

    Ventana de terminal
    # Keyless es soportado en la versión 1.18.1 y superior
    pnpm install @aptos-labs/ts-sdk
  3. Paso 3. Pasos de Integración del Cliente

    Abajo están los pasos predeterminados para que un cliente integre Cuentas Keyless

    1. Presentar al usuario un botón “Iniciar Sesión con [IdP]” en la UI

    Sección titulada «1. Presentar al usuario un botón “Iniciar Sesión con [IdP]” en la UI»
    1. En el fondo, creamos un par de claves efímeras. Almacena esto en almacenamiento local.

      import {EphemeralKeyPair} from '@aptos-labs/ts-sdk';
      const ephemeralKeyPair = EphemeralKeyPair.generate();
    2. Guardar el EphemeralKeyPair en almacenamiento local, indexado por su nonce.

      // Esto guarda el EphemeralKeyPair en almacenamiento local
      storeEphemeralKeyPair(ephemeralKeyPair);
    Implementación de ejemplo para storeEphemeralKeyPair
    /**
    * Almacenar el par de claves efímeras en localStorage.
    */
    export const storeEphemeralKeyPair = (ekp: EphemeralKeyPair): void =>
    localStorage.setItem("@aptos/ekp", encodeEphemeralKeyPair(ekp));
    /**
    * Recuperar el par de claves efímeras de localStorage si existe.
    */
    export const getLocalEphemeralKeyPair = (): EphemeralKeyPair | undefined => {
    try {
    const encodedEkp = localStorage.getItem("@aptos/ekp");
    return encodedEkp ? decodeEphemeralKeyPair(encodedEkp) : undefined;
    } catch (error) {
    console.warn(
    "Failed to decode ephemeral key pair from localStorage",
    error
    );
    return undefined;
    }
    };
    /**
    * Convertir a string los pares de claves efímeras para ser almacenados en localStorage
    */
    export const encodeEphemeralKeyPair = (ekp: EphemeralKeyPair): string =>
    JSON.stringify(ekp, (_, e) => {
    if (typeof e === "bigint") return { __type: "bigint", value: e.toString() };
    if (e instanceof Uint8Array)
    return { __type: "Uint8Array", value: Array.from(e) };
    if (e instanceof EphemeralKeyPair)
    return { __type: "EphemeralKeyPair", data: e.bcsToBytes() };
    return e;
    });
    /**
    * Parsear los pares de claves efímeras de un string
    */
    export const decodeEphemeralKeyPair = (encodedEkp: string): EphemeralKeyPair =>
    JSON.parse(encodedEkp, (_, e) => {
    if (e && e.__type === "bigint") return BigInt(e.value);
    if (e && e.__type === "Uint8Array") return new Uint8Array(e.value);
    if (e && e.__type === "EphemeralKeyPair")
    return EphemeralKeyPair.fromBytes(e.data);
    return e;
    });
    1. Preparar los parámetros URL de la URL de inicio de sesión. Establecer el redirect_uri y client_id a tus valores configurados con el IdP. Establecer el nonce al nonce del EphemeralKeyPair del paso 1.1.

      const redirectUri = 'https://.../login/callback'
      const clientId = env.IDP_CLIENT_ID
      // Obtener el nonce asociado con ephemeralKeyPair
      const nonce = ephemeralKeyPair.nonce
    2. Construir la URL de inicio de sesión para que el usuario se autentique con el IdP. Asegúrate de que el scope openid esté establecido. Otros scopes como email y profile pueden establecerse basados en las necesidades de tu app.

      const loginUrl = `https://accounts.google.com/o/oauth2/v2/auth?response_type=id_token&scope=openid+email+profile&nonce=${nonce}&redirect_uri=${redirectUri}&client_id=${clientId}`
    3. Cuando el usuario haga clic en el botón de inicio de sesión, redirigir al usuario a la loginUrl que se creó en el paso 1.4.

    2. Manejar el callback parseando el token y crear una cuenta Keyless para el usuario

    Sección titulada «2. Manejar el callback parseando el token y crear una cuenta Keyless para el usuario»
    1. Una vez que el usuario completa el flujo de inicio de sesión, será redirigido al redirect_uri establecido en el paso 1. El JWT se establecerá en la URL como un parámetro de búsqueda en un fragmento URL, indexado por id_token. Extraer el JWT del window haciendo lo siguiente:

      const parseJWTFromURL = (url: string): string | null => {
      const urlObject = new URL(url);
      const fragment = urlObject.hash.substring(1);
      const params = new URLSearchParams(fragment);
      return params.get('id_token');
      };
      // window.location.href = https://.../login/google/callback#id_token=...
      const jwt = parseJWTFromURL(window.location.href)
    2. Decodificar el JWT y extraer el valor nonce del payload.

      import { jwtDecode } from 'jwt-decode';
      const payload = jwtDecode<{ nonce: string }>(jwt);
      const jwtNonce = payload.nonce
    3. Obtener el EphemeralKeyPair almacenado en el paso 1.2. Asegúrate de validar que el nonce coincida con el nonce decodificado y que el EphemeralKeyPair no haya expirado.

      const ekp = getLocalEphemeralKeyPair();
      // Validar el EphemeralKeyPair
      if (!ekp || ekp.nonce !== jwtNonce || ekp.isExpired() ) {
      throw new Error("Ephemeral key pair not found or expired");
      }
    4. Instanciar la KeylessAccount del usuario

      Dependiendo del tipo de Keyless que estés usando, sigue las instrucciones abajo:

      1. Keyless Normal
      import {Aptos, AptosConfig, Network} from '@aptos-labs/ts-sdk';
      const aptos = new Aptos(new AptosConfig({ network: Network.DEVNET })); // Configura tu red aquí
      const keylessAccount = await aptos.deriveKeylessAccount({
      jwt,
      ephemeralKeyPair,
      });
      1. Keyless Federado
      import {Aptos, AptosConfig, Network} from '@aptos-labs/ts-sdk';
      const aptos = new Aptos(new AptosConfig({ network: Network.DEVNET })); // Configura tu red aquí
      const keylessAccount = await aptos.deriveKeylessAccount({
      jwt,
      ephemeralKeyPair,
      jwkAddress: jwkOwner.accountAddress
      });

    3. Almacenar la KeylessAccount en almacenamiento local (Opcional)

    Sección titulada «3. Almacenar la KeylessAccount en almacenamiento local (Opcional)»
    1. Después de que la cuenta ha sido derivada, almacenar la KeylessAccount en almacenamiento local. Esto permite al usuario regresar a la aplicación sin tener que re-autenticarse.

      export const storeKeylessAccount = (account: KeylessAccount): void =>
      localStorage.setItem("@aptos/account", encodeKeylessAccount(account));
      export const encodeKeylessAccount = (account: KeylessAccount): string =>
      JSON.stringify(account, (_, e) => {
      if (typeof e === "bigint") return { __type: "bigint", value: e.toString() };
      if (e instanceof Uint8Array)
      return { __type: "Uint8Array", value: Array.from(e) };
      if (e instanceof KeylessAccount)
      return { __type: "KeylessAccount", data: e.bcsToBytes() };
      return e;
      });
    2. Cuando el usuario regrese a la aplicación, recuperar la KeylessAccount de almacenamiento local y usarla para firmar transacciones.

      export const getLocalKeylessAccount = (): KeylessAccount | undefined => {
      try {
      const encodedAccount = localStorage.getItem("@aptos/account");
      return encodedAccount ? decodeKeylessAccount(encodedAccount) : undefined;
      } catch (error) {
      console.warn(
      "Failed to decode account from localStorage",
      error
      );
      return undefined;
      }
      };
      export const decodeKeylessAccount = (encodedAccount: string): KeylessAccount =>
      JSON.parse(encodedAccount, (_, e) => {
      if (e && e.__type === "bigint") return BigInt(e.value);
      if (e && e.__type === "Uint8Array") return new Uint8Array(e.value);
      if (e && e.__type === "KeylessAccount")
      return KeylessAccount.fromBytes(e.data);
      return e;
      });

    4. Enviar transacciones a la blockchain de Aptos

    Sección titulada «4. Enviar transacciones a la blockchain de Aptos»
    1. Crear la transacción que quieres enviar. Abajo hay una transacción simple de transferencia de moneda por ejemplo:

      import {Account} from '@aptos-labs/ts-sdk';
      const bob = Account.generate();
      const transaction = await aptos.transferCoinTransaction({
      sender: keylessAccount.accountAddress,
      recipient: bob.accountAddress,
      amount: 100,
      });
    2. Firmar y enviar la transacción a la cadena.

      const committedTxn = await aptos.signAndSubmitTransaction({ signer: keylessAccount, transaction });
    3. Esperar a que la transacción sea procesada en cadena

      const committedTransactionResponse = await aptos.waitForTransaction({ transactionHash: committedTxn.hash });