I'm working on an application written in Ionic 5 with Angular, and I'm having trouble using a third-party API. This API freely uses the original Android application, as well as the original web client, but the developers of this API have some kind of restriction so that third-party clients cannot use it. After racking my brains over this for several days, I came to the conclusion that the problem is in the request headers, namely X-S-N, X-S-S, X-S-T, X-Device-Identifier. They are generated in a special way for each request, the mechanism of which I could not figure out. This is what the original authorization request header from the original web client looks like: :authority gethornet.com :method POST ath /api/v3/session :scheme https accept application/json accept-encoding gzip, deflate, br, zstd accept-language ru cache-control no-cache content-length 74 content-type application/json origin https://next.hornet.com pragma no-cache priority u=1, i referer https://next.hornet.com/ sec-ch-ua "Chromium";v="136", "Microsoft Edge";v="136", "Not.A/Brand";v="99" sec-ch-ua-mobile ?0 sec-ch-ua-platform "Windows" sec-fetch-dest empty sec-fetch-mode cors sec-fetch-site cross-site user-agent Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/136.0.0.0 Safari/537.36 Edg/136.0.0.0 x-client-version Web 2.28.0 x-device-identifier web-206d0b84-a40d-43a0-8456-fcdf12c3ef78 x-s-n 2680c015-14e9-4566-881f-dd754c5d27fd x-s-s 2837bcbdc56ab9f4aba5f181c7ede2b699431646e9e663a5f4790967b43f478c x-s-t 1746345953 x-timezone Europe/Kiev I analyzed the original scripts that generate the request signature using the browser debugger and found a couple of interesting places: protected getHeaders(url: string): Headers { const { locale, currentAppUrl, accessToken, deviceId, profileId, deviceLocation, req2, req1, appName, timeZone, } = this._context const headers: Headers = { Accept: "application/json", "Content-Type": "application/json", "X-Client-Version": "Web " + this._appConfig.version, "Accept-Language": locale, Referer: currentAppUrl, } if (accessToken) { headers["Authorization"] = `Hornet ${accessToken}` } if (deviceLocation) { headers["X-Device-Location"] = deviceLocation } if (appName) { headers["X-App"] = appName } if (timeZone) { headers["X-Timezone"] = timeZone } headers["X-Device-Identifier"] = deviceId const signature = this.getSignature(url) if (["internal", "external"].includes(signature)) { const { n, t, s } = this.addMetaToPayload( "n", {}, signature === "external", deviceId, profileId, req2 || "", req1 || "" ) headers["X-S-N"] = n headers["X-S-T"] = t headers["X-S-S"] = s } return headers } protected getSignature(url: string) { const internalRoutes = [ "/profiles/verify$", "/reaction?$", "/comments$", "/favourites$", "/activities/[0-9]+/award$", "sso_tokens$", "messages$", "quickies_members/near", ] const internalRoutesRegex = new RegExp(internalRoutes.join("|"), "gi") // req2 if (url.match(internalRoutesRegex)) { return "internal" } // req1 if (url.match(/\/accounts$|\/session$|\/sso_tokens\/exchange$/)) { return "external" } return "" } protected addMetaToPayload( k: string, p: any, sw: any, dId: any, pId: any, r2: string, r1: string ): ReqSig { sw = sw ? 1 : 0 if (!p || p[k] || !k) { return p } p[k] = k === "n" ? uuidv4() : k === "t" ? Math.round(new Date().getTime() / 1000) + (this._tsDiff || 0) : HmacSHA256( sw ? [p["n"], dId, p["t"]].join(",") : [p["n"], dId, pId, p["t"]].join(","), sw ? process.env["REQ1"] || r1 || "" : r2 || "" ) + "" return this.addMetaToPayload(this._reqm[k], p, sw, dId, pId, r2, r1) } } Then I tried to implement these algorithms in my application using ChatGPT, but unfortunately it didn't work and the server responded that it couldn't verify the authenticity of the request. Here is part of my code: export class ApiService { private baseUrl = 'https://gethornet.com/api/v3'; private req1Hex = '96dda5357b92ee297dcd1f4858d35a012a6339d45999ec35f0432db045e40041286be22e5b4fcfaff75c03b6e192f124ca3be0b287bd658715f3531bb56c2b99'; // private const r1 = "96dda5357b92ee297dcd1f4858d35a012a6339d45999ec35f0432db045e40041286be22e5b4fcfaff75c03b6e192f124ca3be0b287bd658715f3531bb56c2b99"; // private const r2 = "930950994872554322b77f64e367a6a5bb7d82648e6b9fa4817cfe3b63e3fd09e76cea4b35d76810f83eb38cef722f1242d4e95be47781635b1bda5c6f999e50"; private timezone = Intl.DateTimeFormat().resolvedOptions().timeZone; constructor(private http: HttpClient) {} async login(id: string, secret: string): Promise<Observable<any>> { const androidId = (await Device.getId()).identifier; const n = uuidv4(); const t = Math.floor(Date.now() / 1000); const signature = this.generateSignature(n, androidId, t); const body = { session: { id, provider: 'Hornet', secret } }; const headers = new HttpHeaders({ 'Accept': 'application/json', 'Content-Type': 'application/json', 'X-Client-Version': 'Web 2.28.0', 'X-Timezone': this.timezone, 'X-Device-Identifier': 'web-'+androidId, 'X-S-N': n, 'X-S-T': t.toString(), 'X-S-S': signature }); console.log(signature); return this.http.post(`${this.baseUrl}/session`, body, { headers }); } private generateSignature(n: string, deviceId: string, t: number): string { const message = [n, deviceId, t].join(','); const key = enc.Hex.parse(this.req1Hex); // Hex return HmacSHA256(message, key).toString(enc.Hex); } } Unfortunately, I'm at a dead end here and can't figure out what I'm doing wrong. I'd appreciate some advice. Continue reading...