Custom Authentication with Web3Auth
In this guide we will be going over how to use Web3Auth to create a custom authentication flow for your application. This guide focuses on how to handle custom authentication to join room from server-side.
Walkthrough
We will be using the Web3Auth Modal SDK (opens in a new tab) to authenticate user from server-side.
Install Web3Auth Modal SDK and Huddle01 SDK
pnpm add @web3auth/modal @web3auth/base @huddle01/react @huddle01/server-sdk
Initialize Web3Auth Modal SDK
You need to get your clientId
from Web3Auth Dashboard (opens in a new tab).
Once you have your clientId
you can create an instance of web3Auth
.
export const web3auth = new Web3Auth({
clientId: process.env.NEXT_PUBLIC_CLIENT_ID,
web3AuthNetwork: WEB3AUTH_NETWORK.MAINNET,
chainConfig: {
chainNamespace: CHAIN_NAMESPACES.EIP155,
chainId: "0x1",
rpcTarget: "https://rpc.ankr.com/eth",
},
});
import { web3auth } from "@/utils/web3auth";
useEffect(() => {
const init = async () => {
await web3auth.initModal();
};
if (!web3auth.connected) {
init();
}
}, [web3auth.connected]);
Creating API route to verify and generate access token
We will verify the user's identity by comparing the appPubKey
sent from client with the public_key
from the decoded idToken
.
If the verification is successful, we will generate an access token and send it back to the client.
import { AccessToken, Role } from "@huddle01/server-sdk/auth";
import * as jose from "jose";
export const dynamic = "force-dynamic";
export async function POST(request: Request) {
// Get roomId and appPubKey from request body
const { roomId, appPubKey } = await request.json();
// Get idToken from request header
const idToken = request.headers.get("authorization")?.split(" ")[1];
if (!idToken) {
return new Response("Unauthorized", { status: 401 });
}
const jwks = jose.createRemoteJWKSet(
new URL("https://api-auth.web3auth.io/jwks")
);
const jwtDecoded = await jose.jwtVerify(idToken, jwks, {
algorithms: ["ES256"],
});
// Verify that decoded jwt has the same appPubKey as the one sent from client
if ((jwtDecoded.payload as any).wallets[0].public_key === appPubKey) {
// Generate Access Token if verification is successful
const accessToken = new AccessToken({
apiKey: process.env.API_KEY!,
roomId: roomId as string,
role: Role.HOST,
permissions: {
admin: true,
canConsume: true,
canProduce: true,
canProduceSources: {
cam: true,
mic: true,
screen: true,
},
canRecvData: true,
canSendData: true,
canUpdateMetadata: true,
},
options: {
metadata: {
displayName: (jwtDecoded.payload as any).name,
},
},
});
const token = await accessToken.toJwt();
return new Response(token, { status: 200 });
} else {
return new Response("Unauthorized", { status: 401 });
}
}
Sending request for authentication
We will generate appPubKey
which will be used for verification from the server-side.
import { getPublicCompressed } from "@toruslabs/eccrypto";
const authenticateUser = async () => {
// Get user info from web3auth
const info = await web3auth.getUserInfo();
// Get app_scoped_key from web3auth
const app_scoped_key = (await web3auth.provider?.request({
method: "eth_private_key", // use "private_key" for other non-evm chains
})) as any;
// Convert app_scoped_key to appPubKey
const app_pub_key = getPublicCompressed(
Buffer.from(app_scoped_key.padStart(64, "0"), "hex")
).toString("hex");
// Send request to server to generate access token
const tokenResponse = await fetch(`/token?roomId=${params.roomId}`, {
method: "POST",
headers: {
"Content-Type": "application/json",
Authorization: `Bearer ${info.idToken}`,
},
body: JSON.stringify({ appPubKey: app_pub_key, roomId: params.roomId }),
});
// Get access token from server
const token = await tokenResponse.text();
// Join room with access token
if (state === "idle")
await joinRoom({
roomId: params.roomId,
token,
});
};
useEffect(() => {
const init = async () => {
await web3auth.initModal();
};
if (!web3auth.connected) {
init();
}
const handleConnected = async () => {
await authenticateUser();
};
// Listen to connected event to authenticate user
web3auth.on(ADAPTER_EVENTS.CONNECTED, handleConnected);
return () => {
web3auth.off(ADAPTER_EVENTS.CONNECTED, handleConnected);
};
}, [web3auth.connected]);
You're all set! Happy Hacking! 🎉
Thank you for following this guide till end. You can find the full source code here (opens in a new tab).