Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

InitData doesn't return correct values after page Refresh #18

Open
Raxort opened this issue Feb 28, 2024 · 19 comments
Open

InitData doesn't return correct values after page Refresh #18

Raxort opened this issue Feb 28, 2024 · 19 comments

Comments

@Raxort
Copy link

Raxort commented Feb 28, 2024

Hey! Once again I need your assistance. I am building a webapp in Nuxt3. As you know, when you open a webapp in Telegram you can click on three dots in the header part and select "Refresh" the page. But after the refresh, InitData returns a string with value "query_id" and that's all...

So I have a question:
Is this a normal behavior that InitData doesn't return correct string after page refresh?

P.S.
Anyway, if the InitData is empty it should return null or undefined. But right now it returns "query_id".

@Raxort Raxort changed the title InitData doesn't return values after page Refresh InitData doesn't return correct values after page Refresh Feb 28, 2024
@deptyped
Copy link
Owner

Hi. Works correctly for me on Telegram Android. Can you reproduce it using window.Telegram.WebApp.initData directly?

Also, please provide more information. What Telegram client and version? Can you reproduce it on other clients? Which way do you launch the web app, from a keyboard button, from an inline button, from the bot menu button, via inline mode, from a direct link?

@Raxort
Copy link
Author

Raxort commented Feb 29, 2024

Hi. Works correctly for me on Telegram Android. Can you reproduce it using window.Telegram.WebApp.initData directly?

Just tried with this code in Nuxt 3:

onMounted(() => nextTick(()=>{
  console.log('-- InitData --')
  console.log(window.Telegram.WebApp.initData)
  console.log('---')
}))

Also tried with

import { useWebApp } from 'vue-tg'

Both show the same initData result after refreshing. You can see it on screenshot below:

https://imgur.com/uX9s5iM

FYI: Setting WebApp header color and background color works fine. But initData doesn't work.

I am starting the app with Inline Button and it works fine on the first launch. The problem occurs only when I refresh the app from the "Refresh" button in the header.

@Raxort
Copy link
Author

Raxort commented Feb 29, 2024

I temporarily resolved the issue by writing initData value into cookie on first launch and using this cookie after relaunch. But I believe it's not the best solution...

@Raxort
Copy link
Author

Raxort commented Feb 29, 2024

Forgot to mention the Telegram versions:

Latest version of TG on Windows (4.15).
Latest on iPhone.

@deptyped
Copy link
Owner

Can't reproduce on Windows either 🤔
I'm trying with this code: https://codesandbox.io/p/devbox/vue-tg-18-rmhmhz

@Raxort
Copy link
Author

Raxort commented Feb 29, 2024

Ok, got you. I will be digging into the issue when I have time. Thanks for assistance!

@Raxort
Copy link
Author

Raxort commented Feb 29, 2024

Can't reproduce on Windows either 🤔 I'm trying with this code: https://codesandbox.io/p/devbox/vue-tg-18-rmhmhz

I was able to reproduce. Nuxt uses "pages" directory for creating routes.
Add "pages" directory into the project folder and add for example an empty index.vue page into "pages"

<template>
  <div>
  </div>
</template>

<script setup>
</script>

<style scoped>
</style>

Without pages directory everything works fine. But when I add this directory the Refresh stops working. I don't know why. Maybe you can find a problem.

BTW, Nuxt uses vue-router under the hood, so you don't need to install it with Nuxt3.

@deptyped
Copy link
Owner

deptyped commented Mar 1, 2024

Now I was able to reproduce the problem. I found out that something modifies the hash when the page loads. On the initial load, the hash contains the following:

#tgWebAppData=query_id%3DAAEK1wFcAAAAAArXAVwVu0Dq%26user%3D%257B%2522id%2522%253A1543624458%252C%2522first_name%2522%253A%2522Deptyped%2522%252C%2522last_name%2522%253A%2522%2522%252C%2522username%2522%253A%2522deptyped%2522%252C%2522language_code%2522%253A%2522en%2522%252C%2522allows_write_to_pm%2522%253Atrue%257D%26auth_date%3D1709286347%26hash%3D94acc4d7e17a1d33a47ec5624aef8ebec1d6664e2b37107ddcc4a3fb840f62a9&tgWebAppVersion=7.0&tgWebAppPlatform=weba&tgWebAppThemeParams=%7B%22bg_color%22%3A%22%23212121%22%2C%22text_color%22%3A%22%23ffffff%22%2C%22hint_color%22%3A%22%23aaaaaa%22%2C%22link_color%22%3A%22%238774e1%22%2C%22button_color%22%3A%22%238774e1%22%2C%22button_text_color%22%3A%22%23ffffff%22%2C%22secondary_bg_color%22%3A%22%230f0f0f%22%2C%22header_bg_color%22%3A%22%23212121%22%2C%22accent_text_color%22%3A%22%238774e1%22%2C%22section_bg_color%22%3A%22%23212121%22%2C%22section_header_text_color%22%3A%22%23aaaaaa%22%2C%22subtitle_text_color%22%3A%22%23aaaaaa%22%2C%22destructive_text_color%22%3A%22%23e53935%22%7D

Later, something (most likely Nuxt or vue-router, I'm not sure, maybe some sanitizer) changes the hash to:

#tgWebAppData=query_id=AAEK1wFcAAAAAArXAVwVu0Dq&user={%22id%22:1543624458,%22first_name%22:%22Deptyped%22,%22last_name%22:%22%22,%22username%22:%22deptyped%22,%22language_code%22:%22en%22,%22allows_write_to_pm%22:true}&auth_date=1709286347&hash=94acc4d7e17a1d33a47ec5624aef8ebec1d6664e2b37107ddcc4a3fb840f62a9&tgWebAppVersion=7.0&tgWebAppPlatform=weba&tgWebAppThemeParams={%22bg_color%22:%22#212121%22,%22text_color%22:%22#ffffff%22,%22hint_color%22:%22#aaaaaa%22,%22link_color%22:%22#8774e1%22,%22button_color%22:%22#8774e1%22,%22button_text_color%22:%22#ffffff%22,%22secondary_bg_color%22:%22#0f0f0f%22,%22header_bg_color%22:%22#212121%22,%22accent_text_color%22:%22#8774e1%22,%22section_bg_color%22:%22#212121%22,%22section_header_text_color%22:%22#aaaaaa%22,%22subtitle_text_color%22:%22#aaaaaa%22,%22destructive_text_color%22:%22#e53935%22}

After reloading the page, the modified hash remains, which breaks the parsing of hash in telegram-web-app.js and leads to this problem.

@Raxort
Copy link
Author

Raxort commented Mar 1, 2024

That's a much more complicated problem than I thought. Thanks for digging. I will think about how to fix. If I find a fix, I will comment.

@NikitaVelixJob
Copy link

Hello, any news on that?

@Raxort
Copy link
Author

Raxort commented Apr 6, 2024

Hello, any news on that?

I asked VueRouter team to disable hash encoding
vuejs/router#2155

They said they are not planning to do this.

From Vue Router perspective, this normalization is really useful to keep consistent behavior. You can however already get out of this behavior by using the Memory history (createMemoryHistory()). It seems like the appropriate history for a Telegram app. If you want to have a URL, you can always create a custom history that matches your need

So we have what we have.

@NikitaVelixJob
Copy link

Hello, any news on that?

I asked VueRouter team to disable hash encoding vuejs/router#2155

They said they are not planning to do this.

From Vue Router perspective, this normalization is really useful to keep consistent behavior. You can however already get out of this behavior by using the Memory history (createMemoryHistory()). It seems like the appropriate history for a Telegram app. If you want to have a URL, you can always create a custom history that matches your need

So we have what we have.

Eh... Thanks Raxort, appreciate. I am using this way. Could you share with me how you use it in your application?

const initDataUnsafe = useLocalStorage('initDataUnsafe', {
  auth_date: null,
  hash: null,
  can_send_after: null,
  chat: null,
  chat_instance: null,
  chat_type: null,
  query_id: null,
  receiver: null,
  start_param: null,
  user: null,
});

if (useWebApp().initDataUnsafe.query_id)
  initDataUnsafe.value = useWebApp().initDataUnsafe;

@NikitaVelixJob
Copy link

That's a much more complicated problem than I thought. Thanks for digging. I will think about how to fix. If I find a fix, I will comment.

Hello, any news on that 💚

@IAMSDR
Copy link
Contributor

IAMSDR commented May 21, 2024

Hai, i am able to solve this by changing some lines in telegram-web-app.js

// from line 88 to 102
function urlParseQueryString(queryString) {
    var params = {};
    if (!queryString.length) {
      return params;
    }
    var queryStringParams = []
    if (queryString.indexOf('&tg') < 0) {
      queryStringParams = queryString.split('&');
    }
    else {
      queryStringParams = queryString.split(/&(?=tg)/);
    }
    var i, param, paramName, paramValue;
    for (i = 0; i < queryStringParams.length; i++) {
      param = queryStringParams[i].split(/=(.*)/s);
      paramName = urlSafeDecode(param[0]);
      paramValue = param[1] == null ? null : urlSafeDecode(param[1]);
      params[paramName] = paramValue;
    }
    return params;
  }

check above function, i used regex to split the string correctly and it worked.
i know it's not a good practice but a Temporary solution :)

After editing, u can place this telegram-web-app.js in public folder and change the head script tag to

script: [
    {
      src: "/telegram-web-app.js",
      defer: true,
    },
  ],

that's it :)

@NikitaVelixJob
Copy link

WOW!
Thank you so much!

Is it actually okay to use mine twa.js script in order to build telegram web app. Doesn't it check if used code the same? Can it lead to any security issues?

@IAMSDR
Copy link
Contributor

IAMSDR commented May 21, 2024

Yes, it's okay to self-host the telegram-web-app.js, and I didn't receive any warnings or errors, so it seems it's not checking the code.
No, it didn't lead to any security issues, but it would be better to validate the webAppData as the documentation suggests.
However, it's not a complete solution :(

@miqe
Copy link

miqe commented Aug 24, 2024

Yes, it's okay to self-host the telegram-web-app.js, and I didn't receive any warnings or errors, so it seems it's not checking the code. No, it didn't lead to any security issues, but it would be better to validate the webAppData as the documentation suggests. However, it's not a complete solution :(

Still after modifying telegram-web-app.js, its not working for me. Both useWebApp().initData and window.Telegram.WebApp.initData returns blank. Any other alternative solutions?

@IlyaSemenov
Copy link
Contributor

IlyaSemenov commented Oct 14, 2024

FWIW, I ended up using this in plugins/vue-tg-hash.client.ts (with Nuxt):

export default defineNuxtPlugin(() => {
  const { hash } = location
  const router = useRouter()

  // If vue-router broke hash on init, restore it back.
  // See https://github.com/deptyped/vue-telegram/issues/18
  let needFixHash = router.currentRoute.value.hash !== hash
  if (needFixHash) {
    router.afterEach(() => {
      if (needFixHash) {
        location.hash = hash
        needFixHash = false
      }
    })
  }
})

@vsnation
Copy link

The problem is in method: urlParseQueryString

You can solve it replacing

var param = queryStringParams[i].split('=')

TO

param = queryStringParams[i].split(/=(.+)/)

To Test in Console:

Telegram.Utils.urlParseQueryString = function urlParseQueryString(queryString) {
    var params = {};
    if (!queryString.length) {
      return params;
    }
    var queryStringParams = queryString.split('&');
console.log(queryStringParams)
    var i, param, paramName, paramValue;
    for (i = 0; i < queryStringParams.length; i++) {
      param = queryStringParams[i].split(/=(.+)/);
      paramName = Telegram.Utils.urlSafeDecode(param[0]);
      paramValue = param[1] == null ? null : Telegram.Utils.urlSafeDecode(param[1]);
      params[paramName] = paramValue;
console.log(param, paramName, paramValue, params)
    }
    return params;
  }
Telegram.Utils.urlParseHashParams = function urlParseHashParams(locationHash) {
    locationHash = locationHash.replace(/^#/, '');
    var params = {};
    if (!locationHash.length) {
      return params;
    }
    if (locationHash.indexOf('=') < 0 && locationHash.indexOf('?') < 0) {
      params._path = Telegram.Utils.urlSafeDecode(locationHash);
      return params;
    }
    var qIndex = locationHash.indexOf('?');
    if (qIndex >= 0) {
      var pathParam = locationHash.substr(0, qIndex);
      params._path = Telegram.Utils.urlSafeDecode(pathParam);
      locationHash = locationHash.substr(qIndex + 1);
    }
    var query_params = Telegram.Utils.urlParseQueryString(locationHash);
    for (var k in query_params) {
      params[k] = query_params[k];
    }
    return params;
  }

Let me know if it works for you. For us it solved sessions issue.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

7 participants