♻️(frontend) support Bearer in servers/y-provider
Support passing API Token as Bearer in the Authorization-header. Signed-off-by: Stephan Meijer <me@stephanmeijer.com>
This commit is contained in:
@@ -26,7 +26,7 @@ describe('Server Tests', () => {
|
|||||||
|
|
||||||
expect(response.status).toBe(401);
|
expect(response.status).toBe(401);
|
||||||
expect(response.body).toStrictEqual({
|
expect(response.body).toStrictEqual({
|
||||||
error: 'Forbidden: Invalid API Key',
|
error: 'Unauthorized: Invalid API Key',
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|||||||
@@ -18,10 +18,49 @@ import {
|
|||||||
COLLABORATION_SERVER_ORIGIN as origin,
|
COLLABORATION_SERVER_ORIGIN as origin,
|
||||||
} from '../src/env';
|
} from '../src/env';
|
||||||
|
|
||||||
|
const expectedBlocks = [
|
||||||
|
{
|
||||||
|
children: [],
|
||||||
|
content: [
|
||||||
|
{
|
||||||
|
styles: {},
|
||||||
|
text: 'Example document',
|
||||||
|
type: 'text',
|
||||||
|
},
|
||||||
|
],
|
||||||
|
id: expect.any(String),
|
||||||
|
props: {
|
||||||
|
backgroundColor: 'default',
|
||||||
|
isToggleable: false,
|
||||||
|
level: 1,
|
||||||
|
textAlignment: 'left',
|
||||||
|
textColor: 'default',
|
||||||
|
},
|
||||||
|
type: 'heading',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
children: [],
|
||||||
|
content: [
|
||||||
|
{
|
||||||
|
styles: {},
|
||||||
|
text: 'Lorem ipsum dolor sit amet.',
|
||||||
|
type: 'text',
|
||||||
|
},
|
||||||
|
],
|
||||||
|
id: expect.any(String),
|
||||||
|
props: {
|
||||||
|
backgroundColor: 'default',
|
||||||
|
textAlignment: 'left',
|
||||||
|
textColor: 'default',
|
||||||
|
},
|
||||||
|
type: 'paragraph',
|
||||||
|
},
|
||||||
|
];
|
||||||
|
|
||||||
console.error = vi.fn();
|
console.error = vi.fn();
|
||||||
|
|
||||||
describe('Server Tests', () => {
|
describe('Server Tests', () => {
|
||||||
test('POST /api/convert with incorrect API key should responds with 401', async () => {
|
test('POST /api/convert with incorrect API key responds with 401', async () => {
|
||||||
const app = initApp();
|
const app = initApp();
|
||||||
|
|
||||||
const response = await request(app)
|
const response = await request(app)
|
||||||
@@ -32,11 +71,11 @@ describe('Server Tests', () => {
|
|||||||
|
|
||||||
expect(response.status).toBe(401);
|
expect(response.status).toBe(401);
|
||||||
expect(response.body).toStrictEqual({
|
expect(response.body).toStrictEqual({
|
||||||
error: 'Forbidden: Invalid API Key',
|
error: 'Unauthorized: Invalid API Key',
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
test('POST /api/convert with a Bearer token', async () => {
|
test('POST /api/convert with incorrect Bearer token responds with 401', async () => {
|
||||||
const app = initApp();
|
const app = initApp();
|
||||||
|
|
||||||
const response = await request(app)
|
const response = await request(app)
|
||||||
@@ -45,10 +84,9 @@ describe('Server Tests', () => {
|
|||||||
.set('authorization', 'Bearer test-secret-api-key')
|
.set('authorization', 'Bearer test-secret-api-key')
|
||||||
.set('content-type', 'application/json');
|
.set('content-type', 'application/json');
|
||||||
|
|
||||||
// Warning: Changing the authorization header to Bearer token format will break backend compatibility with this microservice.
|
|
||||||
expect(response.status).toBe(401);
|
expect(response.status).toBe(401);
|
||||||
expect(response.body).toStrictEqual({
|
expect(response.body).toStrictEqual({
|
||||||
error: 'Forbidden: Invalid API Key',
|
error: 'Unauthorized: Invalid API Key',
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
@@ -83,68 +121,33 @@ describe('Server Tests', () => {
|
|||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
test('POST /api/convert with correct content', async () => {
|
test.each([[apiKey], [`Bearer ${apiKey}`]])(
|
||||||
const app = initApp();
|
'POST /api/convert with correct content with Authorization: %s',
|
||||||
|
async (authHeader) => {
|
||||||
|
const app = initApp();
|
||||||
|
|
||||||
const document = [
|
const document = [
|
||||||
'# Example document',
|
'# Example document',
|
||||||
'',
|
'',
|
||||||
'Lorem ipsum dolor sit amet.',
|
'Lorem ipsum dolor sit amet.',
|
||||||
'',
|
'',
|
||||||
].join('\n');
|
].join('\n');
|
||||||
|
|
||||||
const response = await request(app)
|
const response = await request(app)
|
||||||
.post('/api/convert')
|
.post('/api/convert')
|
||||||
.set('origin', origin)
|
.set('Origin', origin)
|
||||||
.set('authorization', apiKey)
|
.set('Authorization', authHeader)
|
||||||
.set('content-type', 'application/json')
|
.send(document);
|
||||||
.send(document);
|
|
||||||
|
|
||||||
expect(response.status).toBe(200);
|
expect(response.status).toBe(200);
|
||||||
expect(response.body).toBeInstanceOf(Buffer);
|
expect(response.body).toBeInstanceOf(Buffer);
|
||||||
|
|
||||||
const editor = ServerBlockNoteEditor.create();
|
const editor = ServerBlockNoteEditor.create();
|
||||||
const doc = new Y.Doc();
|
const doc = new Y.Doc();
|
||||||
Y.applyUpdate(doc, response.body);
|
Y.applyUpdate(doc, response.body);
|
||||||
const blocks = editor.yDocToBlocks(doc, 'document-store');
|
const blocks = editor.yDocToBlocks(doc, 'document-store');
|
||||||
|
|
||||||
expect(blocks).toStrictEqual([
|
expect(blocks).toStrictEqual(expectedBlocks);
|
||||||
{
|
},
|
||||||
children: [],
|
);
|
||||||
content: [
|
|
||||||
{
|
|
||||||
styles: {},
|
|
||||||
text: 'Example document',
|
|
||||||
type: 'text',
|
|
||||||
},
|
|
||||||
],
|
|
||||||
id: expect.any(String),
|
|
||||||
props: {
|
|
||||||
backgroundColor: 'default',
|
|
||||||
isToggleable: false,
|
|
||||||
level: 1,
|
|
||||||
textAlignment: 'left',
|
|
||||||
textColor: 'default',
|
|
||||||
},
|
|
||||||
type: 'heading',
|
|
||||||
},
|
|
||||||
{
|
|
||||||
children: [],
|
|
||||||
content: [
|
|
||||||
{
|
|
||||||
styles: {},
|
|
||||||
text: 'Lorem ipsum dolor sit amet.',
|
|
||||||
type: 'text',
|
|
||||||
},
|
|
||||||
],
|
|
||||||
id: expect.any(String),
|
|
||||||
props: {
|
|
||||||
backgroundColor: 'default',
|
|
||||||
textAlignment: 'left',
|
|
||||||
textColor: 'default',
|
|
||||||
},
|
|
||||||
type: 'paragraph',
|
|
||||||
},
|
|
||||||
]);
|
|
||||||
});
|
|
||||||
});
|
});
|
||||||
|
|||||||
@@ -24,12 +24,19 @@ export const httpSecurity = (
|
|||||||
res: Response,
|
res: Response,
|
||||||
next: NextFunction,
|
next: NextFunction,
|
||||||
): void => {
|
): void => {
|
||||||
// Secret API Key check
|
let apiKey = req.headers['authorization'];
|
||||||
// Note: Changing this header to Bearer token format will break backend compatibility with this microservice.
|
|
||||||
const apiKey = req.headers['authorization'];
|
|
||||||
|
|
||||||
if (!apiKey || !VALID_API_KEYS.includes(apiKey)) {
|
if (!apiKey) {
|
||||||
res.status(401).json({ error: 'Forbidden: Invalid API Key' });
|
res.status(401).json({ error: 'Unauthorized: No credentials given' });
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (apiKey?.startsWith('Bearer ')) {
|
||||||
|
apiKey = apiKey.slice('Bearer '.length);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!VALID_API_KEYS.includes(apiKey)) {
|
||||||
|
res.status(401).json({ error: 'Unauthorized: Invalid API Key' });
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user