fix(sdk): render CLI OAuth success page on localhost (twenty dev) (#22131)
This commit is contained in:
@@ -2,14 +2,26 @@ import http from 'node:http';
|
||||
|
||||
import { startCallbackServer } from '../callback-server';
|
||||
|
||||
const httpGet = (url: string): Promise<{ status: number; body: string }> =>
|
||||
const httpGet = (
|
||||
url: string,
|
||||
): Promise<{
|
||||
status: number;
|
||||
body: string;
|
||||
headers: http.IncomingHttpHeaders;
|
||||
}> =>
|
||||
new Promise((resolve, reject) => {
|
||||
http
|
||||
.get(url, (res) => {
|
||||
let body = '';
|
||||
|
||||
res.on('data', (chunk: string) => (body += chunk));
|
||||
res.on('end', () => resolve({ status: res.statusCode ?? 0, body }));
|
||||
res.on('end', () =>
|
||||
resolve({
|
||||
status: res.statusCode ?? 0,
|
||||
body,
|
||||
headers: res.headers,
|
||||
}),
|
||||
);
|
||||
})
|
||||
.on('error', reject);
|
||||
});
|
||||
@@ -44,6 +56,46 @@ describe('startCallbackServer', () => {
|
||||
}
|
||||
});
|
||||
|
||||
it('should send the success page with an explicit Content-Length (not chunked)', async () => {
|
||||
const server = await startCallbackServer();
|
||||
|
||||
try {
|
||||
const waitPromise = server.waitForCallback();
|
||||
|
||||
const response = await httpGet(
|
||||
`${server.callbackUrl}?code=test-auth-code`,
|
||||
);
|
||||
|
||||
await waitPromise;
|
||||
|
||||
expect(response.status).toBe(200);
|
||||
expect(response.headers['transfer-encoding']).toBeUndefined();
|
||||
expect(response.headers['content-length']).toBe(
|
||||
String(Buffer.byteLength(response.body)),
|
||||
);
|
||||
expect(response.body).toContain('Authentication successful');
|
||||
} finally {
|
||||
server.close();
|
||||
}
|
||||
});
|
||||
|
||||
it('should deliver the full page even when the server is closed right after the callback resolves', async () => {
|
||||
const server = await startCallbackServer();
|
||||
|
||||
const responsePromise = httpGet(
|
||||
`${server.callbackUrl}?code=test-auth-code`,
|
||||
);
|
||||
|
||||
await server.waitForCallback();
|
||||
server.close();
|
||||
|
||||
const response = await responsePromise;
|
||||
|
||||
expect(response.status).toBe(200);
|
||||
expect(response.body).toContain('Authentication successful');
|
||||
expect(response.body).toContain('</html>');
|
||||
});
|
||||
|
||||
it('should resolve with error when callback contains an error', async () => {
|
||||
const server = await startCallbackServer();
|
||||
|
||||
|
||||
@@ -195,7 +195,7 @@ export const startCallbackServer = (options?: {
|
||||
const url = new URL(req.url ?? '/', `http://127.0.0.1`);
|
||||
|
||||
if (url.pathname !== '/callback') {
|
||||
res.writeHead(404);
|
||||
res.writeHead(404, { Connection: 'close' });
|
||||
res.end('Not found');
|
||||
|
||||
return;
|
||||
@@ -205,23 +205,28 @@ export const startCallbackServer = (options?: {
|
||||
const error = url.searchParams.get('error');
|
||||
const isDarkMode = url.searchParams.get('theme') === 'dark';
|
||||
|
||||
const headers = {
|
||||
const result: CallbackResult = code
|
||||
? { success: true, code }
|
||||
: {
|
||||
success: false,
|
||||
error:
|
||||
error ??
|
||||
url.searchParams.get('error_description') ??
|
||||
'Unknown error',
|
||||
};
|
||||
|
||||
const body = result.success
|
||||
? successHtml(isDarkMode)
|
||||
: errorHtml(result.error, isDarkMode);
|
||||
|
||||
res.writeHead(200, {
|
||||
'Content-Type': 'text/html',
|
||||
'Content-Length': Buffer.byteLength(body),
|
||||
Connection: 'close',
|
||||
};
|
||||
});
|
||||
|
||||
if (code) {
|
||||
res.writeHead(200, headers);
|
||||
res.end(successHtml(isDarkMode));
|
||||
callbackResolve({ success: true, code });
|
||||
} else {
|
||||
const errorMessage =
|
||||
error ?? url.searchParams.get('error_description') ?? 'Unknown error';
|
||||
|
||||
res.writeHead(200, headers);
|
||||
res.end(errorHtml(errorMessage, isDarkMode));
|
||||
callbackResolve({ success: false, error: errorMessage });
|
||||
}
|
||||
res.on('close', () => callbackResolve(result));
|
||||
res.end(body);
|
||||
});
|
||||
|
||||
server.listen(0, '127.0.0.1', () => {
|
||||
@@ -252,8 +257,8 @@ export const startCallbackServer = (options?: {
|
||||
},
|
||||
close: () => {
|
||||
clearTimeout(timeoutHandle);
|
||||
server.closeAllConnections();
|
||||
server.close();
|
||||
server.closeIdleConnections();
|
||||
},
|
||||
});
|
||||
});
|
||||
|
||||
Reference in New Issue
Block a user