Custom Authentication

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.

utils/web3auth.ts
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",
  },
});
[roomId]/page.tsx
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.

app/token/route.ts
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.

app/[roomId]/page.tsx
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).

Audio/Video Infrastructure designed for developers to empower them to ship simple yet powerful Audio/Video Apps.
support
company
Copyright © 2024 Graphene 01, Inc. All Rights Reserved.