File size: 7,802 Bytes
7a4c980
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
const ACCOUNT_API = "https://api-pro.urban-vpn.com/rest/v1";
const STATS_API = "https://stats.urban-vpn.com/api/rest/v2";
const CLIENT_APP = "URBAN_VPN_BROWSER_EXTENSION";
const BROWSER = "CHROME";

interface UrbanProxyResult {
    url: string;
    protocol: string;
    host: string;
    port: number;
    username?: string;
    password?: string;
}

const PREFERRED_COUNTRIES = ["US", "GB", "CA", "DE", "FR", "NL", "ES", "IT", "JP", "KR", "SG", "AU"];

export async function fetchUrbanProxy(targetCountryCode = "RANDOM"): Promise<UrbanProxyResult | null> {
    console.log(`[UrbanVPN] Fetching Urban VPN Proxy (Target: ${targetCountryCode})...`);

    // 1. Register Anonymous
    // console.log("[UrbanVPN] 1. Registering Anonymous User...");
    const regUrl = `${ACCOUNT_API}/registrations/clientApps/${CLIENT_APP}/users/anonymous`;

    const regHeaders = {
        "content-type": "application/json",
        "accept": "application/json, text/plain, */*",
        "user-agent": "Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/110.0.0.0 Safari/537.36"
    };

    const regPayload = {
        clientApp: {
            name: CLIENT_APP,
            browser: BROWSER
        }
    };

    let regResp;
    try {
        regResp = await fetch(regUrl, {
            method: "POST",
            headers: regHeaders,
            body: JSON.stringify(regPayload)
        });
    } catch (err) {
        console.error("[UrbanVPN] Network error during registration:", err);
        return null;
    }

    if (!regResp.ok) {
        const text = await regResp.text();
        console.error(`[UrbanVPN] Registration failed: ${regResp.status} ${regResp.statusText}`);
        console.error(text);
        return null;
    }

    const regData = await regResp.json();
    const idToken = regData.id_token || regData.idToken || regData.value;

    if (!idToken) {
        console.error("[UrbanVPN] No ID token found in registration response.");
        return null;
    }

    // 2. Get Security Token
    // console.log("[UrbanVPN] 2. Getting Security Token...");
    const secUrl = `${ACCOUNT_API}/security/tokens/accs`;
    const secHeaders = {
        ...regHeaders,
        "authorization": `Bearer ${idToken}`
    };
    const secPayload = {
        type: "accs",
        clientApp: {
            name: CLIENT_APP
        }
    };

    const secResp = await fetch(secUrl, {
        method: "POST",
        headers: secHeaders,
        body: JSON.stringify(secPayload)
    });

    if (!secResp.ok) {
        const text = await secResp.text();
        console.error(`[UrbanVPN] Security Token request failed: ${secResp.status}`);
        console.error(text);
        return null;
    }

    const secData = await secResp.json();

    let tokenString = "";
    let credUsername = "";
    const credPassword = "1";

    if (secData.token && typeof secData.token === 'object' && secData.token.value) {
        tokenString = secData.token.value;
        credUsername = secData.token.value;
    } else if (typeof secData.token === 'string') {
        tokenString = secData.token;
        credUsername = secData.token;

    } else if (secData.value) {
        tokenString = secData.value;
        credUsername = secData.value;
    }

    if (!tokenString) {
        console.error("[UrbanVPN] No security token found.");
        return null;
    }

    // 3. Get Countries / Proxies
    // console.log("[UrbanVPN] 3. Fetching Proxy List...");
    const countriesUrl = `${STATS_API}/entrypoints/countries`;
    const proxyHeaders = {
        ...regHeaders,
        "authorization": `Bearer ${tokenString}`,
        "X-Client-App": CLIENT_APP
    };

    // @ts-ignore: delete operator on string index signature
    delete proxyHeaders["content-type"];

    const countriesResp = await fetch(countriesUrl, {
        headers: proxyHeaders
    });

    if (!countriesResp.ok) {
        const text = await countriesResp.text();
        console.error(`[UrbanVPN] Failed to fetch countries: ${countriesResp.status}`);
        console.error(text);
        return null;
    }

    const countriesData = await countriesResp.json();

    if (!countriesData.countries || !countriesData.countries.elements) {
        console.error("[UrbanVPN] Invalid countries data format.");
        return null;
    }

    const countries = countriesData.countries.elements;

    // Pick a country
    let selectedCountryCode = targetCountryCode;
    if (selectedCountryCode === "RANDOM") {
        selectedCountryCode = PREFERRED_COUNTRIES[Math.floor(Math.random() * PREFERRED_COUNTRIES.length)];
    }

    // Find target country proxy
    // deno-lint-ignore no-explicit-any
    let targetCountry = countries.find((c: any) => c.code.iso2 === selectedCountryCode);

    // Fallback if random choice not found
    if (!targetCountry) {
        targetCountry = countries[0];
        console.log(`[UrbanVPN] Requested country ${selectedCountryCode} not found, falling back to ${targetCountry.code.iso2}`);
    }

    if (targetCountry) {
        console.log(`[UrbanVPN] Selected Country: ${targetCountry.title} (${targetCountry.code.iso2})`);

        let proxyHost = null;
        let proxyPort = null;
        let signature = null;

        if (targetCountry.address && targetCountry.address.primary) {
            proxyHost = targetCountry.address.primary.host;
            proxyPort = targetCountry.address.primary.port;
        }
        else if (targetCountry.servers && targetCountry.servers.elements && targetCountry.servers.elements.length > 0) {
            // Pick a RANDOM server from the list
            const serverIndex = Math.floor(Math.random() * targetCountry.servers.elements.length);
            const srv = targetCountry.servers.elements[serverIndex];

            if (srv.address && srv.address.primary) {
                proxyHost = srv.address.primary.host;
                proxyPort = srv.address.primary.port || srv.address.primary.port_min;
                signature = srv.signature;
            }
        }

        if (signature) {
            // console.log("[UrbanVPN] Found proxy signature, fetching Auth Proxy Token...");
            const proxyTokenUrl = `${ACCOUNT_API}/security/tokens/accs-proxy`;
            const proxyTokenPayload = {
                type: "accs-proxy",
                clientApp: { name: CLIENT_APP },
                signature: signature
            };

            const proxyTokenHeaders = {
                ...regHeaders,
                "authorization": `Bearer ${tokenString}`
            };

            const ptResp = await fetch(proxyTokenUrl, {
                method: "POST",
                headers: proxyTokenHeaders,
                body: JSON.stringify(proxyTokenPayload)
            });

            if (ptResp.ok) {
                const ptData = await ptResp.json();
                if (ptData.value) {
                    credUsername = ptData.value;
                } else if (ptData.token && ptData.token.value) {
                    credUsername = ptData.token.value;
                }
            } else {
                console.error(`[UrbanVPN] Failed to get Proxy Auth Token: ${ptResp.status}`);
            }
        }

        if (proxyHost) {
            const proxyUrl = `http://${encodeURIComponent(credUsername)}:${encodeURIComponent(credPassword)}@${proxyHost}:${proxyPort}`;
            console.log(`[UrbanVPN] Proxy found: ${proxyHost}:${proxyPort}`);
            return {
                url: proxyUrl,
                protocol: 'http',
                host: proxyHost,
                port: proxyPort,
                username: credUsername,
                password: credPassword
            };
        }
    }

    console.error("[UrbanVPN] No proxy server details found.");
    return null;
}