diff --git a/single-factor-auth-web/sfa-web-ton-telegram-example/.env b/single-factor-auth-web/sfa-web-ton-telegram-example/.env index 3048f226..75a4abe6 100644 --- a/single-factor-auth-web/sfa-web-ton-telegram-example/.env +++ b/single-factor-auth-web/sfa-web-ton-telegram-example/.env @@ -1,4 +1,4 @@ -VITE_SERVER_URL="" # server-url +VITE_SERVER_URL="http://localhost:3000" # server-url VITE_W3A_VERIFIER_NAME="" # w3a-verifier-name VITE_W3A_CLIENT_ID="" # w3a-client-id from web3auth dashboard REACT_APP_SERVER_URL="http://localhost:3000" # server-url diff --git a/single-factor-auth-web/sfa-web-ton-telegram-example/package-lock.json b/single-factor-auth-web/sfa-web-ton-telegram-example/package-lock.json index cfa2f41f..12f2bb0c 100644 --- a/single-factor-auth-web/sfa-web-ton-telegram-example/package-lock.json +++ b/single-factor-auth-web/sfa-web-ton-telegram-example/package-lock.json @@ -24,6 +24,7 @@ "@web3auth/base": "^9.3.0", "@web3auth/base-provider": "^9.3.0", "@web3auth/single-factor-auth": "^9.2.0", + "lucide-react": "^0.454.0", "react": "^18.3.1", "react-dom": "^18.3.1", "source-map-loader": "^4.0.1", @@ -13622,6 +13623,15 @@ "yallist": "^3.0.2" } }, + "node_modules/lucide-react": { + "version": "0.454.0", + "resolved": "https://registry.npmjs.org/lucide-react/-/lucide-react-0.454.0.tgz", + "integrity": "sha512-hw7zMDwykCLnEzgncEEjHeA6+45aeEzRYuKHuyRSOPkhko+J3ySGjGIzu+mmMfDFG1vazHepMaYFYHbTFAZAAQ==", + "license": "ISC", + "peerDependencies": { + "react": "^16.5.1 || ^17.0.0 || ^18.0.0 || ^19.0.0-rc" + } + }, "node_modules/lz-string": { "version": "1.5.0", "resolved": "https://registry.npmjs.org/lz-string/-/lz-string-1.5.0.tgz", diff --git a/single-factor-auth-web/sfa-web-ton-telegram-example/package.json b/single-factor-auth-web/sfa-web-ton-telegram-example/package.json index c19db30f..40b17732 100644 --- a/single-factor-auth-web/sfa-web-ton-telegram-example/package.json +++ b/single-factor-auth-web/sfa-web-ton-telegram-example/package.json @@ -30,6 +30,7 @@ "@web3auth/base": "^9.3.0", "@web3auth/base-provider": "^9.3.0", "@web3auth/single-factor-auth": "^9.2.0", + "lucide-react": "^0.454.0", "react": "^18.3.1", "react-dom": "^18.3.1", "source-map-loader": "^4.0.1", diff --git a/single-factor-auth-web/sfa-web-ton-telegram-example/server/api/telegram.js b/single-factor-auth-web/sfa-web-ton-telegram-example/server/api/telegram.js new file mode 100644 index 00000000..fdcb3da1 --- /dev/null +++ b/single-factor-auth-web/sfa-web-ton-telegram-example/server/api/telegram.js @@ -0,0 +1,48 @@ +import { Telegraf } from "telegraf"; + +export default async function handler(req, res) { + if (req.method !== 'POST') { + return res.status(405).json({ error: 'Method not allowed' }); + } + + const bot = new Telegraf(process.env.TELEGRAM_BOT_TOKEN); + + bot.command('start', async (ctx) => { + await ctx.reply( +`Welcome to Web3Auth Telegram MiniApp! 🌟 + +Experience blockchain wallets reimagined inside Telegram! Our demo showcases integration with the TON blockchain, but that's just the beginning - the same seamless experience can be adapted for any blockchain network. + +How does it work? It's brilliantly simple: +- Your Telegram identity securely powers your Web3 wallet creation +- Access your wallet seamlessly across all your Telegram devices +- No repeated setups - your wallet is always ready when you are + +Ready to experience the future of Web3 authentication?`, + { + reply_markup: { + inline_keyboard: [[ + { + text: "Launch Web3Auth Telegram MiniApp 🚀", + web_app: { url: process.env.APP_URL } + } + ]] + } + } + ); + }); + + try { + await bot.handleUpdate(req.body); + res.status(200).json({ ok: true }); + } catch (error) { + console.error('Webhook handling error:', error); + res.status(500).json({ error: 'Failed to process update' }); + } +} + +export const config = { + api: { + bodyParser: false, + }, +}; \ No newline at end of file diff --git a/single-factor-auth-web/sfa-web-ton-telegram-example/server/package-lock.json b/single-factor-auth-web/sfa-web-ton-telegram-example/server/package-lock.json index 95680e09..8081e449 100644 --- a/single-factor-auth-web/sfa-web-ton-telegram-example/server/package-lock.json +++ b/single-factor-auth-web/sfa-web-ton-telegram-example/server/package-lock.json @@ -15,7 +15,8 @@ "express": "^4.19.2", "express-rate-limit": "^7.4.0", "express-session": "^1.18.0", - "jsonwebtoken": "^8.5.1" + "jsonwebtoken": "^8.5.1", + "telegraf": "^4.16.3" }, "devDependencies": { "@types/express": "^4.17.21", @@ -24,6 +25,12 @@ "crypto": "^1.0.1" } }, + "node_modules/@telegraf/types": { + "version": "7.1.0", + "resolved": "https://registry.npmjs.org/@telegraf/types/-/types-7.1.0.tgz", + "integrity": "sha512-kGevOIbpMcIlCDeorKGpwZmdH7kHbqlk/Yj6dEpJMKEQw5lk0KVQY0OLXaCswy8GqlIVLd5625OB+rAntP9xVw==", + "license": "MIT" + }, "node_modules/@telegram-apps/init-data-node": { "version": "1.1.1", "resolved": "https://registry.npmjs.org/@telegram-apps/init-data-node/-/init-data-node-1.1.1.tgz", @@ -172,6 +179,18 @@ "@types/send": "*" } }, + "node_modules/abort-controller": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/abort-controller/-/abort-controller-3.0.0.tgz", + "integrity": "sha512-h8lQ8tacZYnR3vNQTgibj+tODHI5/+l06Au2Pcriv/Gmet0eaj4TwWH41sO9wnHDiQsEj19q0drzdWdeAHtweg==", + "license": "MIT", + "dependencies": { + "event-target-shim": "^5.0.0" + }, + "engines": { + "node": ">=6.5" + } + }, "node_modules/accepts": { "version": "1.3.8", "resolved": "https://registry.npmjs.org/accepts/-/accepts-1.3.8.tgz", @@ -212,11 +231,33 @@ "npm": "1.2.8000 || >= 1.4.16" } }, + "node_modules/buffer-alloc": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/buffer-alloc/-/buffer-alloc-1.2.0.tgz", + "integrity": "sha512-CFsHQgjtW1UChdXgbyJGtnm+O/uLQeZdtbDo8mfUgYXCHSM1wgrVxXm6bSyrUuErEb+4sYVGCzASBRot7zyrow==", + "license": "MIT", + "dependencies": { + "buffer-alloc-unsafe": "^1.1.0", + "buffer-fill": "^1.0.0" + } + }, + "node_modules/buffer-alloc-unsafe": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/buffer-alloc-unsafe/-/buffer-alloc-unsafe-1.1.0.tgz", + "integrity": "sha512-TEM2iMIEQdJ2yjPJoSIsldnleVaAk1oW3DBVUykyOLsEsFmEc9kn+SFFPz+gl54KQNxlDnAwCXosOS9Okx2xAg==", + "license": "MIT" + }, "node_modules/buffer-equal-constant-time": { "version": "1.0.1", "resolved": "https://registry.npmjs.org/buffer-equal-constant-time/-/buffer-equal-constant-time-1.0.1.tgz", "integrity": "sha512-zRpUiDwd/xk6ADqPMATG8vc9VPrkck7T07OIx0gnjmJAnHnTVXNQG3vfvWNuiZIkwu9KrKdA1iJKfsfTVxE6NA==" }, + "node_modules/buffer-fill": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/buffer-fill/-/buffer-fill-1.0.0.tgz", + "integrity": "sha512-T7zexNBwiiaCOGDg9xNX9PBmjrubblRkENuptryuI64URkXDFum9il/JGL8Lm8wYfAXpredVXXZz7eMHilimiQ==", + "license": "MIT" + }, "node_modules/bytes": { "version": "3.1.2", "resolved": "https://registry.npmjs.org/bytes/-/bytes-3.1.2.tgz", @@ -405,6 +446,15 @@ "node": ">= 0.6" } }, + "node_modules/event-target-shim": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/event-target-shim/-/event-target-shim-5.0.1.tgz", + "integrity": "sha512-i/2XbnSz/uxRCU6+NdVJgKWDTM427+MqYbkQzD321DuCQJUqOuJKIA0IM2+W2xtYHdKOmZ4dR6fExsd4SXL+WQ==", + "license": "MIT", + "engines": { + "node": ">=6" + } + }, "node_modules/express": { "version": "4.19.2", "resolved": "https://registry.npmjs.org/express/-/express-4.19.2.tgz", @@ -770,10 +820,20 @@ "node": ">= 0.6" } }, + "node_modules/mri": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/mri/-/mri-1.2.0.tgz", + "integrity": "sha512-tzzskb3bG8LvYGFF/mDTpq3jpI6Q9wc3LEmBaghu+DdCssd1FakN7Bc0hVNmEyGq1bq3RgfkCb3cmQLpNPOroA==", + "license": "MIT", + "engines": { + "node": ">=4" + } + }, "node_modules/ms": { - "version": "2.1.2", - "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.2.tgz", - "integrity": "sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w==" + "version": "2.1.3", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.3.tgz", + "integrity": "sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==", + "license": "MIT" }, "node_modules/negotiator": { "version": "0.6.3", @@ -783,6 +843,26 @@ "node": ">= 0.6" } }, + "node_modules/node-fetch": { + "version": "2.7.0", + "resolved": "https://registry.npmjs.org/node-fetch/-/node-fetch-2.7.0.tgz", + "integrity": "sha512-c4FRfUm/dbcWZ7U+1Wq0AwCyFL+3nt2bEw05wfxSz+DWpWsitgmSgYmy2dQdWyKC1694ELPqMs/YzUSNozLt8A==", + "license": "MIT", + "dependencies": { + "whatwg-url": "^5.0.0" + }, + "engines": { + "node": "4.x || >=6.0.0" + }, + "peerDependencies": { + "encoding": "^0.1.0" + }, + "peerDependenciesMeta": { + "encoding": { + "optional": true + } + } + }, "node_modules/object-assign": { "version": "4.1.1", "resolved": "https://registry.npmjs.org/object-assign/-/object-assign-4.1.1.tgz", @@ -822,6 +902,15 @@ "node": ">= 0.8" } }, + "node_modules/p-timeout": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/p-timeout/-/p-timeout-4.1.0.tgz", + "integrity": "sha512-+/wmHtzJuWii1sXn3HCuH/FTwGhrp4tmJTxSKJbfS+vkipci6osxXM5mY0jUiRzWKMTgUT8l7HFbeSwZAynqHw==", + "license": "MIT", + "engines": { + "node": ">=10" + } + }, "node_modules/parseurl": { "version": "1.3.3", "resolved": "https://registry.npmjs.org/parseurl/-/parseurl-1.3.3.tgz", @@ -910,11 +999,29 @@ } ] }, + "node_modules/safe-compare": { + "version": "1.1.4", + "resolved": "https://registry.npmjs.org/safe-compare/-/safe-compare-1.1.4.tgz", + "integrity": "sha512-b9wZ986HHCo/HbKrRpBJb2kqXMK9CEWIE1egeEvZsYn69ay3kdfl9nG3RyOcR+jInTDf7a86WQ1d4VJX7goSSQ==", + "license": "MIT", + "dependencies": { + "buffer-alloc": "^1.2.0" + } + }, "node_modules/safer-buffer": { "version": "2.1.2", "resolved": "https://registry.npmjs.org/safer-buffer/-/safer-buffer-2.1.2.tgz", "integrity": "sha512-YZo3K82SD7Riyi0E1EQPojLz7kpepnSQI9IyPbHHg1XXXevb5dJI7tpyN2ADxGcQbHG7vcyRHk0cbwqcQriUtg==" }, + "node_modules/sandwich-stream": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/sandwich-stream/-/sandwich-stream-2.0.2.tgz", + "integrity": "sha512-jLYV0DORrzY3xaz/S9ydJL6Iz7essZeAfnAavsJ+zsJGZ1MOnsS52yRjU3uF3pJa/lla7+wisp//fxOwOH8SKQ==", + "license": "Apache-2.0", + "engines": { + "node": ">= 0.10" + } + }, "node_modules/send": { "version": "0.18.0", "resolved": "https://registry.npmjs.org/send/-/send-0.18.0.tgz", @@ -938,11 +1045,6 @@ "node": ">= 0.8.0" } }, - "node_modules/send/node_modules/ms": { - "version": "2.1.3", - "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.3.tgz", - "integrity": "sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==" - }, "node_modules/serve-static": { "version": "1.15.0", "resolved": "https://registry.npmjs.org/serve-static/-/serve-static-1.15.0.tgz", @@ -1003,6 +1105,45 @@ "node": ">= 0.8" } }, + "node_modules/telegraf": { + "version": "4.16.3", + "resolved": "https://registry.npmjs.org/telegraf/-/telegraf-4.16.3.tgz", + "integrity": "sha512-yjEu2NwkHlXu0OARWoNhJlIjX09dRktiMQFsM678BAH/PEPVwctzL67+tvXqLCRQQvm3SDtki2saGO9hLlz68w==", + "license": "MIT", + "dependencies": { + "@telegraf/types": "^7.1.0", + "abort-controller": "^3.0.0", + "debug": "^4.3.4", + "mri": "^1.2.0", + "node-fetch": "^2.7.0", + "p-timeout": "^4.1.0", + "safe-compare": "^1.1.4", + "sandwich-stream": "^2.0.2" + }, + "bin": { + "telegraf": "lib/cli.mjs" + }, + "engines": { + "node": "^12.20.0 || >=14.13.1" + } + }, + "node_modules/telegraf/node_modules/debug": { + "version": "4.3.7", + "resolved": "https://registry.npmjs.org/debug/-/debug-4.3.7.tgz", + "integrity": "sha512-Er2nc/H7RrMXZBFCEim6TCmMk02Z8vLC2Rbi1KEBggpo0fS6l0S1nnapwmIi3yW/+GOJap1Krg4w0Hg80oCqgQ==", + "license": "MIT", + "dependencies": { + "ms": "^2.1.3" + }, + "engines": { + "node": ">=6.0" + }, + "peerDependenciesMeta": { + "supports-color": { + "optional": true + } + } + }, "node_modules/toidentifier": { "version": "1.0.1", "resolved": "https://registry.npmjs.org/toidentifier/-/toidentifier-1.0.1.tgz", @@ -1011,6 +1152,12 @@ "node": ">=0.6" } }, + "node_modules/tr46": { + "version": "0.0.3", + "resolved": "https://registry.npmjs.org/tr46/-/tr46-0.0.3.tgz", + "integrity": "sha512-N3WMsuqV66lT30CrXNbEjx4GEwlow3v6rr4mCcv6prnfwhS01rkgyFdjPNBYd9br7LpXV1+Emh01fHnq2Gdgrw==", + "license": "MIT" + }, "node_modules/type-is": { "version": "1.6.18", "resolved": "https://registry.npmjs.org/type-is/-/type-is-1.6.18.tgz", @@ -1063,6 +1210,22 @@ "engines": { "node": ">= 0.8" } + }, + "node_modules/webidl-conversions": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/webidl-conversions/-/webidl-conversions-3.0.1.tgz", + "integrity": "sha512-2JAn3z8AR6rjK8Sm8orRC0h/bcl/DqL7tRPdGZ4I1CjdF+EaMLmYxBHyXuKL849eucPFhvBoxMsflfOb8kxaeQ==", + "license": "BSD-2-Clause" + }, + "node_modules/whatwg-url": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/whatwg-url/-/whatwg-url-5.0.0.tgz", + "integrity": "sha512-saE57nupxk6v3HY35+jzBwYa0rKSy0XR8JSxZPwgLr7ys0IBzhGviA1/TUGJLmSVqs8pb9AnvICXEuOHLprYTw==", + "license": "MIT", + "dependencies": { + "tr46": "~0.0.3", + "webidl-conversions": "^3.0.0" + } } } } diff --git a/single-factor-auth-web/sfa-web-ton-telegram-example/server/package.json b/single-factor-auth-web/sfa-web-ton-telegram-example/server/package.json index fee7f598..70437b5c 100644 --- a/single-factor-auth-web/sfa-web-ton-telegram-example/server/package.json +++ b/single-factor-auth-web/sfa-web-ton-telegram-example/server/package.json @@ -7,7 +7,8 @@ "express": "^4.19.2", "express-rate-limit": "^7.4.0", "express-session": "^1.18.0", - "jsonwebtoken": "^8.5.1" + "jsonwebtoken": "^8.5.1", + "telegraf": "^4.16.3" }, "description": "", "keywords": [], diff --git a/single-factor-auth-web/sfa-web-ton-telegram-example/src/App.css b/single-factor-auth-web/sfa-web-ton-telegram-example/src/App.css index cdcd2afd..d46f818a 100644 --- a/single-factor-auth-web/sfa-web-ton-telegram-example/src/App.css +++ b/single-factor-auth-web/sfa-web-ton-telegram-example/src/App.css @@ -1,191 +1,317 @@ -/* Main Container */ -.container { - width: 60%; - margin: auto; - padding: 0; +@import url('https://fonts.googleapis.com/css2?family=Inter:wght@300;400;500;600;700;800&display=swap'); + +:root { + --bg-color: #f8faff; + --text-color: #1a1b25; + --border-color: #e0e0e0; + --hover-bg: rgba(0, 0, 0, 0.05); + --box-shadow: rgba(0, 0, 0, 0.08); + --card-bg: #ffffff; +} + +:root.dark-mode { + --bg-color: #0F1221; + --text-color: #ffffff; + --border-color: #2a2d3d; + --hover-bg: rgba(255, 255, 255, 0.1); + --box-shadow: rgba(0, 0, 0, 0.3); + --card-bg: #1a1f35; +} + +/* Base styles */ +html, body { + max-width: 100vw; + overflow-x: hidden; + margin: 0; + padding: 0; } body { - background-color: var(--bg-color, #ffffff); /* Fallback to white if not set */ - color: var(--text-color, #333333); /* Fallback to black text */ + font-family: 'Inter', sans-serif; + background: var(--bg-color); + color: var(--text-color); + min-height: 100vh; + transition: background-color 0.3s ease; } -.user-info-box, .info-box { - background-color: var(--bg-color); - color: var(--text-color); +/* Container */ +.container { + width: 100%; + max-width: min(600px, 90%); + margin: 0 auto; + padding: 16px; + min-height: 100vh; + display: flex; + flex-direction: column; + justify-content: space-between; + box-sizing: border-box; +} + +/* Header section */ +.header { + display: flex; + flex-direction: column; + align-items: center; + text-align: center; + gap: 8px; + margin-bottom: 24px; +} + +.logo-container { + position: relative; + width: 100%; + display: flex; + align-items: center; + justify-content: center; + margin-bottom: 16px; } -.main { - min-height: 100vh; - padding: 4rem 0; - flex: 1; - display: flex; - flex-direction: column; - justify-content: center; - align-items: center; +.web3auth-logo { + width: min(180px, 40%); + height: auto; + margin: 0 auto; + filter: drop-shadow(0 2px 4px var(--box-shadow)); } +.theme-toggle { + position: absolute; + right: 0; + top: 50%; + transform: translateY(-50%); + padding: 8px; + border-radius: 50%; + background: var(--card-bg); + border: 1px solid var(--border-color); + cursor: pointer; + display: flex; + align-items: center; + justify-content: center; + transition: all 0.3s ease; +} + +.theme-toggle:hover { + background-color: var(--hover-bg); +} + +.theme-toggle svg { + width: 20px; + height: 20px; + color: var(--text-color); +} + +/* Title and description */ .title { - line-height: 1.15; - font-size: 3rem; - text-align: center; - margin: 50px; + font-size: clamp(1.5rem, 4vw, 2rem); + font-weight: 800; + margin: 0; + letter-spacing: -0.5px; + background: linear-gradient(to right, #66D4F6, #4D92FF, #BB65FF); + -webkit-background-clip: text; + background-clip: text; + -webkit-text-fill-color: transparent; } -.title a { - color: #0070f3; - text-decoration: none; +.description { + font-size: clamp(0.9rem, 3vw, 1.1rem); + font-weight: 500; + line-height: 1.5; + margin: 8px 0 16px; + text-align: center; + color: var(--text-color); + opacity: 0.9; + max-width: 500px; + padding: 0 16px; } +/* Grid layout */ .grid { - display: flex; - flex-direction: column; - align-items: center; + display: flex; + flex-direction: column; + gap: 12px; + margin: 16px 0; } -.card { - margin: 0.5rem; - padding: 0.7rem; - text-align: center; - color: #0070f3; - background-color: #fafafa; - text-decoration: none; - border: 1px solid #0070f3; - border-radius: 10px; - transition: color 0.15s ease, border-color 0.15s ease; - width: 100%; +/* Info boxes */ +.user-info-box, .info-box { + padding: 16px; + margin: 0; + border: 1px solid var(--border-color); + border-radius: 12px; + background: var(--card-bg); + box-shadow: 0 4px 12px var(--box-shadow); + width: 100%; + cursor: pointer; + transition: all 0.3s ease; + box-sizing: border-box; } -.card:hover, -.card:focus, -.card:active { - cursor: pointer; - background-color: #f1f1f1; +.user-info-box { + display: flex; + align-items: center; + justify-content: flex-start; + gap: 15px; + flex-wrap: wrap; } -.footer { - display: flex; - flex: 1; - padding: 2rem 0; - border-top: 1px solid #eaeaea; - justify-content: center; - align-items: center; - margin-top: 10rem; +.info-box:hover { + background-color: var(--hover-bg); + border-color: var(--text-color); + transform: translateY(-2px); } -.footer a { - display: flex; - justify-content: center; - align-items: center; - flex-grow: 1; +/* User info */ +.user-info { + display: flex; + flex-direction: column; + gap: 8px; } -.logo { - height: 1.5rem; - margin-left: 0.5rem; +.user-info p { + margin: 0; + display: flex; + align-items: center; + gap: 8px; } -@media (max-width: 1200px) { - .container { - width: 100%; - } +.user-info strong { + color: var(--text-color); } -.flex-container { - display: flex; - flex-flow: row wrap; +.id-with-logo { + display: flex; + align-items: center; + gap: 8px; } -.flex-container > div { - width: 100px; - margin: 10px; - text-align: center; - line-height: 75px; - font-size: 30px; +.telegram-logo { + width: 20px; + height: 20px; } -#console { - width: 100%; - height: 100%; - overflow: auto; - word-wrap: break-word; - font-size: 16px; - font-family: monospace; +.user-avatar { + width: 50px; + height: 50px; + border-radius: 50%; + object-fit: cover; } -/* 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 content */ +.info-box-content { + display: flex; + align-items: flex-start; + justify-content: space-between; + gap: 10px; + width: 100%; } -.info-box:hover { - background-color: #eef; +.info-box-content p { + margin: 0; + flex: 1; + text-align: left; + min-width: 0; } -.loader-container { - display: flex; - justify-content: center; - align-items: center; - height: 100%; +.info-box-content strong { + display: block; + margin-bottom: 5px; } -/* Ellipsis for long strings */ -p { - white-space: nowrap; - overflow: hidden; - text-overflow: ellipsis; - max-width: 100%; /* Ensure ellipsis kicks in at full box width */ +.ellipsed-text { + display: block; + width: 100%; + word-wrap: break-word; + word-break: break-all; + font-family: monospace; + font-size: clamp(0.75rem, 2.5vw, 0.9rem); + text-align: left; } -/* 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 */ +/* Copy icon */ +.copy-icon { + color: var(--text-color); + opacity: 0.6; + transition: all 0.2s ease; + flex-shrink: 0; + margin-top: 4px; } -/* Style the logo */ -.telegram-logo { - width: 20px; - height: 20px; - margin-left: 4px; /* Minimal space between the ID and logo */ +.copy-icon.success { + color: #4CAF50; + opacity: 1; } -/* 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; +.info-box:hover .copy-icon { + opacity: 1; } -.user-avatar { - width: 50px; - height: 50px; - border-radius: 50%; - margin-right: 10px; +/* Footer */ +.footer { + display: flex; + justify-content: center; + padding: 16px 0; + margin-top: 16px; + position: relative; + border-top: 1px solid var(--border-color); +} + +.learn-more-button { + padding: 12px 24px; + font-weight: 600; + text-align: center; + border-radius: 8px; + text-decoration: none; + transition: all 0.3s ease; + color: var(--text-color); + background: var(--card-bg); + border: 1px solid var(--border-color); + box-shadow: 0 2px 8px var(--box-shadow); +} + +.learn-more-button:hover { + background-color: var(--hover-bg); + border-color: var(--text-color); +} + +/* Responsive adjustments */ +@media (max-height: 700px) { + .container { + padding: 12px; + } + + .header { + gap: 6px; + margin-bottom: 16px; + } + + .logo-container { + margin-bottom: 12px; + } + + .grid { + gap: 8px; + } + + .user-info-box, .info-box { + padding: 12px; + } + + .footer { + padding: 12px 0; + margin-top: 12px; + } +} + +.breathing-outline { + border: 2px dashed rgba(0, 0, 0, 0.2); + border-radius: 8px; + animation: breathing 1.5s ease-in-out infinite; +} + +@keyframes breathing { + 0%, 100% { + border-color: rgba(0, 0, 0, 0.2); + } + 50% { + border-color: rgba(0, 0, 0, 0.5); + } } diff --git a/single-factor-auth-web/sfa-web-ton-telegram-example/src/App.tsx b/single-factor-auth-web/sfa-web-ton-telegram-example/src/App.tsx index 1b4a39f2..ff226aad 100644 --- a/single-factor-auth-web/sfa-web-ton-telegram-example/src/App.tsx +++ b/single-factor-auth-web/sfa-web-ton-telegram-example/src/App.tsx @@ -1,64 +1,74 @@ import { useEffect, useState } from "react"; -import { Web3Auth, decodeToken } from "@web3auth/single-factor-auth"; +import { Web3Auth } from "@web3auth/single-factor-auth"; import { CHAIN_NAMESPACES, WEB3AUTH_NETWORK } from "@web3auth/base"; import { CommonPrivateKeyProvider } from "@web3auth/base-provider"; import { getHttpEndpoint } from "@orbs-network/ton-access"; -import TonRPC from "./tonRpc"; // Import the TonRPC class -import { useLaunchParams } from "@telegram-apps/sdk-react"; +import TonRPC from "./tonRpc"; +import { useLaunchParams, User } from "@telegram-apps/sdk-react"; import { useTelegramMock } from "./hooks/useMockTelegramInitData"; -import Loading from "./Loading"; -import TelegramLogo from "./assets/Logo.svg"; // Assuming the logo is in the assets folder +import { Sun, Moon, Copy, Check } from "lucide-react"; +import TelegramLogo from "./assets/TelegramLogo.svg"; +import web3AuthLogoLight from "./assets/web3AuthLogoLight.svg"; +import web3AuthLogoDark from "./assets/web3AuthLogoDark.svg"; import "./App.css"; const verifier = "w3a-telegram-demo"; const clientId = "BPi5PB_UiIZ-cPz1GtV5i1I2iOSOHuimiXBI0e-Oe_u6X3oVAbCiAZOTEBtTXw4tsluTITPqA8zMsfxIKMjiqNQ"; +// Gravatar fallback URL with a default image +const getGravatarUrl = (identifier: string) => { + const hash = identifier.toLowerCase(); // Use identifier directly for simplicity + return `https://www.gravatar.com/avatar/${hash}?d=mp`; +}; + function App() { - const [isLoggingIn, setIsLoggingIn] = useState(false); const [web3authSfa, setWeb3authSfa] = useState(null); const [web3AuthInitialized, setWeb3AuthInitialized] = useState(false); - const [userData, setUserData] = useState(null); // State to hold parsed user info + const [userData, setUserData] = useState(null); const [tonAccountAddress, setTonAccountAddress] = useState(null); const [signedMessage, setSignedMessage] = useState(null); - const [isLoggedIn, setIsLoggedIn] = useState(false); + const [isDarkMode, setIsDarkMode] = useState(false); + const [copiedStates, setCopiedStates] = useState<{ [key: string]: boolean }>({ + account: false, + message: false, + }); + + const { initDataRaw, initData } = useLaunchParams() || {}; - const { initDataRaw, initData, themeParams } = useLaunchParams() || {}; + useTelegramMock(); - useTelegramMock(); // Initialize the Telegram mock data + const toggleDarkMode = () => { + setIsDarkMode(!isDarkMode); + }; useEffect(() => { - if (themeParams) { - const bgColor = themeParams.bg_color || "#ffffff"; - const textColor = themeParams.text_color || "#333333"; - document.documentElement.style.setProperty("--bg-color", bgColor); - document.documentElement.style.setProperty("--text-color", textColor); + if (isDarkMode) { + document.documentElement.classList.add("dark-mode"); + document.body.classList.add("dark-mode"); + } else { + document.documentElement.classList.remove("dark-mode"); + document.body.classList.remove("dark-mode"); } - }, []); + }, [isDarkMode]); useEffect(() => { const initializeWeb3Auth = async () => { try { - console.log("Fetching TON Testnet RPC endpoint..."); const testnetRpc = await getHttpEndpoint({ network: "testnet", protocol: "json-rpc", }); - console.log("TON Testnet RPC endpoint: ", testnetRpc); - const chainConfig = { chainNamespace: CHAIN_NAMESPACES.OTHER, chainId: "testnet", rpcTarget: testnetRpc, displayName: "TON Testnet", - blockExplorerUrl: "https://testnet.tonscan.org", ticker: "TON", tickerName: "Toncoin", }; - const privateKeyProvider = new CommonPrivateKeyProvider({ - config: { chainConfig }, - }); + const privateKeyProvider = new CommonPrivateKeyProvider({ config: { chainConfig } }); const web3authInstance = new Web3Auth({ clientId, @@ -68,73 +78,50 @@ function App() { }); setWeb3authSfa(web3authInstance); - - console.log("Initializing Web3Auth..."); - await web3authInstance.init(); // Ensure Web3Auth is initialized - console.log("Web3Auth initialized."); - + await web3authInstance.init(); setWeb3AuthInitialized(true); } catch (error) { - console.error("Error fetching TON Testnet RPC endpoint: ", error); + console.error("Error initializing Web3Auth:", error); } }; initializeWeb3Auth(); }, []); + // Set user data directly from initData if available + useEffect(() => { + if (initData && initData.user) { + setUserData(initData.user); + } + }, [initData]); + useEffect(() => { const connectWeb3Auth = async () => { if (web3authSfa && web3AuthInitialized && initDataRaw) { - setIsLoggingIn(true); try { - console.log("Checking Web3Auth connection status..."); - if (web3authSfa.status === "connected") { await web3authSfa.logout(); - console.log("Logged out successfully."); } - if (web3authSfa.status === "not_ready") { - await web3authSfa.init(); - console.log("Web3Auth initialized."); - } - console.log("initData: ", initData); - console.log("initData.user: ", initData.user); - console.log("initData.user.photoUrl: ", initData.user.photoUrl); - - const idToken = await getIdTokenFromServer(initDataRaw, initData.user.photoUrl); // Pass photoUrl - if (!idToken) { - console.error("No ID token found."); - setIsLoggingIn(false); - return; - } - - const { payload } = decodeToken(idToken); - console.log("Decoded idToken payload: ", payload); + const idToken = await getIdTokenFromServer(initDataRaw, initData?.user?.photoUrl); + if (!idToken) return; await web3authSfa.connect({ verifier, - verifierId: (payload as any).sub, + verifierId: initData?.user?.id.toString(), idToken: idToken, }); - console.log("Successfully logged in."); - setUserData(payload); - setIsLoggedIn(true); - - // Initialize TonRPC and fetch the account address + // Set TON account address and signed message after connecting const tonRpc = new TonRPC(web3authSfa.provider); const tonAddress = await tonRpc.getAccounts(); - setTonAccountAddress(tonAddress); // Set the TON address dynamically + setTonAccountAddress(tonAddress); - // Sign a message and set it const messageToSign = "Hello, TON!"; const signedMsg = await tonRpc.signMessage(messageToSign); - setSignedMessage(signedMsg); // Set the signed message + setSignedMessage(signedMsg); } catch (error) { console.error("Error during Web3Auth connection:", error); - } finally { - setIsLoggingIn(false); } } }; @@ -142,92 +129,118 @@ function App() { if (web3AuthInitialized && initDataRaw) { connectWeb3Auth(); } - }, [initDataRaw, web3authSfa, web3AuthInitialized]); + }, [initDataRaw, web3authSfa, web3AuthInitialized, initData?.user?.photoUrl]); const getIdTokenFromServer = async (initDataRaw: string, photoUrl: string | undefined) => { const isMocked = !!sessionStorage.getItem("____mocked"); - const response = await fetch(`${import.meta.env.VITE_SERVER_URL}/auth/telegram`, { method: "POST", headers: { "Content-Type": "application/json", }, - body: JSON.stringify({ initDataRaw, isMocked, photoUrl }), // Send photoUrl as an additional field + body: JSON.stringify({ initDataRaw, isMocked, photoUrl }), }); - const data = await response.json(); - console.log("Received ID token from server:", data.token); return data.token; }; - const copyToClipboard = (text: string) => { - navigator.clipboard.writeText(text); - alert("Copied to clipboard!"); + const copyToClipboard = async (text: string, type: "account" | "message") => { + try { + await navigator.clipboard.writeText(text); + setCopiedStates((prev) => ({ + ...prev, + [type]: true, + })); + + setTimeout(() => { + setCopiedStates((prev) => ({ + ...prev, + [type]: false, + })); + }, 2000); + } catch (err) { + console.error("Failed to copy text:", err); + } }; - const userInfoBox = ( -
- User avatar -
-
-

- ID: {userData?.telegram_id} -

- Telegram Logo + return ( +
+
+
+ Web3Auth Logo + +
+
+

Web3Auth Telegram MiniApp

+
+
+

Seamless wallet access on any chain with Telegram. Just one click, and you're in!

-

- Username: {userData?.username} -

-

- Name: {userData?.name} -

-
- ); - const tonAccountBox = ( -
copyToClipboard(tonAccountAddress || "")}> -

- TON Account: {tonAccountAddress} -

-
- ); - - const signedMessageBox = ( -
copyToClipboard(signedMessage || "")}> -

- Signed Message: {signedMessage} -

-
- ); - - const logoutView = ( -
- -
- ); - - return ( -
-

Web3Auth TON Telegram MiniApp

- {isLoggingIn ? ( - - ) : ( -
- {isLoggedIn ? ( +
+
+ {userData ? ( <> - {userInfoBox} - {tonAccountBox} - {signedMessageBox} + User avatar +
+
+

+ ID: {userData?.id} +

+ Telegram Logo +
+

+ Username: {userData?.username} +

+

+ Name: {`${userData?.firstName} ${userData?.lastName || ""}`} +

+
) : ( - logoutView +

Loading user info...

)}
- )} + +
tonAccountAddress && copyToClipboard(tonAccountAddress, "account")} + > +
+

+ TON Account: + {tonAccountAddress || "Loading..."} +

+ {tonAccountAddress && + (copiedStates.account ? : )} +
+
+ +
signedMessage && copyToClipboard(signedMessage, "message")} + > +
+

+ Signed Message: + {signedMessage || "Loading..."} +

+ {signedMessage && (copiedStates.message ? : )} +
+
+
+
diff --git a/single-factor-auth-web/sfa-web-ton-telegram-example/src/assets/Logo.svg b/single-factor-auth-web/sfa-web-ton-telegram-example/src/assets/TelegramLogo.svg similarity index 100% rename from single-factor-auth-web/sfa-web-ton-telegram-example/src/assets/Logo.svg rename to single-factor-auth-web/sfa-web-ton-telegram-example/src/assets/TelegramLogo.svg diff --git a/single-factor-auth-web/sfa-web-ton-telegram-example/src/spinner.svg b/single-factor-auth-web/sfa-web-ton-telegram-example/src/assets/spinner.svg similarity index 100% rename from single-factor-auth-web/sfa-web-ton-telegram-example/src/spinner.svg rename to single-factor-auth-web/sfa-web-ton-telegram-example/src/assets/spinner.svg diff --git a/single-factor-auth-web/sfa-web-ton-telegram-example/src/assets/web3AuthLogoDark.png b/single-factor-auth-web/sfa-web-ton-telegram-example/src/assets/web3AuthLogoDark.png new file mode 100644 index 00000000..cd4b904f Binary files /dev/null and b/single-factor-auth-web/sfa-web-ton-telegram-example/src/assets/web3AuthLogoDark.png differ diff --git a/single-factor-auth-web/sfa-web-ton-telegram-example/src/assets/web3AuthLogoDark.svg b/single-factor-auth-web/sfa-web-ton-telegram-example/src/assets/web3AuthLogoDark.svg new file mode 100644 index 00000000..8d925fc6 --- /dev/null +++ b/single-factor-auth-web/sfa-web-ton-telegram-example/src/assets/web3AuthLogoDark.svg @@ -0,0 +1,6 @@ + + + + + + diff --git a/single-factor-auth-web/sfa-web-ton-telegram-example/src/assets/web3AuthLogoLight.png b/single-factor-auth-web/sfa-web-ton-telegram-example/src/assets/web3AuthLogoLight.png new file mode 100644 index 00000000..66788c11 Binary files /dev/null and b/single-factor-auth-web/sfa-web-ton-telegram-example/src/assets/web3AuthLogoLight.png differ diff --git a/single-factor-auth-web/sfa-web-ton-telegram-example/src/assets/web3AuthLogoLight.svg b/single-factor-auth-web/sfa-web-ton-telegram-example/src/assets/web3AuthLogoLight.svg new file mode 100644 index 00000000..d3744ebc --- /dev/null +++ b/single-factor-auth-web/sfa-web-ton-telegram-example/src/assets/web3AuthLogoLight.svg @@ -0,0 +1,6 @@ + + + + + + diff --git a/single-factor-auth-web/sfa-web-ton-telegram-example/src/components/DarkModeToggle.tsx b/single-factor-auth-web/sfa-web-ton-telegram-example/src/components/DarkModeToggle.tsx new file mode 100644 index 00000000..ca9bbab0 --- /dev/null +++ b/single-factor-auth-web/sfa-web-ton-telegram-example/src/components/DarkModeToggle.tsx @@ -0,0 +1,19 @@ +import { Sun, Moon } from 'lucide-react'; + +const DarkModeToggle = ({ isDark, onToggle }) => { + return ( + + ); +}; + +export default DarkModeToggle; \ No newline at end of file diff --git a/single-factor-auth-web/sfa-web-ton-telegram-example/src/Loading.tsx b/single-factor-auth-web/sfa-web-ton-telegram-example/src/components/Loading.tsx similarity index 63% rename from single-factor-auth-web/sfa-web-ton-telegram-example/src/Loading.tsx rename to single-factor-auth-web/sfa-web-ton-telegram-example/src/components/Loading.tsx index 2d89041d..91e3ad53 100644 --- a/single-factor-auth-web/sfa-web-ton-telegram-example/src/Loading.tsx +++ b/single-factor-auth-web/sfa-web-ton-telegram-example/src/components/Loading.tsx @@ -1,4 +1,4 @@ -import loader from "./spinner.svg"; // Tell webpack this JS file uses this image +import loader from "../assets/spinner.svg"; // Tell webpack this JS file uses this image const Loading = () => (