From b4ad3598ee4319e1047ef31fbcfeba8b19b3d776 Mon Sep 17 00:00:00 2001 From: Dmitry Date: Wed, 17 Dec 2025 05:36:21 +0300 Subject: [PATCH] stuff --- mnemo_cards_backend/lib/user/telegram.dart | 90 ++++++++++++++----- .../lib/domain/services/auth_service.dart | 19 +++- 2 files changed, 86 insertions(+), 23 deletions(-) diff --git a/mnemo_cards_backend/lib/user/telegram.dart b/mnemo_cards_backend/lib/user/telegram.dart index 9dc38ab..0ec0df7 100644 --- a/mnemo_cards_backend/lib/user/telegram.dart +++ b/mnemo_cards_backend/lib/user/telegram.dart @@ -10,11 +10,12 @@ class TelegramUtils { /// Following the Python implementation exactly /// /// [initData] should be URL-encoded (as received from Telegram Web App) + /// [isAlreadyDecoded] if true, treats initData as already decoded bool checkValidateInitData(String hashStr, String initData, String token, - {String cStr = "WebAppData"}) { + {String cStr = "WebAppData", bool isAlreadyDecoded = false}) { try { - // Decode URL-encoded string - final decodedData = Uri.decodeComponent(initData); + // Decode URL-encoded string if needed + final decodedData = isAlreadyDecoded ? initData : Uri.decodeComponent(initData); // Split into chunks and filter out hash parameter final chunks = decodedData @@ -55,35 +56,80 @@ class TelegramUtils { Future<({String id, String? username})?> getUserId(String initialData) async { try { log('getUserId: initialData length: ${initialData.length}'); - final tgWebAppData = Uri.decodeComponent(initialData); - log('getUserId: decoded data length: ${tgWebAppData.length}'); + log('getUserId: initialData preview: ${initialData.length > 150 ? initialData.substring(0, 150) : initialData}'); - // Extract hash from the data - final hashMatch = RegExp(r'hash=([^&]+)').firstMatch(tgWebAppData); - if (hashMatch == null) { - log('getUserId: hash not found in data'); - return null; + // Check if data is a toString() representation of TelegramInitData object + // If it contains "raw: " pattern, extract the raw value + // Pattern: raw: user=...&hash=...} (may have closing brace at the end) + String? rawData; + final rawMatch = RegExp(r'raw:\s*(.+)').firstMatch(initialData); + if (rawMatch != null) { + rawData = rawMatch.group(1)?.trim(); + // Remove trailing } if present (from the closing brace of the object) + if (rawData != null && rawData.endsWith('}')) { + rawData = rawData.substring(0, rawData.length - 1); + } + log('getUserId: extracted raw data from TelegramInitData toString(), length: ${rawData?.length ?? 0}'); } - final hash = hashMatch.group(1)!; + + // Use raw data if extracted, otherwise use initialData as-is + final dataToProcess = rawData ?? initialData; + + // Try to determine if data is URL-encoded or already decoded + // If it contains % characters, it's likely URL-encoded + final isUrlEncoded = dataToProcess.contains('%'); + log('getUserId: data appears to be ${isUrlEncoded ? "URL-encoded" : "decoded"}'); + + String dataToValidate; + String hash; + String decodedData; + + if (isUrlEncoded) { + // Data is URL-encoded, extract hash from encoded string + final hashMatchEncoded = RegExp(r'hash=([^&]+)').firstMatch(dataToProcess); + if (hashMatchEncoded == null) { + log('getUserId: hash not found in URL-encoded data'); + return null; + } + final hashEncoded = hashMatchEncoded.group(1)!; + hash = Uri.decodeComponent(hashEncoded); + dataToValidate = dataToProcess; + decodedData = Uri.decodeComponent(dataToProcess); + // Validate with URL-encoded data (default behavior) + if (!checkValidateInitData(hash, dataToValidate, _botToken)) { + log('getUserId: validation failed with URL-encoded data'); + return null; + } + } else { + // Data appears to be already decoded, extract hash directly + final hashMatch = RegExp(r'hash=([^&]+)').firstMatch(dataToProcess); + if (hashMatch == null) { + log('getUserId: hash not found in decoded data'); + return null; + } + hash = hashMatch.group(1)!; + dataToValidate = dataToProcess; + decodedData = dataToProcess; + // Validate with already decoded data + if (!checkValidateInitData(hash, dataToValidate, _botToken, isAlreadyDecoded: true)) { + log('getUserId: validation failed with decoded data'); + return null; + } + } + log('getUserId: hash extracted: ${hash.substring(0, hash.length > 20 ? 20 : hash.length)}...'); - - // Validate using the new method - // Note: checkValidateInitData expects the original URL-encoded data - // (as received from Telegram Web App), so we pass initialData, not tgWebAppData - if (!checkValidateInitData(hash, initialData, _botToken)) { - log('getUserId: validation failed'); - return null; - } log('getUserId: validation passed'); - // Extract user ID from user parameter - final userMatch = RegExp(r'user=([^&]+)').firstMatch(tgWebAppData); + // Extract user ID from user parameter (use decoded data) + final userMatch = RegExp(r'user=([^&]+)').firstMatch(decodedData); if (userMatch == null) { log('getUserId: user parameter not found'); return null; } - final userJson = Uri.decodeComponent(userMatch.group(1)!); + // If data was already decoded, user value is also already decoded + final userValue = userMatch.group(1)!; + final userJson = isUrlEncoded ? Uri.decodeComponent(userValue) : userValue; final userData = jsonDecode(userJson) as Map; final userId = userData['id']?.toString(); final username = userData['username']?.toString(); diff --git a/mnemo_cards_web_v2/lib/domain/services/auth_service.dart b/mnemo_cards_web_v2/lib/domain/services/auth_service.dart index 6db6b16..108253b 100644 --- a/mnemo_cards_web_v2/lib/domain/services/auth_service.dart +++ b/mnemo_cards_web_v2/lib/domain/services/auth_service.dart @@ -44,7 +44,24 @@ class AuthService { try { // Try different ways to get init data if (webAppData.initData != null) { - initData = webAppData.initData.toString(); + // Try to get raw property directly if available (for URL-encoded string) + // Otherwise fall back to toString() + final initDataObject = webAppData.initData!; + try { + // Use dynamic access to try to get raw property + final dynamicObj = initDataObject as dynamic; + if (dynamicObj.raw != null) { + initData = dynamicObj.raw.toString(); + log('Using raw property from TelegramInitData', name: 'AuthService'); + } else { + initData = initDataObject.toString(); + log('Using toString() from TelegramInitData', name: 'AuthService'); + } + } catch (e) { + // Fallback to toString() if raw property access fails + initData = initDataObject.toString(); + log('Fell back to toString() for initData', name: 'AuthService'); + } } else if (webAppData.initDataUnsafe != null) { initData = webAppData.initDataUnsafe.toString(); }