stuff
Some checks are pending
Backend CI / test (push) Waiting to run
Backend CI / build (push) Blocked by required conditions
Web App CI / test (push) Waiting to run
Web App CI / build (push) Blocked by required conditions
Deploy Mnemo Cards / Deploy Backend (push) Waiting to run
Deploy Mnemo Cards / Deploy Web App (push) Blocked by required conditions
Deploy Mnemo Cards / Final Verification (push) Blocked by required conditions
Some checks are pending
Backend CI / test (push) Waiting to run
Backend CI / build (push) Blocked by required conditions
Web App CI / test (push) Waiting to run
Web App CI / build (push) Blocked by required conditions
Deploy Mnemo Cards / Deploy Backend (push) Waiting to run
Deploy Mnemo Cards / Deploy Web App (push) Blocked by required conditions
Deploy Mnemo Cards / Final Verification (push) Blocked by required conditions
This commit is contained in:
parent
3afd853f15
commit
b4ad3598ee
2 changed files with 86 additions and 23 deletions
|
|
@ -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);
|
||||
// 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}');
|
||||
}
|
||||
|
||||
// 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 data');
|
||||
log('getUserId: hash not found in decoded data');
|
||||
return null;
|
||||
}
|
||||
final hash = hashMatch.group(1)!;
|
||||
log('getUserId: hash extracted: ${hash.substring(0, hash.length > 20 ? 20 : hash.length)}...');
|
||||
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;
|
||||
}
|
||||
}
|
||||
|
||||
// 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: hash extracted: ${hash.substring(0, hash.length > 20 ? 20 : hash.length)}...');
|
||||
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<String, dynamic>;
|
||||
final userId = userData['id']?.toString();
|
||||
final username = userData['username']?.toString();
|
||||
|
|
|
|||
|
|
@ -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();
|
||||
}
|
||||
|
|
|
|||
Loading…
Reference in a new issue