Skip to content

Commit 70ea915

Browse files
ahsan-javaidjanniks
authored andcommitted
fix: allow referrer header in request options
1 parent 49a094b commit 70ea915

File tree

9 files changed

+94
-36
lines changed

9 files changed

+94
-36
lines changed

packages/common/src/fetchUtil.ts

Lines changed: 31 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1,12 +1,38 @@
11
import 'cross-fetch/polyfill';
22

3+
// Define a default request options and allow modification using getters, setters
4+
// Reference: https://developer.mozilla.org/en-US/docs/Web/API/Request/Request
5+
const defaultFetchOpts: RequestInit = {
6+
// By default referrer value will be client:origin: above reference link
7+
referrerPolicy: 'origin', // Use origin value for referrer policy
8+
// https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/Referrer-Policy
9+
};
10+
/*
11+
* Get fetch options
12+
* @return fetchOptions
13+
*/
14+
export const getFetchOptions = () => {
15+
return defaultFetchOpts;
16+
};
17+
/*
18+
* Set fetch options
19+
* Users can change default referrer as well as other options when fetch is used internally by stacks.js libraries or from server side
20+
* @example
21+
* Reference: https://developer.mozilla.org/en-US/docs/Web/API/Request/Request
22+
* setFetchOptions({ referrer: 'no-referrer', referrerPolicy: 'no-referrer', ... other options as per above reference });
23+
* Now all the subsequent fetchPrivate will use above options
24+
* @return fetchOptions
25+
*/
26+
export const setFetchOptions = (ops: RequestInit) => {
27+
return Object.assign(defaultFetchOpts, ops);
28+
};
29+
330
/** @ignore */
431
export async function fetchPrivate(input: RequestInfo, init?: RequestInit): Promise<Response> {
5-
const defaultFetchOpts: RequestInit = {
6-
referrer: 'no-referrer',
7-
referrerPolicy: 'no-referrer',
8-
};
9-
const fetchOpts = Object.assign(defaultFetchOpts, init);
32+
const fetchOpts = {};
33+
// Use the provided options in request options along with default or user provided values
34+
Object.assign(fetchOpts, init, defaultFetchOpts);
35+
1036
const fetchResult = await fetch(input, fetchOpts);
1137
return fetchResult;
1238
}

packages/common/tests/fetch.test.ts

Lines changed: 28 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,28 @@
1+
import { fetchPrivate, getFetchOptions, setFetchOptions } from '../src'
2+
import fetchMock from "jest-fetch-mock";
3+
4+
test('Verify fetch private options', async () => {
5+
const defaultOptioins = getFetchOptions();
6+
7+
expect(defaultOptioins).toEqual({ referrerPolicy: 'origin' });
8+
9+
// Override default options when fetchPrivate is called internally by other stacks.js libraries like transactions or from server side
10+
// This is for developers as they cannot directly pass options directly in fetchPrivate
11+
const modifiedOptions: RequestInit= { referrer: 'http://test.com', referrerPolicy: 'same-origin' };
12+
13+
// Developers can set fetch options globally one time specifically when fetchPrivate is used internally by stacks.js libraries
14+
setFetchOptions(modifiedOptions);
15+
16+
expect(getFetchOptions()).toEqual(modifiedOptions);
17+
18+
// Browser will replace about:client with actual url but it will not be visible in test case
19+
fetchMock.mockOnce(`{ status: 'success'}`, { headers: modifiedOptions as any });
20+
21+
const result = await fetchPrivate('https://example.com');
22+
23+
// Verify the request options
24+
expect(result.status).toEqual(200);
25+
expect(result.headers.get('referrer')).toEqual(modifiedOptions.referrer);
26+
expect(result.headers.get('referrerPolicy')).toEqual(modifiedOptions.referrerPolicy);
27+
})
28+

packages/keychain/src/identity.ts

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@ import { Identity as IdentifyInterface, Profile } from './common';
55
import IdentityAddressOwnerNode from './nodes/identity-address-owner-node';
66
import { DEFAULT_PROFILE, fetchProfile, signAndUploadProfile } from './profiles';
77
import { getProfileURLFromZoneFile, IdentityKeyPair } from './utils';
8+
import { fetchPrivate } from '@stacks/common';
89
import {
910
connectToGaiaHubWithConfig,
1011
DEFAULT_GAIA_HUB,
@@ -131,7 +132,7 @@ export class Identity implements IdentifyInterface {
131132

132133
async fetchNames() {
133134
const getNamesUrl = `https://stacks-node-api.stacks.co/v1/addresses/bitcoin/${this.address}`;
134-
const res = await fetch(getNamesUrl);
135+
const res = await fetchPrivate(getNamesUrl);
135136
const data = await res.json();
136137
const { names }: { names: string[] } = data;
137138
return names;

packages/keychain/src/profiles.ts

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@ import { Identity, Profile } from './common';
44
import { IdentityKeyPair } from './utils';
55
import { uploadToGaiaHub } from './utils/gaia';
66
import { GaiaHubConfig } from '@stacks/storage';
7+
import { fetchPrivate } from '@stacks/common';
78

89
export const DEFAULT_PROFILE: Profile = {
910
'@type': 'Person',
@@ -81,7 +82,7 @@ const sendUsernameToRegistrar = async ({
8182
'Content-Type': 'application/json',
8283
};
8384

84-
const response = await fetch(registerUrl, {
85+
const response = await fetchPrivate(registerUrl, {
8586
method: 'POST',
8687
headers: requestHeaders,
8788
body: registrationRequestBody,
@@ -156,7 +157,7 @@ export const fetchProfile = async ({
156157
}) => {
157158
try {
158159
const url = await identity.profileUrl(gaiaUrl);
159-
const res = await fetch(url);
160+
const res = await fetchPrivate(url);
160161
if (res.ok) {
161162
const json = await res.json();
162163
const { decodedToken } = json[0];

packages/keychain/src/utils/gaia.ts

Lines changed: 15 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
// @ts-ignore
2-
import { Buffer } from '@stacks/common';
2+
import { Buffer, fetchPrivate } from '@stacks/common';
33
import { TokenSigner, Json } from 'jsontokens';
44
import { getPublicKeyFromPrivate, publicKeyToAddress } from '@stacks/encryption';
55
import randomBytes from 'randombytes';
@@ -13,7 +13,7 @@ interface HubInfo {
1313
}
1414

1515
export const getHubInfo = async (hubUrl: string) => {
16-
const response = await fetch(`${hubUrl}/hub_info`);
16+
const response = await fetchPrivate(`${hubUrl}/hub_info`);
1717
const data: HubInfo = await response.json();
1818
return data;
1919
};
@@ -115,16 +115,19 @@ export const uploadToGaiaHub = async (
115115
): Promise<string> => {
116116
const contentType = 'application/json';
117117

118-
const response = await fetch(`${hubConfig.server}/store/${hubConfig.address}/${filename}`, {
119-
method: 'POST',
120-
headers: {
121-
'Content-Type': contentType,
122-
Authorization: `bearer ${hubConfig.token}`,
123-
},
124-
body: contents,
125-
referrer: 'no-referrer',
126-
referrerPolicy: 'no-referrer',
127-
});
118+
const response = await fetchPrivate(
119+
`${hubConfig.server}/store/${hubConfig.address}/${filename}`,
120+
{
121+
method: 'POST',
122+
headers: {
123+
'Content-Type': contentType,
124+
Authorization: `bearer ${hubConfig.token}`,
125+
},
126+
body: contents,
127+
referrer: 'no-referrer',
128+
referrerPolicy: 'no-referrer',
129+
}
130+
);
128131
const { publicURL } = await response.json();
129132
return publicURL;
130133
};

packages/keychain/src/utils/index.ts

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
import { Buffer } from '@stacks/common';
1+
import { Buffer, fetchPrivate } from '@stacks/common';
22
import { BIP32Interface } from 'bitcoinjs-lib';
33
import IdentityAddressOwnerNode from '../nodes/identity-address-owner-node';
44
import { createSha2Hash, publicKeyToAddress } from '@stacks/encryption';
@@ -185,7 +185,7 @@ export const validateSubdomainAvailability = async (
185185
subdomain: Subdomains = Subdomains.BLOCKSTACK
186186
) => {
187187
const url = `${registrars[subdomain].apiUrl}/${name.toLowerCase()}.${subdomain}`;
188-
const resp = await fetch(url);
188+
const resp = await fetchPrivate(url);
189189
const data = await resp.json();
190190
return data;
191191
};
@@ -249,7 +249,7 @@ interface NameInfoResponse {
249249

250250
export const getProfileURLFromZoneFile = async (name: string) => {
251251
const url = `https://stacks-node-api.stacks.co/v1/names/${name}`;
252-
const res = await fetch(url);
252+
const res = await fetchPrivate(url);
253253
if (res.ok) {
254254
const nameInfo: NameInfoResponse = await res.json();
255255
const zone = parseZoneFile(nameInfo.zonefile);

packages/keychain/src/wallet/index.ts

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
import { Buffer } from '@stacks/common';
1+
import { Buffer, fetchPrivate } from '@stacks/common';
22
import { mnemonicToSeed } from 'bip39';
33
import { bip32, BIP32Interface } from 'bitcoinjs-lib';
44
import { ChainID } from '@stacks/transactions';
@@ -227,7 +227,7 @@ export class Wallet {
227227

228228
async fetchConfig(gaiaConfig: GaiaHubConfig): Promise<WalletConfig | null> {
229229
try {
230-
const response = await fetch(
230+
const response = await fetchPrivate(
231231
`${gaiaConfig.url_prefix}${gaiaConfig.address}/wallet-config.json`
232232
);
233233
const encrypted = await response.text();

packages/transactions/src/utils.ts

Lines changed: 3 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -1,10 +1,9 @@
1-
import { Buffer } from '@stacks/common';
1+
import { Buffer, fetchPrivate } from '@stacks/common';
22
import { sha256, sha512 } from 'sha.js';
33
import { ClarityValue, serializeCV } from './clarity';
44
import RIPEMD160 from 'ripemd160-min';
55
import { utils } from '@noble/secp256k1';
66
import { deserializeCV } from './clarity';
7-
import fetch from 'cross-fetch';
87
import { c32addressDecode } from 'c32check';
98
import lodashCloneDeep from 'lodash.clonedeep';
109
import { with0x } from '@stacks/common';
@@ -199,15 +198,8 @@ export function isClarityName(name: string) {
199198
}
200199

201200
/** @ignore */
202-
export async function fetchPrivate(input: RequestInfo, init?: RequestInit): Promise<Response> {
203-
const defaultFetchOpts: RequestInit = {
204-
referrer: 'no-referrer',
205-
referrerPolicy: 'no-referrer',
206-
};
207-
const fetchOpts = Object.assign(defaultFetchOpts, init);
208-
const fetchResult = await fetch(input, fetchOpts);
209-
return fetchResult;
210-
}
201+
export { fetchPrivate };
202+
211203
/**
212204
* Converts a clarity value to a hex encoded string with `0x` prefix
213205
* @param {ClarityValue} cv - the clarity value to convert

packages/wallet-sdk/tests/models/wallet.test.ts

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -43,6 +43,13 @@ test('restore wallet with username owned by stx private key', async () => {
4343
fetchMock
4444
.once(mockGaiaHubInfo)
4545
.once(JSON.stringify('no found'), { status: 404 }) // TODO mock fetch legacy wallet config
46+
.once(JSON.stringify({ // mock wallet-config.json
47+
iv: "a39461ec18e5dea8ac759d5b8141319d",
48+
ephemeralPK: "03e04d0046a9dd5b7a8fea4de55a8d912909738b26f2c72f8e5962217fa45f00bb",
49+
cipherText: "bf16d2da29b54a153d553ab99597096f3fa11bd964441927355c1d979bf614477c8ceba7098620a37fa98f92f0f79813f771b6e2c2087e6fd2b1675d98a5e14f28cba28134dac2913bcb06f469439a16b47778747c7e93f50169727a7b9053b5441c8645fc729b28f063d2ffd673a01342e2cbc4fbf0e05350a67ec53ee3b7e43ff4e2dddbded5868cc3f5c4ca323b621bd13b5f9f036dc4406c2418e98b1b905974479cc79ab102d9ba1eb7fe858987dd0777ed3b0356d6bd0bc775213ef878bffaa58c40365d831a9e436fbfc388bcff2909659cab38a65ae8512508f6fda247437d4819c98ea15e48a4a00c1594eb58f7bf7eb85aad7cd51e5b43a7ca1fec06385be125d0b8c07bac1ac1094bb4687e620f23a5a14f4b20674ccd198271eb2451f12ad294efa79a9b5a001a3682a5ec833140b78333f57ce9912f60ff94edf99ee0b5e59ddfe7fb4a1472c0d303eeab22471585a5a5689ed9779e4ded10a4feecf5107df4c847522b2d6c95ed1e45cccb8b834b47a79f6671b49ffbdb02e4887465ca521472b7f11a53be0221eaeeffed2c6cf4d17a6fdae4b8f2b963d8a102c5376e6fa01bdaf9dc3d544c9954090b23fc02c8f500319b0cc43d7f73ff5012514d473afc818967eb0d0837a9c6920f9bc39e5f49fefdbc4fa33e6be88820a1abaeacb836bd398e7031d4286121383f53e2873ea1c2f0b649a12aec7db049505c58323fb34aaaf8d59fc0b962df05b8e9ea0dabf7fc9923b25af9ff3bd08a0b2dea7462a9e889485aba8605592308847468e843fca721a70aae9528d4abaae1a539f57c624f06b5b7dfdcf0a9d94b509697a1d0020b5f0b60ab19cc6abaf14928612663a9b6f15e18174a0a31fc506c428df13889fd877b7b639106d72c1b9dc8509758035337776c9d2d489da61f8de92a880b8ab802bd098fea111ab6af59fadd275285c59a98f824d5023990664856f971a6928869d3447f4426cb6855be55e43778f65a77e4d4348da0eda3e45e56a5d8fd11aff6ec62f53eac1cd862",
50+
mac: "623331e6804bf8c0dee7db7894a84ed02c70bf1ec0b6a5b2ccaff7d0638a9d88",
51+
wasString: true
52+
}))
4653
.once(JSON.stringify({ names: ['public_profile_for_testing.id.blockstack'] }))
4754
.once(JSON.stringify('ok')); // updateWalletConfig
4855

0 commit comments

Comments
 (0)