Skip to main content

Server-Side Setup for Telegram Mini App with Web3Auth

servertelegramauthenticationjwtweb3authWeb3Auth Team | October 24, 2024
Live Demo

Before diving into development, experience Web3Auth in action! Check out our demo Telegram Mini App:

👉 Launch Demo Mini App

Objectives​

This guide is part of a two-part series aimed at helping you build a fully functioning Telegram Mini App that uses Web3Auth for decentralized authentication. By the end of this series, you will have:

  1. Set up a backend server that can handle Telegram authentication, validate the initData, and generate JWT tokens.
  2. Integrated the Web3Auth system into the client-side app to authenticate users and retrieve wallet details (e.g., TON blockchain addresses).

By following these two guides, you will be able to securely authenticate Telegram users and allow them to interact with decentralized applications via their wallet on the TON blockchain.


Guide Breakdown​

  1. Part 1 (Current Guide): Focuses on setting up the server-side logic to validate Telegram login data and generate JWT tokens.

    • This will include setting up an Express server, handling Telegram login requests, validating initData, and generating JWT tokens for users.
    • You will also see how to mock data during development for easy testing and debugging.
  2. Part 2: Focuses on the client-side integration with Web3Auth, where you will use the JWT tokens generated in Part 1 to authenticate users and retrieve their wallet information from Web3Auth.

    • You will implement the Web3Auth SDK and handle interactions between the Telegram Mini App and Web3Auth to access TON wallet details.

Already completed this guide?

You can continue to Part 2, where we implement the client-side app and integrate Web3Auth for authentication. Go to Part 2 to continue your journey in creating a working Telegram Mini App.


Overview​

In this guide, we will set up a backend server for a Telegram Mini App using Telegram OAuth and Web3Auth for authentication. The server will validate the Telegram-provided initData and generate JWT tokens to securely authenticate users.

Telegram Mini App Flow Diagram

Above is a visual flow diagram that shows how this process works from start to finish. You can visualize the interaction between the user, the Telegram client, the server, and Web3Auth.

  • The process starts when a user opens the Telegram Mini App.
  • Telegram provides initData, which includes user data and a signature.
  • The client forwards this data to the backend server for validation.
  • Once the server validates the initData, it generates a JWT token and returns it to the client.
  • Finally, the client uses the JWT token with Web3Auth to authenticate the user and obtain wallet details.

By following this flow, you will enable users to authenticate seamlessly within the Telegram Mini App environment while allowing them access to their decentralized wallet information.


What You Will Learn:​

  1. Set up an Express server to handle Telegram Mini App authentication.
  2. Validate Telegram login data and generate JWT tokens.
  3. Use the isMocked parameter to bypass validation for local testing.

Prerequisites​

  • Basic knowledge of Node.js and Express.
  • A Telegram bot token (learn how to get one from the Telegram Bot documentation).
  • Understanding of JWT authentication.

Step 1: Install Required Packages​

npm install express jsonwebtoken @telegram-apps/init-data-node dotenv cors

Package Usage:​

  • express: Used to create and manage the server.
  • jsonwebtoken: Required for generating and verifying JWT tokens, which are essential for authenticating users.
  • @telegram-apps/init-data-node: Provides the utility to validate Telegram's initData to ensure data integrity and prevent tampering.
  • dotenv: Allows you to securely manage environment variables like your bot token and JWT secret.
  • cors: Used to configure Cross-Origin Resource Sharing (CORS), which ensures that your app can securely communicate with other domains like your front-end app.

Step 2: Environment Variables Setup​

Create a .env file to store your sensitive data.

TELEGRAM_BOT_TOKEN=your_bot_token_here
JWT_SECRET=your_secret_key
NODE_ENV=development
APP_URL="https://your-app-url.com"
JWT_KEY_ID=your_key_id

The NODE_ENV environment variable allows you to detect whether the app is running in development or production mode.


Step 3: Set Up the Express Server​

Let’s break down the code into smaller chunks to better understand each part:

Part 1: Basic Setup​

const jwt = require("jsonwebtoken");
const fs = require("fs");
const express = require("express");
const dotenv = require("dotenv");
const path = require("path");
const { validate } = require("@telegram-apps/init-data-node");

dotenv.config();

const app = express();
app.use(express.json()); // Middleware to parse JSON requests
const { TELEGRAM_BOT_TOKEN, JWT_KEY_ID, APP_URL } = process.env;
const privateKey = fs.readFileSync(path.resolve(__dirname, "privateKey.pem"), "utf8");
Where Do I Get the PEM File?

The PEM file is a private key used for signing JSON Web Tokens (JWTs). If you're wondering how to generate or obtain this PEM file, follow the BYO JWT guide from Web3Auth. It explains how to create a custom JWT verifier and generate the necessary keys. You can find the guide here: Custom JWT Providers.

This step is crucial if you're using your own custom login providers, as you'll need to register this verifier on the Web3Auth dashboard, which we will use in Part 2 when setting up the client-side app.

This is the basic setup for your Express server. It reads your environment variables, sets up the required middleware for parsing JSON, and loads the private key that will be used for signing the JWT.


Part 2: CORS Configuration​

// Define allowed origins
const allowedOrigins = [APP_URL];

// CORS configuration
app.use((req, res, next) => {
const origin = req.headers.origin;
if (allowedOrigins.includes(origin)) {
res.setHeader("Access-Control-Allow-Origin", origin);
res.setHeader("Access-Control-Allow-Methods", "GET, POST, OPTIONS");
res.setHeader(
"Access-Control-Allow-Headers",
"Content-Type, Authorization, X-Requested-With, Accept",
);
res.setHeader("Access-Control-Allow-Credentials", "true");
}

if (req.method === "OPTIONS") {
res.setHeader(
"Access-Control-Allow-Headers",
"Content-Type, Authorization, X-Requested-With, Accept",
);
return res.sendStatus(204);
}
next();
});

Here, we are configuring CORS (Cross-Origin Resource Sharing) to only allow requests from specific origins (your app’s URL). Be sure to add your app’s URL to the allowedOrigins array.

tip

After deploying your client-side app in Part 2, don't forget to add its URL to the allowedOrigins array to ensure proper communication between the client and the server.


Part 3: Rate Limiting​

const RateLimit = require("express-rate-limit");

const limiter = RateLimit({
windowMs: 15 * 60 * 1000, // 15 minutes
max: 100, // Max 100 requests per IP
message: "Too many requests from this IP, please try again later.",
});

app.use(limiter);

This section sets up a rate limiter to prevent abuse by limiting the number of requests from the same IP address.

Why Use a Rate Limiter and CORS Configuration?
  • Rate Limiter: Protects your server from being overwhelmed by too many requests in a short period, preventing abuse or DoS attacks. In this example, it limits each IP address to 100 requests every 15 minutes.

  • CORS: The Cross-Origin Resource Sharing (CORS) settings ensure only requests from your allowed origins (like your app's domain) can access the server. Don't forget to whitelist your app's origin by adding its address to the allowedOrigins array, ensuring the right domain is permitted.


Part 4: Helper Function to Generate JWT Token​

const generateJwtToken = (userData) => {
const payload = {
telegram_id: userData.id,
username: userData.username,
avatar_url: userData.photo_url || "https://www.gravatar.com/avatar",
sub: userData.id.toString(),
name: userData.first_name,
iss: "https://api.telegram.org",
iat: Math.floor(Date.now() / 1000),
exp: Math.floor(Date.now() / 1000) + 60 * 60, // Token valid for 1 hour
};
return jwt.sign(payload, privateKey, { algorithm: "RS256", keyid: JWT_KEY_ID });
};

This function creates a JWT using the userData and signs it with your private key. This JWT will be used for user authentication.


Part 5: Routes​

// Route 1: Test route to verify server is running
app.get("/test", (req, res) => {
res.json({ message: "Connection successful. Server is running!" });
});

This route is just for testing whether the server is running successfully.

// Route 2: Telegram authentication route
app.post("/auth/telegram", async (req, res) => {
const { initDataRaw, isMocked, photoUrl } = req.body; // Extract photoUrl from request body

if (!initDataRaw) {
return res.status(400).json({ error: "initDataRaw is required" });
}

if (isMocked) {
// Handle mock data parsing
const data = new URLSearchParams(initDataRaw);
const user = JSON.parse(decodeURIComponent(data.get("user")));
const mockUser = {
id: user.id,
username: user.username,
photo_url: photoUrl || user.photo_url || "https://www.gravatar.com/avatar",
first_name: user.first_name,
};
const JWTtoken = generateJwtToken(mockUser);
return res.json({ token: JWTtoken });
}

try {
// Validate the real initDataRaw using @telegram-apps/init-data-node
validate(initDataRaw, TELEGRAM_BOT_TOKEN);

// If validation is successful, parse the data
const data = new URLSearchParams(initDataRaw);
const user = JSON.parse(decodeURIComponent(data.get("user")));
const validatedUser = {
...user,
photo_url: photoUrl || user.photo_url || "https://www.gravatar.com/avatar",
};

// Generate the JWT token
const JWTtoken = generateJwtToken(validatedUser);
res.json({ token: JWTtoken });
} catch (error) {
console.error("Error validating Telegram data:", error);
res.status(400).json({ error: "Invalid Telegram data" });
}
});

This route handles the Telegram login process. If the data is mocked, it creates a mock user and generates a JWT. If not, it validates the initData using the @telegram-apps/init-data-node package.


Step 4: Run the Server​

node index.js

You can now run your server and verify its operation by sending requests to the /test or /auth/telegram routes.


Once you deploy your app (which you'll build in Part 2), you can replace the APP_URL with your deployed app’s URL.

For custom JWT login (getting the JWKS and more), follow the guide here: Custom JWT Providers. This will also help you create a verifier on the Web3Auth dashboard, which you'll use in Part 2.


This guide sets up the server-side logic for validating Telegram logins and generating JWT tokens. To continue the journey, head over to Part 2, where we integrate Web3Auth into the client-side app for a complete solution.