Guía de Integración Keyless
A un alto nivel, hay tres pasos a seguir para integrar Cuentas Keyless.
- 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
- Instalar el SDK de TypeScript de Aptos.
- Integrar soporte de Cuenta Keyless en tu cliente de aplicación
- Configurar el flujo
"Iniciar Sesión con [Idp]"
para tu usuario. - Instanciar la
KeylessAccount
del usuario - Firmar y enviar transacciones a través de la
KeylessAccount
.
- Configurar el flujo
Implementación de Ejemplo
Sección titulada «Implementación de Ejemplo»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.
-
Paso 1. Configurar tu integración OpenID con tu IdP
El primer paso es configurar la configuración con tu(s) IdP(s).
-
Paso 2. Instalar el SDK de TypeScript de Aptos
Ventana de terminal # Keyless es soportado en la versión 1.18.1 y superiorpnpm install @aptos-labs/ts-sdk -
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»-
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(); -
Guardar el
EphemeralKeyPair
en almacenamiento local, indexado por sunonce
.// Esto guarda el EphemeralKeyPair en almacenamiento localstoreEphemeralKeyPair(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;});-
Preparar los parámetros URL de la URL de inicio de sesión. Establecer el
redirect_uri
yclient_id
a tus valores configurados con el IdP. Establecer elnonce
al nonce delEphemeralKeyPair
del paso 1.1.const redirectUri = 'https://.../login/callback'const clientId = env.IDP_CLIENT_ID// Obtener el nonce asociado con ephemeralKeyPairconst nonce = ephemeralKeyPair.nonce -
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 comoemail
yprofile
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}` -
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»-
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 porid_token
. Extraer el JWT delwindow
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) -
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 -
Obtener el
EphemeralKeyPair
almacenado en el paso 1.2. Asegúrate de validar que el nonce coincida con el nonce decodificado y que elEphemeralKeyPair
no haya expirado.const ekp = getLocalEphemeralKeyPair();// Validar el EphemeralKeyPairif (!ekp || ekp.nonce !== jwtNonce || ekp.isExpired() ) {throw new Error("Ephemeral key pair not found or expired");} -
Instanciar la
KeylessAccount
del usuarioDependiendo del tipo de Keyless que estés usando, sigue las instrucciones abajo:
- 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,});- 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)»-
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;}); -
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»-
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,}); -
Firmar y enviar la transacción a la cadena.
const committedTxn = await aptos.signAndSubmitTransaction({ signer: keylessAccount, transaction }); -
Esperar a que la transacción sea procesada en cadena
const committedTransactionResponse = await aptos.waitForTransaction({ transactionHash: committedTxn.hash });
-