Skip to content

Commit f0655e0

Browse files
authored
fix(atlas-service): pass csrf tokens with the requests COMPASS-8490 (#6465)
fix(atlas-service): pass csrf tokens with the requests
1 parent 75a0852 commit f0655e0

File tree

5 files changed

+84
-12
lines changed

5 files changed

+84
-12
lines changed

packages/atlas-service/src/atlas-service.spec.ts

+26
Original file line numberDiff line numberDiff line change
@@ -132,4 +132,30 @@ describe('AtlasService', function () {
132132
);
133133
expect(getAuthHeadersFn.calledOnce).to.be.true;
134134
});
135+
136+
it('should set CSRF headers when available', async function () {
137+
const fetchStub = sandbox.stub().resolves({ status: 200, ok: true });
138+
global.fetch = fetchStub;
139+
document.head.append(
140+
(() => {
141+
const el = document.createElement('meta');
142+
el.setAttribute('name', 'csrf-token');
143+
el.setAttribute('content', 'token');
144+
return el;
145+
})()
146+
);
147+
document.head.append(
148+
(() => {
149+
const el = document.createElement('meta');
150+
el.setAttribute('name', 'CSRF-TIME');
151+
el.setAttribute('content', 'time');
152+
return el;
153+
})()
154+
);
155+
await atlasService.fetch('/foo/bar', { method: 'POST' });
156+
expect(fetchStub.firstCall.lastArg.headers).to.deep.eq({
157+
'X-CSRF-Time': 'time',
158+
'X-CSRF-Token': 'token',
159+
});
160+
});
135161
});

packages/atlas-service/src/atlas-service.ts

+18
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,23 @@ function normalizePath(path?: string) {
1919
return encodeURI(path);
2020
}
2121

22+
function getCSRFHeaders() {
23+
return {
24+
'X-CSRF-Token':
25+
document
26+
.querySelector('meta[name="csrf-token" i]')
27+
?.getAttribute('content') ?? '',
28+
'X-CSRF-Time':
29+
document
30+
.querySelector('meta[name="csrf-time" i]')
31+
?.getAttribute('content') ?? '',
32+
};
33+
}
34+
35+
function shouldAddCSRFHeaders(method = 'get') {
36+
return !/^(get|head|options|trace)$/.test(method.toLowerCase());
37+
}
38+
2239
export class AtlasService {
2340
private config: AtlasServiceConfig;
2441
constructor(
@@ -60,6 +77,7 @@ export class AtlasService {
6077
...init,
6178
headers: {
6279
...this.options?.defaultHeaders,
80+
...(shouldAddCSRFHeaders(init?.method) && getCSRFHeaders()),
6381
...init?.headers,
6482
},
6583
});

packages/compass-web/sandbox/index.tsx

+20-2
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
import React from 'react';
1+
import React, { useLayoutEffect } from 'react';
22
import ReactDOM from 'react-dom';
33
import { resetGlobalCSS, css, Body } from '@mongodb-js/compass-components';
44
import { CompassWeb } from '../src/index';
@@ -16,9 +16,22 @@ const sandboxContainerStyles = css({
1616

1717
resetGlobalCSS();
1818

19+
function getMetaEl(name: string) {
20+
return (
21+
document.querySelector(`meta[name="${name}" i]`) ??
22+
(() => {
23+
const el = document.createElement('meta');
24+
el.setAttribute('name', name);
25+
document.head.prepend(el);
26+
return el;
27+
})()
28+
);
29+
}
30+
1931
const App = () => {
2032
const [currentTab, updateCurrentTab] = useWorkspaceTabRouter();
21-
const { status, projectId } = useAtlasProxySignIn();
33+
const { status, projectParams } = useAtlasProxySignIn();
34+
const { projectId, csrfToken, csrfTime } = projectParams ?? {};
2235

2336
const atlasServiceSandboxBackendVariant =
2437
process.env.COMPASS_WEB_HTTP_PROXY_CLOUD_CONFIG === 'local'
@@ -28,6 +41,11 @@ const App = () => {
2841
? 'web-sandbox-atlas-dev'
2942
: 'web-sandbox-atlas';
3043

44+
useLayoutEffect(() => {
45+
getMetaEl('csrf-token').setAttribute('content', csrfToken ?? '');
46+
getMetaEl('csrf-time').setAttribute('content', csrfTime ?? '');
47+
}, [csrfToken, csrfTime]);
48+
3149
if (status === 'checking') {
3250
return null;
3351
}

packages/compass-web/sandbox/sandbox-atlas-sign-in.tsx

+20-7
Original file line numberDiff line numberDiff line change
@@ -12,12 +12,18 @@ console.info(
1212

1313
type SignInStatus = 'checking' | 'signed-in' | 'signed-out';
1414

15+
type ProjectParams = {
16+
projectId: string;
17+
csrfToken: string;
18+
csrfTime: string;
19+
};
20+
1521
type AtlasLoginReturnValue =
1622
| {
1723
status: 'checking' | 'signed-out';
18-
projectId: null;
24+
projectParams: null;
1925
}
20-
| { status: 'signed-in'; projectId: string };
26+
| { status: 'signed-in'; projectParams: ProjectParams };
2127

2228
const bodyContainerStyles = css({
2329
display: 'flex',
@@ -64,7 +70,9 @@ const IS_CI =
6470

6571
export function useAtlasProxySignIn(): AtlasLoginReturnValue {
6672
const [status, setStatus] = useState<SignInStatus>('checking');
67-
const [projectId, setProjectId] = useState<string | null>(null);
73+
const [projectParams, setProjectParams] = useState<ProjectParams | null>(
74+
null
75+
);
6876

6977
const signIn = ((window as any).__signIn = useCallback(async () => {
7078
try {
@@ -104,7 +112,12 @@ export function useAtlasProxySignIn(): AtlasLoginReturnValue {
104112
if (!projectId) {
105113
throw new Error('failed to get projectId');
106114
}
107-
setProjectId(projectId);
115+
const { csrfToken, csrfTime } = await fetch(
116+
`/cloud-mongodb-com/v2/${projectId}/params`
117+
).then((res) => {
118+
return res.json();
119+
});
120+
setProjectParams({ projectId, csrfToken, csrfTime });
108121
setStatus('signed-in');
109122
if (IS_CI) {
110123
return;
@@ -151,12 +164,12 @@ export function useAtlasProxySignIn(): AtlasLoginReturnValue {
151164
if (status === 'checking' || status === 'signed-out') {
152165
return {
153166
status,
154-
projectId: null,
167+
projectParams: null,
155168
};
156169
}
157170

158-
if (status === 'signed-in' && projectId) {
159-
return { status, projectId };
171+
if (status === 'signed-in' && projectParams) {
172+
return { status, projectParams };
160173
}
161174

162175
throw new Error('Weird state, ask for help in Compass dev channel');

packages/compass-web/scripts/electron-proxy.js

-3
Original file line numberDiff line numberDiff line change
@@ -168,14 +168,11 @@ class AtlasCloudAuthenticator {
168168
}
169169

170170
async getCloudHeaders() {
171-
// Order is important, fetching data can update the cookies
172-
const csrfHeaders = await this.#getCSRFHeaders();
173171
const cookie = (await this.#getCloudSessionCookies()).join('; ');
174172
return {
175173
cookie,
176174
host: CLOUD_HOST,
177175
origin: CLOUD_ORIGIN,
178-
...csrfHeaders,
179176
};
180177
}
181178

0 commit comments

Comments
 (0)