Skip to content

Commit

Permalink
fix(atlas-service): pass csrf tokens with the requests COMPASS-8490 (#…
Browse files Browse the repository at this point in the history
…6465)

fix(atlas-service): pass csrf tokens with the requests
  • Loading branch information
gribnoysup authored Nov 8, 2024
1 parent 75a0852 commit f0655e0
Show file tree
Hide file tree
Showing 5 changed files with 84 additions and 12 deletions.
26 changes: 26 additions & 0 deletions packages/atlas-service/src/atlas-service.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -132,4 +132,30 @@ describe('AtlasService', function () {
);
expect(getAuthHeadersFn.calledOnce).to.be.true;
});

it('should set CSRF headers when available', async function () {
const fetchStub = sandbox.stub().resolves({ status: 200, ok: true });
global.fetch = fetchStub;
document.head.append(
(() => {
const el = document.createElement('meta');
el.setAttribute('name', 'csrf-token');
el.setAttribute('content', 'token');
return el;
})()
);
document.head.append(
(() => {
const el = document.createElement('meta');
el.setAttribute('name', 'CSRF-TIME');
el.setAttribute('content', 'time');
return el;
})()
);
await atlasService.fetch('/foo/bar', { method: 'POST' });
expect(fetchStub.firstCall.lastArg.headers).to.deep.eq({
'X-CSRF-Time': 'time',
'X-CSRF-Token': 'token',
});
});
});
18 changes: 18 additions & 0 deletions packages/atlas-service/src/atlas-service.ts
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,23 @@ function normalizePath(path?: string) {
return encodeURI(path);
}

function getCSRFHeaders() {
return {
'X-CSRF-Token':
document
.querySelector('meta[name="csrf-token" i]')
?.getAttribute('content') ?? '',
'X-CSRF-Time':
document
.querySelector('meta[name="csrf-time" i]')
?.getAttribute('content') ?? '',
};
}

function shouldAddCSRFHeaders(method = 'get') {
return !/^(get|head|options|trace)$/.test(method.toLowerCase());
}

export class AtlasService {
private config: AtlasServiceConfig;
constructor(
Expand Down Expand Up @@ -60,6 +77,7 @@ export class AtlasService {
...init,
headers: {
...this.options?.defaultHeaders,
...(shouldAddCSRFHeaders(init?.method) && getCSRFHeaders()),
...init?.headers,
},
});
Expand Down
22 changes: 20 additions & 2 deletions packages/compass-web/sandbox/index.tsx
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import React from 'react';
import React, { useLayoutEffect } from 'react';
import ReactDOM from 'react-dom';
import { resetGlobalCSS, css, Body } from '@mongodb-js/compass-components';
import { CompassWeb } from '../src/index';
Expand All @@ -16,9 +16,22 @@ const sandboxContainerStyles = css({

resetGlobalCSS();

function getMetaEl(name: string) {
return (
document.querySelector(`meta[name="${name}" i]`) ??
(() => {
const el = document.createElement('meta');
el.setAttribute('name', name);
document.head.prepend(el);
return el;
})()
);
}

const App = () => {
const [currentTab, updateCurrentTab] = useWorkspaceTabRouter();
const { status, projectId } = useAtlasProxySignIn();
const { status, projectParams } = useAtlasProxySignIn();
const { projectId, csrfToken, csrfTime } = projectParams ?? {};

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

useLayoutEffect(() => {
getMetaEl('csrf-token').setAttribute('content', csrfToken ?? '');
getMetaEl('csrf-time').setAttribute('content', csrfTime ?? '');
}, [csrfToken, csrfTime]);

if (status === 'checking') {
return null;
}
Expand Down
27 changes: 20 additions & 7 deletions packages/compass-web/sandbox/sandbox-atlas-sign-in.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -12,12 +12,18 @@ console.info(

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

type ProjectParams = {
projectId: string;
csrfToken: string;
csrfTime: string;
};

type AtlasLoginReturnValue =
| {
status: 'checking' | 'signed-out';
projectId: null;
projectParams: null;
}
| { status: 'signed-in'; projectId: string };
| { status: 'signed-in'; projectParams: ProjectParams };

const bodyContainerStyles = css({
display: 'flex',
Expand Down Expand Up @@ -64,7 +70,9 @@ const IS_CI =

export function useAtlasProxySignIn(): AtlasLoginReturnValue {
const [status, setStatus] = useState<SignInStatus>('checking');
const [projectId, setProjectId] = useState<string | null>(null);
const [projectParams, setProjectParams] = useState<ProjectParams | null>(
null
);

const signIn = ((window as any).__signIn = useCallback(async () => {
try {
Expand Down Expand Up @@ -104,7 +112,12 @@ export function useAtlasProxySignIn(): AtlasLoginReturnValue {
if (!projectId) {
throw new Error('failed to get projectId');
}
setProjectId(projectId);
const { csrfToken, csrfTime } = await fetch(
`/cloud-mongodb-com/v2/${projectId}/params`
).then((res) => {
return res.json();
});
setProjectParams({ projectId, csrfToken, csrfTime });
setStatus('signed-in');
if (IS_CI) {
return;
Expand Down Expand Up @@ -151,12 +164,12 @@ export function useAtlasProxySignIn(): AtlasLoginReturnValue {
if (status === 'checking' || status === 'signed-out') {
return {
status,
projectId: null,
projectParams: null,
};
}

if (status === 'signed-in' && projectId) {
return { status, projectId };
if (status === 'signed-in' && projectParams) {
return { status, projectParams };
}

throw new Error('Weird state, ask for help in Compass dev channel');
Expand Down
3 changes: 0 additions & 3 deletions packages/compass-web/scripts/electron-proxy.js
Original file line number Diff line number Diff line change
Expand Up @@ -168,14 +168,11 @@ class AtlasCloudAuthenticator {
}

async getCloudHeaders() {
// Order is important, fetching data can update the cookies
const csrfHeaders = await this.#getCSRFHeaders();
const cookie = (await this.#getCloudSessionCookies()).join('; ');
return {
cookie,
host: CLOUD_HOST,
origin: CLOUD_ORIGIN,
...csrfHeaders,
};
}

Expand Down

0 comments on commit f0655e0

Please sign in to comment.