dApp
Tutorial
Integrate Aptos Keyless accounts to create a seamless login experience. Use familiar Web2 logins, no more wallet setup!
Author: Darren

This example demonstrates how to use familiar Web2 login providers (Google, Apple etc.) to create wallets for your users.

Aptos Keyless allows your users to set up an Aptos blockchain account from their certain existing social accounts, rather than from a traditional secret key or mnemonic. In a nutshell, with Aptos Keyless, a user’s blockchain account is their social account. In the future, Aptos Keyless will support a growing number of OpenID Connect (OIDC) providers.


"use client";
import { useEffect, useState } from "react";
import { jwtDecode } from "jwt-decode";
import { getLocalEphemeralKeyPair } from "@/hooks/useEphemeralKeyPair";
import { useRouter } from "next/navigation";
import { getAptosClient } from "@/utils/aptosClient";
import { EphemeralKeyPair } from "@aptos-labs/ts-sdk";
import { useKeylessAccount } from "@/context/KeylessAccountContext";
import { toast } from "sonner";
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");
};
function CallbackPage() {
const { setKeylessAccount } = useKeylessAccount();
const { push } = useRouter();
const [progress, setProgress] = useState<number>(0);
const [hasError, setHasError] = useState<boolean>(false);
useEffect(() => {
const interval = setInterval(() => {
setProgress((currentProgress) => {
if (currentProgress >= 100) {
clearInterval(interval);
return 100;
}
return currentProgress + 1;
});
}, 50);
async function deriveAccount() {
const jwt = parseJWTFromURL(window.location.href);
if (!jwt) {
setHasError(true);
setProgress(100);
toast.error("No JWT found in URL. Please try logging in again.");
return;
}
const payload = jwtDecode<{ nonce: string }>(jwt);
const jwtNonce = payload.nonce;
const ephemeralKeyPair = getLocalEphemeralKeyPair(jwtNonce);
if (!ephemeralKeyPair) {
setHasError(true);
setProgress(100);
toast.error(
"No ephemeral key pair found for the given nonce. Please try logging in again."
);
return;
}
await createKeylessAccount(jwt, ephemeralKeyPair);
clearInterval(interval);
setProgress(100);
push("/");
}
deriveAccount();
}, []);
const createKeylessAccount = async (
jwt: string,
ephemeralKeyPair: EphemeralKeyPair
) => {
const aptosClient = getAptosClient();
const keylessAccount = await aptosClient.deriveKeylessAccount({
jwt,
ephemeralKeyPair,
});
const accountCoinsData = await aptosClient.getAccountCoinsData({
accountAddress: keylessAccount?.accountAddress.toString(),
});
// account does not exist yet -> fund it
if (accountCoinsData.length === 0) {
try {
await aptosClient.fundAccount({
accountAddress: keylessAccount.accountAddress,
amount: 200000000, // faucet 2 APT to create the account
});
} catch (error) {
console.log("Error funding account: ", error);
toast.error(
"Failed to fund account. Please try logging in again or use another account."
);
}
}
console.log("Keyless Account: ", keylessAccount.accountAddress.toString());
setKeylessAccount(keylessAccount);
};
return (
<div className="flex items-center justify-center h-screen w-screen">
<div className="nes-container is-rounded shadow-md cursor-not-allowed bg-gray-200">
<h1>Loading your blockchain account...</h1>
<br />
<progress
className={`nes-progress ${hasError ? "is-error" : "is-primary"}`}
value={progress}
max="100"
></progress>
</div>
</div>
);
}
export default CallbackPage;