Skip to main content

How to use Telegram OAuth with Web3Auth

telegramoauthtelegram login widgetauthenticationtelegram logintelegram oauthWeb3Auth Team | August 15, 2024

This guide will explain the basic steps of integrating the Telegram Login Widget aka Telegram OAuth with Web3Auth for authentication. In this guide, you will learn how to create a Telegram bot, generate a JWKS file, and establish a custom JWT verifier on Web3Auth. Finally, we will demonstrate how to implement the Telegram OAuth flow for user authentication and generate Ethereum/Solana keys using Web3Auth Plug and Play and Single Factor Auth SDKs.

TLDR;
  1. Create a Telegram bot and generate an API Token.
  2. Create a JWKS file for your public key used for JWT signing and host it on a public endpoint.
  3. Set up a Telegram custom JWT verifier for Telegram on the Web3Auth Dashboard.
  4. Implement the Telegram OAuth flow for user authentication.
  5. Use user data from Telegram to generate the JWT token.
  6. Pass the JWT token to Web3Auth for generating Ethereum/Solana keys using the Web3Auth Plug and Play and Single Factor Auth SDKs.
Important Links

How it works?

When a user clicks on the Telegram Login Widget, it initiates the Telegram OAuth flow. The widget will prompt the user to log in to their Telegram account and grant permission for the application to access their Telegram data. Once the user logs in, the Telegram API will send the user data to the specified callback URL in the Telegram Login Widget. The callback URL, with the help of the backend server, will then create a JWT token using the user data and send it to the application's frontend. The frontend will then send the JWT token to the Web3Auth SDK, which will use it to generate Ethereum or Solana keys.

Prerequisites

  1. Telegram bot with an API Token.
  2. Custom JWT verifier for Telegram

Express server for Telegram OAuth

To set up an Express server to manage the Telegram OAuth flow, you will need to create three routes:

  1. / - This route will simply check if the server is running.

  2. /login - Use this route to display the login page with the Telegram Login Widget.

  3. /callback - This route will manage the Telegram OAuth callback and generate the JWT.

In order to build the server, you will use the express, jsonwebtoken, and @telegram-auth/server packages. The generateJwtToken function will be used to create a JWT token based on the user data received from Telegram. The /callback route will validate the Telegram data using the AuthDataValidator class and generate the JWT token, which will then be sent to the frontend.

What each packages do:

  • express - A Node.js web application framework that provides a robust set of features for web and mobile applications.

  • jsonwebtoken - An implementation of JSON Web Tokens (JWT) to generate and verify JWT tokens.

  • @telegram-auth/server - A package that provides a utility class to validate the Telegram data received from the Telegram Login Widget.

The server will utilize the login.html file to display the Telegram Login Widget. This HTML file will dynamically create the Telegram Login Widget using the Telegram bot username and the callback URL.

A few important points:

  • Ensure that the private key matches the one generated during the JWKS setup phase.

  • The JWT_KEY_ID should be identical to the one specified during the JWKS setup phase and should be present in the JWKS file.

server.js
const express = require("express");
const app = express();
const { resolve } = require("path");
const jwt = require("jsonwebtoken");
const fs = require("fs");
const { AuthDataValidator } = require("@telegram-auth/server");
const { objectToAuthDataMap } = require("@telegram-auth/server/utils");

// Read the private key for JWT signing (Ensure you specify the correct path to the private key file)
const privateKey = fs.readFileSync("/path/to/privateKey.pem", "utf8");

const generateJwtToken = (userData) => {
const payload = {
telegram_id: userData.id,
username: userData.username,
avatar_url: userData.photo_url,
sub: userData.id.toString(),
name: userData.first_name,
iss: "https://api.telegram.org", // Issuer
iat: Math.floor(Date.now() / 1000),
exp: Math.floor(Date.now() / 1000) + 60 * 60, // 1 hour expiration, can lower or increase as needed
};

return jwt.sign(payload, privateKey, { algorithm: "RS256", keyid: "JWT_KEY_ID" });
};

app.get("/", (req, res) => res.send("Express Server for Telegram Login to be used with Web3Auth"));

app.get("/login", (req, res) => {
res.sendFile(resolve(__dirname, "login.html"));
});

app.get("/callback", async (req, res) => {
const validator = new AuthDataValidator({ botToken: process.env.TELEGRAM_BOT_API_TOKEN }); // Use environment variable for bot token
const data = objectToAuthDataMap(req.query || {});

try {
const user = await validator.validate(data);
const JWTtoken = generateJwtToken(user);

const redirectUrl = `${process.env.FRONTEND_URL}?token=${JWTtoken}`; // Use environment variable for frontend URL
res.redirect(redirectUrl);
} catch (error) {
console.error("Error validating Telegram data:", error);
res.status(400).send("Invalid Telegram data");
}
});

// Port 3000 or environment variable for port if deployed on a different service
const PORT = process.env.PORT || 3000;
app.listen(PORT, () => console.log(`Server ready on port ${PORT}.`));

module.exports = app;

Please note that the Telegram OAuth flow will not work in localhost. To test the Telegram OAuth flow, you'll need to deploy the server to a public endpoint. Alternatively, you can use a service like ngrok to create a public URL for your localhost.

For your convenience, we have deployed the server to Vercel. You can find the server code here and the live demo here.

Edit Telegram Bot to set the domain

To ensure that the Telegram Login Widget works correctly, you will need to set the domain of your server.

Check this guide on editing the Telegram Bot at https://core.telegram.org/bots/features#edit-bots to set the domain.

  1. Simply select your Bot from the BotFather and click on the Bot Settings option.

  2. Then, click on the Domain option, and click on Set Domain to enter the domain of your server.

  3. You will see a message saying Success! Domain updated.

    Setting Domain in a Telegram Bot

Using Telegram Login with Web3Auth

The Telegram OAuth server mentioned above will authenticate users and generate a JWT token. This token can be utilized with Web3Auth's Plug and Play SDK or Single Factor Auth SDK. In the upcoming sections, we will illustrate how to use the Web3Auth SDKs with the JWT token.

Web3Auth Plug and Play SDK

To use the Web3Auth Plug and Play SDK with the Telegram OAuth flow, you'll need to first configure Web3auth based on your project and Telegram verifier. Then, initiate the login by redirecting the user to the /login route of your Express server. After the user logs in and is redirected back to the frontend, you can extract the JWT token from the URL and pass it to the Web3Auth SDK to initiate the login process with Web3Auth.

Set up the Web3Auth PnP SDK

Install the PnP packages and then import it and configure the Web3Auth PnP No Modal SDK with the Telegram verifier.

import { Web3AuthNoModal } from "@web3auth/no-modal";
import { EthereumPrivateKeyProvider } from "@web3auth/ethereum-provider";
import {
WALLET_ADAPTERS,
CHAIN_NAMESPACES,
IProvider,
UX_MODE,
WEB3AUTH_NETWORK,
} from "@web3auth/base";
import { AuthAdapter } from "@web3auth/auth-adapter";

const clientId =
"BPi5PB_UiIZ-cPz1GtV5i1I2iOSOHuimiXBI0e-Oe_u6X3oVAbCiAZOTEBtTXw4tsluTITPqA8zMsfxIKMjiqNQ"; // get from https://dashboard.web3auth.io

const chainConfig = {
chainNamespace: CHAIN_NAMESPACES.EIP155,
chainId: "0x1", // Please use 0x1 for Mainnet
rpcTarget: "https://rpc.ankr.com/eth",
displayName: "Ethereum Mainnet",
blockExplorerUrl: "https://etherscan.io/",
ticker: "ETH",
tickerName: "Ethereum",
logo: "https://cryptologos.cc/logos/ethereum-eth-logo.png",
};

const privateKeyProvider = new EthereumPrivateKeyProvider({ config: { chainConfig } });

const web3auth = new Web3AuthNoModal({
clientId,
web3AuthNetwork: WEB3AUTH_NETWORK.SAPPHIRE_MAINNET,
privateKeyProvider,
});

const authAdapter = new AuthAdapter({
adapterSettings: {
uxMode: UX_MODE.REDIRECT,
loginConfig: {
jwt: {
verifier: "w3a-telegram-oauth-demo", // Replace with your verifier name
typeOfLogin: "jwt",
clientId,
},
},
},
});
web3auth.configureAdapter(authAdapter);

await web3auth?.init();

Initiate the Telegram OAuth flow

Redirect the user to the /login route of your Express server to initiate the Telegram OAuth flow.

const login = async () => {
window.location.href = `${SERVER_URL}/login`;
// e.g https://w3a-telegram-server.vercel.app/login
};

Extract the JWT token from the URL

Extract the JWT token from the URL and pass it to the Web3Auth SDK to initiate the login process and then reset the URL state to remove the token.

useEffect(() => {
const params = new URLSearchParams(window.location.search);
const jwtToken = params.get("token");
if (jwtToken) {
loginWithWeb3Auth(jwtToken);
window.history.replaceState({}, document.title, window.location.pathname);
}
}, []);

Login with Web3Auth using the JWT token

Use the JWT token in the connectTo() method to initiate the login process with Web3Auth with the verifierIdField set to sub or any other field based on the verifier's configuration.

const loginWithWeb3Auth = async (token: string) => {
await web3auth?.init();
const web3authProvider = await web3auth?.connectTo(WALLET_ADAPTERS.AUTH, {
loginProvider: "jwt",
extraLoginOptions: {
id_token: token,
verifierIdField: "sub", // Based on the verifier's verifierIdField
},
});
};

The above code snippets demonstrate how to use the Web3Auth Plug and Play SDK with the Telegram OAuth flow. You can find the complete example code in this GitHub repository.

Web3Auth Single Factor Auth SDK

To use the Web3Auth Single Factor Auth SDK with the Telegram OAuth flow, there could be two ways to use it:

  1. Using the Web3Auth Single Factor Auth SDK in the Node environment.
  2. Using the Web3Auth Single Factor Auth SDK in the browser environment.

Using the Web3Auth Single Factor Auth SDK in the Node environment

To use the Web3Auth Single Factor Auth SDK in the Node environment, you'll need to first configure the SFA Node SDK with the Web3Auth project and Telegram verifier details. Then, initiate the login by calling the connect method with the verifier details along with the JWT token.

Set up the Web3Auth SFA Node SDK

Install the SFA Node packages and then import it and configure the Web3Auth SFA Node SDK with the Telegram verifier.

Note: For this, you can continue to use the Express server setup from the previous section. Simply update with the below code snippet.

const { Web3Auth } = require("@web3auth/node-sdk");
const { EthereumPrivateKeyProvider } = require("@web3auth/ethereum-provider");

const privateKeyProvider = new EthereumPrivateKeyProvider({
config: {
chainConfig: {
chainId: "0x1",
rpcTarget: "https://rpc.ankr.com/eth",
displayName: "Ethereum Mainnet",
blockExplorer: "https://etherscan.io",
ticker: "ETH",
tickerName: "Ethereum",
},
},
});

const web3auth = new Web3Auth({
clientId:
"BPi5PB_UiIZ-cPz1GtV5i1I2iOSOHuimiXBI0e-Oe_u6X3oVAbCiAZOTEBtTXw4tsluTITPqA8zMsfxIKMjiqNQ", // Get your Client ID from the Web3Auth Dashboard
web3AuthNetwork: "sapphire_mainnet",
usePnPKey: false, // Setting this to true returns the same key as PnP Web SDK, By default, this SDK returns CoreKitKey.
});
web3auth.init({ provider: privateKeyProvider });

Initiate the Telegram OAuth flow

The "/login" route will start the Telegram OAuth flow. The code snippet remains the same for this part. When handling the "/callback" route, update the function to utilize the "getPrivateKey()" function for generating the Ethereum private key. "getPrivateKey()" is a helper function that uses the JWT token and Telegram user ID to produce the Ethereum private key. This is necessary because we are working in a server environment and don't have access to web3 libraries at the server level. Therefore, we are using a provider to obtain the Ethereum private and public keys. For simplicity, we are logging the Ethereum private key and public address to the express server, but you can customize this according to your needs.

Here's the updated code snippet:

app.get("/login", (req, res) => {
res.sendFile(resolve(__dirname, "login.html"));
});

app.get("/callback", async (req, res) => {
const validator = new AuthDataValidator({ botToken: process.env.TELEGRAM_BOT_API_TOKEN }); // Use environment variable for bot token
const data = objectToAuthDataMap(req.query || {});

try {
const user = await validator.validate(data);
const JWTtoken = generateJwtToken(user);
// getPriavteKey function is defined below
const ethData = await getPrivateKey(JWTtoken, user.id.toString());
// Use ethData as per your requirement
console.log("Ethereum Data:", ethData);
res.json("Ethereum Data is generated on your server.");
} catch (error) {
console.error("Error validating Telegram data:", error);
res.status(400).send("Invalid Telegram data");
}
});

Generate Ethereum private key

Use the provider to generate the Ethereum private key using the JWT token and the Telegram user ID.

const getPrivateKey = async (idToken, verifierId) => {
const web3authNodeprovider = await web3auth.connect({
verifier: "WEB3AUTH_VERIFIER_NAME", // Replace with your verifier name
verifierId,
idToken,
});

const ethPrivateKey = await web3authNodeprovider.request({ method: "eth_private_key" });
const ethPublicAddress = await web3authNodeprovider.request({ method: "eth_accounts" });
const ethData = {
ethPrivateKey,
ethPublicAddress,
};
return ethData;
};

The above code snippets demonstrate how to use the Web3Auth SFA Node SDK with the Telegram OAuth flow. You can find the complete example code in this GitHub repository.

Using the Web3Auth Single Factor Auth SDK in the browser environment

To use the Web3Auth Single Factor Auth SDK in the browser environment, you'll need to first configure the SFA JS SDK with the Web3Auth project and Telegram verifier details. Then, initiate the login by calling the connect method with the verifier details along with the JWT token.

Set up the Web3Auth SFA JS SDK

Install the SFA JS packages and then import it and configure the Web3Auth SFA JS SDK with the Telegram verifier in your React or any other frontend project.

import { CHAIN_NAMESPACES, WEB3AUTH_NETWORK } from "@web3auth/base";
import { Web3Auth, decodeToken } from "@web3auth/single-factor-auth";
import { EthereumPrivateKeyProvider } from "@web3auth/ethereum-provider";

const chainConfig = {
chainId: "0x1",
displayName: "Ethereum Mainnet",
chainNamespace: CHAIN_NAMESPACES.EIP155,
tickerName: "Ethereum",
ticker: "ETH",
decimals: 18,
rpcTarget: "https://rpc.ankr.com/eth",
blockExplorerUrl: "https://etherscan.io",
};

const ethereumPrivateKeyProvider = new EthereumPrivateKeyProvider({
config: { chainConfig },
});

const web3auth = new Web3Auth({
clientId:
"BPi5PB_UiIZ-cPz1GtV5i1I2iOSOHuimiXBI0e-Oe_u6X3oVAbCiAZOTEBtTXw4tsluTITPqA8zMsfxIKMjiqNQ", // Get your Client ID from the Web3Auth Dashboard
web3AuthNetwork: WEB3AUTH_NETWORK.SAPPHIRE_MAINNET,
usePnPKey: false, // Setting this to true returns the same key as PnP Web SDK, By default, this SDK returns CoreKitKey.
privateKeyProvider: ethereumPrivateKeyProvider,
});

await web3auth.init();

Initiate the Telegram OAuth flow

The /login route will initiate the Telegram OAuth flow. The /callback will handle the Telegram OAuth callback and generate the JWT token. The code snippet remains the same for both routes from the Express server setup section.

Extract the JWT token from the URL

Extract the JWT token from the URL and pass it to the Web3Auth SDK to initiate the login process and then reset the URL state to remove the token.

useEffect(() => {
const params = new URLSearchParams(window.location.search);
const jwtToken = params.get("token");
if (jwtToken) {
loginWithWeb3Auth(jwtToken);
window.history.replaceState({}, document.title, window.location.pathname);
}
}, []);

Login with Web3Auth SFA JS using the JWT token

Use the JWT token in the connect() method to initiate the login process with Web3Auth SFA JS SDK.

const loginWithWeb3Auth = async (token: string) => {
const decodedToken = decodeToken(token);
const verifierId = decodedToken.sub;
const web3authProvider = await web3auth.connect({
verifier: "WEB3AUTH_VERIFIER_NAME", // Replace with your verifier name
verifierId,
idToken: token,
});
};

The above code snippets demonstrate how to use the Web3Auth SFA JS SDK with the Telegram OAuth flow. You can find the complete example code in this GitHub repository.