Skip to content

Commit

Permalink
Added Telegram server code, Mini app code
Browse files Browse the repository at this point in the history
  • Loading branch information
ihsraham committed Oct 21, 2024
1 parent 016f5c5 commit 89b6dee
Show file tree
Hide file tree
Showing 13 changed files with 350 additions and 179 deletions.
1 change: 1 addition & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -102,3 +102,4 @@ tests/cases/user/prettier/prettier
.expo
build
single-factor-auth-web/sfa-web-ton-telegram-example/src/.env
single-factor-auth-web/sfa-web-ton-telegram-example/src/.env
Original file line number Diff line number Diff line change
Expand Up @@ -6,29 +6,35 @@ const path = require("path");
const { AuthDataValidator } = require("@telegram-auth/server");
const { objectToAuthDataMap } = require("@telegram-auth/server/utils");
const RateLimit = require("express-rate-limit");
const cors = require('cors');

dotenv.config();

const app = express();
app.use(express.json()); // Middleware to parse JSON requests

const { TELEGRAM_BOT_TOKEN, SERVER_URL, JWT_KEY_ID } = process.env;
const { TELEGRAM_BOT_TOKEN, JWT_KEY_ID, APP_URL } = process.env;
const privateKey = fs.readFileSync(path.resolve(__dirname, "privateKey.pem"), "utf8");

// CORS configuration
const corsOptions = {
origin: SERVER_URL || 'http://localhost:3000', // Use the SERVER_URL from .env or fallback to localhost during dev
credentials: true, // This allows cookies/credentials to be sent across domains
methods: ['GET', 'POST', 'PUT', 'DELETE', 'OPTIONS'], // Allowed HTTP methods
allowedHeaders: ['Origin', 'X-Requested-With', 'Content-Type', 'Accept', 'Authorization'], // Allowed headers
};
// Define allowed origins
const allowedOrigins = [APP_URL]; // Add more origins if needed

// Apply CORS middleware to all routes
app.use(cors(corsOptions));
// CORS configuration
app.use((req, res, next) => {
const origin = req.headers.origin;
if (allowedOrigins.includes(origin)) {
res.setHeader('Access-Control-Allow-Origin', origin); // Allow only the allowed origins
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'); // Allow credentials like cookies
}

// Handle preflight requests (OPTIONS method) for all routes
app.options('*', cors(corsOptions));
// Handle preflight requests (OPTIONS method)
if (req.method === 'OPTIONS') {
res.setHeader('Access-Control-Allow-Headers', 'Content-Type, Authorization, X-Requested-With, Accept');
return res.sendStatus(204); // Send no content status for OPTIONS requests
}
next(); // Pass control to the next middleware
});

// Rate limiter configuration
const limiter = RateLimit({
Expand All @@ -41,38 +47,72 @@ app.use(limiter);

// Helper function to generate JWT token
const generateJwtToken = (userData) => {
console.log("id", userData.id);
const payload = {
telegram_id: userData.id,
username: userData.username,
avatar_url: userData.photo_url,
avatar_url: userData.photo_url || "https://www.gravatar.com/avatar", // Default photo URL if not available
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,
exp: Math.floor(Date.now() / 1000) + 60 * 60, // Token valid for 1 hour
};
return jwt.sign(payload, privateKey, { algorithm: "RS256", keyid: JWT_KEY_ID });
};

// Validate Telegram data and generate JWT
app.post("/auth/telegram", async (req, res) => {
const { initDataRaw } = req.body;
// Route 1: Test route to check if the server is running
app.get("/test", (req, res) => {
res.json({ message: "Connection successful. Server is running!" });
});

// Route 2: Telegram authentication route
app.post("/auth/telegram", async (req, res) => {
const { initDataRaw, isMocked } = req.body;
console.log("Received initDataRaw:", initDataRaw);
console.log("isMocked:", isMocked);
if (!initDataRaw) {
return res.status(400).json({ error: "initDataRaw is required" });
}

if (isMocked) {
// Directly parse the mocked initDataRaw without validation
const data = new URLSearchParams(initDataRaw);
const user = JSON.parse(decodeURIComponent(data.get("user"))); // Decode the 'user' parameter from initDataRaw

const mockUser = {
id: user.id,
username: user.username,
photo_url: user.photo_url || "https://www.gravatar.com/avatar", // Default photo URL for mocked user
first_name: user.first_name,
};

console.log("Parsed mock user data:", mockUser);

const JWTtoken = generateJwtToken(mockUser);
return res.json({ token: JWTtoken });
}

// For real scenarios, proceed with validation
const validator = new AuthDataValidator({ botToken: TELEGRAM_BOT_TOKEN });
const data = objectToAuthDataMap(new URLSearchParams(initDataRaw));

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

// Ensure a photo URL is available or use a default one
const validatedUser = {
...user,
photo_url: user.photo_url || "https://www.gravatar.com/avatar", // Fallback photo URL if missing
};

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" });
}
});

// Start the server
app.listen(3000, () => console.log("Server ready on port 3000."));
80 changes: 79 additions & 1 deletion single-factor-auth-web/sfa-web-ton-telegram-example/src/App.css
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
/* Main Container */
.container {
width: 60%;
margin: auto;
Expand Down Expand Up @@ -28,8 +29,8 @@

.grid {
display: flex;
align-items: center;
flex-direction: column;
align-items: center;
}

.card {
Expand Down Expand Up @@ -101,3 +102,80 @@
font-size: 16px;
font-family: monospace;
}

/* Updated box sizes */
.user-info-box, .info-box {
padding: 15px;
margin: 15px 0;
border: 1px solid #ccc;
border-radius: 10px;
background-color: #f9f9f9;
box-shadow: 0 4px 8px rgba(0, 0, 0, 0.1);
width: 350px;
height: auto;
display: flex;
align-items: center;
justify-content: center;
text-align: center;
cursor: pointer;
transition: background-color 0.3s ease;
}

.info-box:hover {
background-color: #eef;
}

.loader-container {
display: flex;
justify-content: center;
align-items: center;
height: 100%;
}

/* Ellipsis for long strings */
p {
white-space: nowrap;
overflow: hidden;
text-overflow: ellipsis;
max-width: 100%; /* Ensure ellipsis kicks in at full box width */
}

/* Adjust alignment and spacing of ID and Logo */
.id-with-logo {
display: inline-flex;
align-items: center; /* Vertically align the text and logo */
gap: 4px; /* Reduce the gap between the ID and the logo */
margin-bottom: 0; /* Remove unnecessary margin below the ID */
max-width: fit-content; /* Adjust width to fit content only */
}

/* Style the logo */
.telegram-logo {
width: 20px;
height: 20px;
margin-left: 4px; /* Minimal space between the ID and logo */
}

/* Adjust overall spacing for user-info-box */
.user-info-box {
padding: 15px;
margin: 10px 0; /* Adjust the margin for better spacing */
border: 1px solid #ccc;
border-radius: 10px;
background-color: #f9f9f9;
box-shadow: 0 4px 8px rgba(0, 0, 0, 0.1);
width: 350px;
height: auto;
display: flex;
align-items: center;
text-align: center;
cursor: pointer;
transition: background-color 0.3s ease;
}

.user-avatar {
width: 50px;
height: 50px;
border-radius: 50%;
margin-right: 10px;
}
Loading

0 comments on commit 89b6dee

Please sign in to comment.