Skip to content

Commit a344c05

Browse files
authored
Merge pull request #7665 from Expensify/Rory-FixDesktopCORS
[CP Stg] Fix CORS errors in desktop application
2 parents d8a3247 + 0424b45 commit a344c05

File tree

5 files changed

+78
-13
lines changed

5 files changed

+78
-13
lines changed

config/electron.config.js

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,11 @@
1+
const ENVIRONMENT = require('../src/CONST/ENVIRONMENT');
2+
13
module.exports = {
24
appId: 'com.expensifyreactnative.chat',
35
productName: 'New Expensify',
46
extraMetadata: {
57
main: './desktop/main.js',
8+
electronEnvironment: process.env.SHOULD_DEPLOY_PRODUCTION ? ENVIRONMENT.PRODUCTION : ENVIRONMENT.STAGING,
69
},
710
mac: {
811
category: 'public.app-category.finance',

desktop/ELECTRON_ENVIRONMENT.js

Lines changed: 30 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,30 @@
1+
// This variable is injected into package.json by electron-builder via the extraMetadata field (specified in electron.config.js)
2+
// It will be `PROD` on production, `STG` on staging, and `undefined` on dev (because dev doesn't use electron-builder)
3+
const {electronEnvironment} = require('../package.json');
4+
const ENVIRONMENT = require('../src/CONST/ENVIRONMENT');
5+
6+
/**
7+
* @returns {String} – One of ['PROD', 'STG', 'DEV']
8+
*/
9+
function getEnvironment() {
10+
// If we are on dev, then the NODE_ENV environment variable will be present (set by the executing shell in start.js)
11+
if (process.env.NODE_ENV === 'development') {
12+
return ENVIRONMENT.DEV;
13+
}
14+
15+
// Otherwise, use the environment injected into package.json by electron-builder
16+
return electronEnvironment;
17+
}
18+
19+
function isDev() {
20+
return getEnvironment() === ENVIRONMENT.DEV;
21+
}
22+
23+
function isProd() {
24+
return getEnvironment() === ENVIRONMENT.PRODUCTION;
25+
}
26+
27+
module.exports = {
28+
isDev,
29+
isProd,
30+
};

desktop/main.js

Lines changed: 36 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -11,10 +11,10 @@ const serve = require('electron-serve');
1111
const contextMenu = require('electron-context-menu');
1212
const {autoUpdater} = require('electron-updater');
1313
const log = require('electron-log');
14+
const ELECTRON_ENVIRONMENT = require('./ELECTRON_ENVIRONMENT');
1415
const ELECTRON_EVENTS = require('./ELECTRON_EVENTS');
1516
const checkForUpdates = require('../src/libs/checkForUpdates');
1617

17-
const isDev = process.env.NODE_ENV === 'development';
1818
const port = process.env.PORT || 8080;
1919

2020
/**
@@ -41,7 +41,7 @@ autoUpdater.logger.transports.file.level = 'info';
4141
_.assign(console, log.functions);
4242

4343
// setup Hot reload
44-
if (isDev) {
44+
if (ELECTRON_ENVIRONMENT.isDev()) {
4545
try {
4646
require('electron-reloader')(module, {
4747
watchRenderer: false,
@@ -124,12 +124,12 @@ const electronUpdater = browserWindow => ({
124124
});
125125

126126
const mainWindow = (() => {
127-
const loadURL = isDev
127+
const loadURL = ELECTRON_ENVIRONMENT.isDev()
128128
? win => win.loadURL(`http://localhost:${port}`)
129129
: serve({directory: `${__dirname}/../dist`});
130130

131131
// Prod and staging set the icon in the electron-builder config, so only update it here for dev
132-
if (isDev) {
132+
if (ELECTRON_ENVIRONMENT.isDev()) {
133133
app.dock.setIcon(`${__dirname}/icon-dev.png`);
134134
app.setName('New Expensify');
135135
}
@@ -147,8 +147,38 @@ const mainWindow = (() => {
147147
titleBarStyle: 'hidden',
148148
});
149149

150+
/*
151+
* The default origin of our Electron app is app://- instead of https://new.expensify.com or https://staging.new.expensify.com
152+
* This causes CORS errors because the referer and origin headers are wrong and the API responds with an Access-Control-Allow-Origin that doesn't match app://-
153+
*
154+
* To fix this, we'll:
155+
*
156+
* 1. Modify headers on any outgoing requests to match the origin of our corresponding web environment.
157+
* 2. Modify the Access-Control-Allow-Origin header of the response to match the "real" origin of our Electron app.
158+
*/
159+
if (!ELECTRON_ENVIRONMENT.isDev()) {
160+
const newDotURL = ELECTRON_ENVIRONMENT.isProd() ? 'https://new.expensify.com' : 'https://staging.new.expensify.com';
161+
162+
// Modify the origin and referer for requests sent to our API
163+
const validDestinationFilters = {urls: ['https://*.expensify.com/*']};
164+
browserWindow.webContents.session.webRequest.onBeforeSendHeaders(validDestinationFilters, (details, callback) => {
165+
// eslint-disable-next-line no-param-reassign
166+
details.requestHeaders.origin = newDotURL;
167+
// eslint-disable-next-line no-param-reassign
168+
details.requestHeaders.referer = newDotURL;
169+
callback({requestHeaders: details.requestHeaders});
170+
});
171+
172+
// Modify access-control-allow-origin header for the response
173+
browserWindow.webContents.session.webRequest.onHeadersReceived(validDestinationFilters, (details, callback) => {
174+
// eslint-disable-next-line no-param-reassign
175+
details.responseHeaders['access-control-allow-origin'] = ['app://-'];
176+
callback({responseHeaders: details.responseHeaders});
177+
});
178+
}
179+
150180
// Prod and staging overwrite the app name in the electron-builder config, so only update it here for dev
151-
if (isDev) {
181+
if (ELECTRON_ENVIRONMENT.isDev()) {
152182
browserWindow.setTitle('New Expensify');
153183
}
154184

@@ -286,7 +316,7 @@ const mainWindow = (() => {
286316

287317
// Start checking for JS updates
288318
.then((browserWindow) => {
289-
if (isDev) {
319+
if (ELECTRON_ENVIRONMENT.isDev()) {
290320
return;
291321
}
292322

src/CONST/ENVIRONMENT.js

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
module.exports = {
2+
DEV: 'DEV',
3+
STAGING: 'STG',
4+
PRODUCTION: 'PROD',
5+
};

src/CONST.js renamed to src/CONST/index.js

Lines changed: 4 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
import lodashGet from 'lodash/get';
22
import Config from 'react-native-config';
3-
import * as Url from './libs/Url';
3+
import ENVIRONMENT from './ENVIRONMENT';
4+
import * as Url from '../libs/Url';
45

56
const CLOUDFRONT_URL = 'https://d2k5nsl2zxldvw.cloudfront.net';
67
const ACTIVE_ENVIRONMENT_NEW_EXPENSIFY_URL = Url.addTrailingForwardSlash(lodashGet(Config, 'EXPENSIFY_URL_CASH', 'https://new.expensify.com'));
@@ -390,12 +391,6 @@ const CONST = {
390391
391392
},
392393

393-
ENVIRONMENT: {
394-
DEV: 'DEV',
395-
STAGING: 'STG',
396-
PRODUCTION: 'PROD',
397-
},
398-
399394
// Used to delay the initial fetching of reportActions when the app first inits or reconnects (e.g. returning
400395
// from backgound). The times are based on how long it generally seems to take for the app to become interactive
401396
// in each scenario.
@@ -614,4 +609,6 @@ const CONST = {
614609
},
615610
};
616611

612+
CONST.ENVIRONMENT = ENVIRONMENT;
613+
617614
export default CONST;

0 commit comments

Comments
 (0)