1. This site uses cookies. By continuing to use this site, you are agreeing to our use of cookies. Learn More.
  2. Anuncie Aqui
    Anuncie aqui você Também: fdantas@4each.com.br

Defining a method for authenticating requests in a third-party API

Discussão em 'Angular' iniciado por thescs, Maio 4, 2025.

  1. thescs

    thescs Guest

    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
    :path /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...

Compartilhe esta Página