carcabot / tiktok-signature

Generate tiktok signature token using node
733 stars 285 forks source link

Example for module usage for a user-specific signed url #142

Closed pthieu closed 2 years ago

pthieu commented 2 years ago

Is your feature request related to a problem? Please describe. Awesome work here. I'm just a bit confused about the usage for the module and think an example would help me and maybe some others who are checking out the repo.

An example of how to sign a user-specific URL and then invoke that URL.

Describe the solution you'd like I want to call the https://m.tiktok.com/api/post/item_list API and retrieve a list of videos from a specific user (say my own user). I would like example code of how to insert my user id into the payload to be signed.

Then an example on how to take the response payload to construct the signed URL to make the actual API call.

This would be similar to the trending list example, but would provide extra steps on how to insert the user id.

carcabot commented 2 years ago

Hi there,

Here's a working example of what you are trying to achieve.

dynamicParams should be changed , in your case you have to change secUid with your user ID.

pthieu commented 2 years ago

@carcabot that makes sense. I know I can get secUid in DevTools if I go to TikTok, but is there an easier way?

Also, are you open to PRs? I'm interested in creating a JS example in the example folder so it shouldn't affect functionality.

carcabot commented 2 years ago

secUid should be available on any profile page source, or you can use a 3rd party library to gather user data easily.

Feel free to submit any PR that can help the community.

Greetings.

pthieu commented 2 years ago

I found an easy way to get it for the time being: https://t.tiktok.com/api/user/detail/?aid=1988&uniqueId={username}&language=it

Will open up a PR when I get a working example.

pthieu commented 2 years ago

@carcabot

Is this a captcha getting in the way?

My code:

const Signer = require('tiktok-signature');
const rp = require('request-promise');

const SEC_UID =
  'put your id here';
const USER_AGENT =
  'Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_6) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/98.0.4758.109 Safari/537.36';

async function main() {
  const signer = new Signer(null, USER_AGENT);
  await signer.init();

  const dynamicParams = `https://t.tiktok.com/api/post/item_list/?aid=1988&count=30&secUid=${SEC_UID}&cursor=0`;
  const signature = await signer.sign(dynamicParams);
  const navigator = await signer.navigator();
  await signer.close();

  // console.log(signature);
  // console.log(navigator);

  const { signed_url: signedURL, x_tt_params: xTtParams } = signature;
  const { user_agent: userAgent } = navigator;

  const res = await testSignedUrl({ userAgent, signedURL, xTtParams });

  console.log(res);
  debugger;
}

async function testSignedUrl({ userAgent, signedURL, xTtParams }) {
  const options = {
    method: 'GET',
    headers: {
      'user-agent': userAgent,
      'x-tt-params': xTtParams,
    },
    uri: signedURL,
    json: true,
  };
  return rp(options);
}

main();

Response:

{
  code: '10000',
  from: '',
  type: 'verify',
  version: '',
  region: 'sg',
  subtype: 'whirl',
  detail: 'Y0MNaZRNCAOhTGciztQp1ImbLDRv0KqUIpRBhcHUJFUbbwLKpE3J6l16sJn4t67GjTR0Mux0BhBYHvK0S6VDduj8EZtEM0B3MzuHq9xiz1wy9SWM4Tmg7kJHaHXsdyqy*gX2OpDXdOLxmejq38bw9eUhGKTakWeJW07L7Rtmxkq-nOoXn*DuCwEd2b8YQ*jcvgMn6E4z2vD4PyqkLnxl7Eb4utk7Pp7BSyLejoGkt*fYT2fPBvzi-Ok9HWwmAE6zj7VW5dRkWAIziLgz7DwAqnB8WbhBm*rh8nFrFwuZhT0LP0uZXxhZAVMvpB*0NQ8-A9rPueABtxNAlCVlbLH1Ag9ePLq41ra6DF5D1oFZdACAoZuwCEpEigCyNXL8eRH3h*04yA4lmqjA',
  verify_event: '',
  fp: 'verify_5b161567bda98b6a50c0414d99909d4b',
  scene: '',
  verify_ticket: '',
  channel_mobile: '',
  sms_content: '',
  mobile: '',
  email: ''
}

I read https://github.com/carcabot/tiktok-signature/issues/90 and https://github.com/carcabot/tiktok-signature/issues/86 and tried to change my user-agent but it didn't work.

pthieu commented 2 years ago

I did also read your trick in https://github.com/carcabot/tiktok-signature/issues/140#issuecomment-1054432454

I don't provide that in the dynamicParams, not sure if this is needed? How would I go about programmatically generating this or can it be any value?

carcabot commented 2 years ago

dynamicParams should contain all parameters because are mandatory.

secUid, cursor and count can be modified dynamically.

dynamicParams = "https://m.tiktok.com/api/post/item_list/?aid=1988&count=30&cursor=0&secUid=MS4wLjABAAAAQ09e6Ck9CQrQQYAPLehEKMlvVS8XzmGcbNHTGXsXIZSIj7Pe21eYtDq0nzKy6-5V&cookie_enabled=true&screen_width=&screen_height=&browser_language=&browser_platform=&browser_name=&browser_version=&browser_online=&timezone_name=Europe/London"

Here's a working example in nodeJS:

const Signer = require("../../tiktok-signature");
const rp = require("request-promise");

const SEC_UID = "MS4wLjABAAAAQ09e6Ck9CQrQQYAPLehEKMlvVS8XzmGcbNHTGXsXIZSIj7Pe21eYtDq0nzKy6-5V";
const USER_AGENT =
  "Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_6) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/98.0.4758.109 Safari/537.36";

async function main() {
  const signer = new Signer(null, USER_AGENT);
  await signer.init();

  const dynamicParams = `https://m.tiktok.com/api/post/item_list/?aid=1988&count=30&cursor=0&secUid=${SEC_UID}&cookie_enabled=true&screen_width=&screen_height=&browser_language=&browser_platform=&browser_name=&browser_version=&browser_online=&timezone_name=Europe/London`;
  const signature = await signer.sign(dynamicParams);
  const navigator = await signer.navigator();
  await signer.close();

  console.log(signature);
  // console.log(navigator);

  const { signed_url: signedURL, x_tt_params: xTtParams } = signature;
  const { user_agent: userAgent } = navigator;

  const res = await testSignedUrl({ userAgent, xTtParams });

  console.log(res);
  debugger;
}

async function testSignedUrl({ userAgent, xTtParams }) {
  const options = {
    method: "GET",
    headers: {
      "user-agent": userAgent,
      "x-tt-params": xTtParams,
    },
    uri: 'https://m.tiktok.com/api/post/item_list/?aid=1988&app_language=en&app_name=tiktok_web&battery_info=1&browser_language=en-US&browser_name=Mozilla&browser_online=true&browser_platform=Win32&browser_version=5.0%20%28Windows%20NT%2010.0%3B%20Win64%3B%20x64%29%20AppleWebKit%2F537.36%20%28KHTML%2C%20like%20Gecko%29%20Chrome%2F98.0.4758.102%20Safari%2F537.36%20Edg%2F98.0.1108.62&channel=tiktok_web&cookie_enabled=true&device_id=7002566096994190854&device_platform=web_pc&focus_state=true&from_page=user&history_len=2&is_fullscreen=false&is_page_visible=true&os=windows&priority_region=RO&referer=&region=RO&root_referer&screen_height=1080&screen_width=1920&tz_name=Europe%2FBucharest&verifyFp=verify_dca8729afe5c502257ed30b0b070dbdb&webcast_language=en&msToken=ke4xktLgsxyAhsw7mGoAII-6L3--7SZyUDxD7M1_-TNM_2bSagox_yzIeYOpoBC-kPlNMdT-9trNOv3X7q4iLqrHyHY8_p5cheQ-GCrOGnAPjgrgb6bNGgX1H03HwC5q-mZXBzgQ&X-Bogus=DFSzswVLOezANCYfS5qBc2XyYJA4&_signature=_02B4Z6wo00001WyqqDQAAIDATwuGmbdoOXlsqqyAADk7e9',
    json: true,
  };
  return rp(options);
}

main();

uri from testSignedUrl function should be the same all the time.

pthieu commented 2 years ago

@carcabot so TikTok requires the static URL to be called but gets the parameters from their backend but needs the x-tt-params header to know what to look for? Wow they're really into their security.

I moved over to axios from request but will send a PR sometime soon.

pthieu commented 2 years ago

Oh one more thing @carcabot: I didn't dive into the library code too much so this part is unknown to me. To sign the URL and get x-tt-params, is it a local encode function in the browser or do the libs actually make a request to TikTok's servers to get the signature?

I'm assuming because we need to launch a headless browser, it's a local encode.