Skip to content
Merged
Show file tree
Hide file tree
Changes from 5 commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 3 additions & 0 deletions .env.template
Original file line number Diff line number Diff line change
Expand Up @@ -20,3 +20,6 @@ SESSION_SECRET=

FEEDBACK_SLACK_URL=
FEEDBACK_URL_LINK=

# frame-ancestors attribute of CSP. Separate multiple values with a space
FRAME_ANCESTORS=
6 changes: 1 addition & 5 deletions index.html
Original file line number Diff line number Diff line change
Expand Up @@ -8,13 +8,9 @@
<title>MCP</title>
</head>

<script>
window.global ||= window;
</script>

<body>
<div id="root"></div>
<script type="module" src="/src/mount.ts"></script>
</body>

</html>
</html>
30 changes: 30 additions & 0 deletions package-lock.json

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

5 changes: 3 additions & 2 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@
"npm": "^11.0.0"
},
"scripts": {
"dev": "node server.js --dev",
"dev": "node server.js --local-dev",
"start": "node server.js",
"build": "tsc && vite build",
"lint": "eslint ./src --report-unused-disable-directives --max-warnings 0",
Expand All @@ -23,9 +23,10 @@
"@fastify/autoload": "^6.3.0",
"@fastify/cookie": "^11.0.2",
"@fastify/env": "^5.0.2",
"@fastify/helmet": "^13.0.1",
"@fastify/http-proxy": "^11.1.2",
"@fastify/sensible": "^6.0.3",
"@fastify/secure-session": "^8.2.0",
"@fastify/sensible": "^6.0.3",
"@fastify/session": "^11.1.0",
"@fastify/static": "^8.1.1",
"@fastify/vite": "^8.1.3",
Expand Down
25 changes: 22 additions & 3 deletions server.js
Original file line number Diff line number Diff line change
@@ -1,18 +1,22 @@
import Fastify from 'fastify';
import FastifyVite from '@fastify/vite';
import helmet from '@fastify/helmet';
import { fileURLToPath } from 'node:url';
import path from 'node:path';
import dotenv from 'dotenv';
import proxy from './server/app.js';
import envPlugin from "./server/config/env.js";
import { copyFileSync } from 'node:fs';

dotenv.config();

const isDev = process.argv.includes('--dev');
const isLocalDev = process.argv.includes('--local-dev');

const __filename = fileURLToPath(import.meta.url);
const __dirname = path.dirname(__filename);
const frontendConfigLocation = isDev ? 'public/frontend-config.json' : 'dist/client/frontend-config.json';
const frontendConfigLocation = isLocalDev
? 'public/frontend-config.json'
: 'dist/client/frontend-config.json';

if (process.env.FRONTEND_CONFIG_PATH !== undefined && process.env.FRONTEND_CONFIG_PATH.length > 0) {
console.log('FRONTEND_CONFIG_PATH is specified. Will copy the frontend-config from there.');
Expand All @@ -24,13 +28,28 @@ const fastify = Fastify({
logger: true,
});

await fastify.register(envPlugin);

fastify.register(
helmet,
{
contentSecurityPolicy: {
directives: {
"connect-src": ["'self'", "sdk.openui5.org"],
"script-src": isLocalDev ? ["'self'", "'unsafe-inline'"] : ["'self'"],
"frame-ancestors": [fastify.config.FRAME_ANCESTORS]
},
}
}
)

fastify.register(proxy, {
prefix: '/api',
});

await fastify.register(FastifyVite, {
root: __dirname,
dev: isDev,
dev: isLocalDev,
spa: true,
});

Expand Down
10 changes: 4 additions & 6 deletions server/app.js
Original file line number Diff line number Diff line change
@@ -1,16 +1,14 @@
import path, { join, dirname } from 'node:path';
import { fileURLToPath } from 'node:url';
import AutoLoad from '@fastify/autoload';
import envPlugin from './config/env.js';
import encryptedSession from './encrypted-session.js';
import path, { join, dirname } from "node:path";
import { fileURLToPath } from "node:url";
import AutoLoad from "@fastify/autoload";
import encryptedSession from "./encrypted-session.js";

export const options = {};

const __filename = fileURLToPath(import.meta.url);
const __dirname = dirname(__filename);

export default async function (fastify, opts) {
await fastify.register(envPlugin);
fastify.register(encryptedSession, {
...opts,
});
Expand Down
2 changes: 2 additions & 0 deletions server/config/env.js
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@ const schema = {
'COOKIE_SECRET',
'SESSION_SECRET',
'API_BACKEND_URL',
'FRAME_ANCESTORS',
],
properties: {
// Application variables (.env)
Expand All @@ -27,6 +28,7 @@ const schema = {
API_BACKEND_URL: { type: 'string' },
FEEDBACK_SLACK_URL: { type: 'string' },
FEEDBACK_URL_LINK: { type: 'string' },
FRAME_ANCESTORS: { type: 'string' },

// System variables
NODE_ENV: { type: 'string', enum: ['development', 'production'] },
Expand Down
12 changes: 7 additions & 5 deletions server/encrypted-session.js
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,7 @@ export const ENCRYPTED_COOKIE_KEY_ENCRYPTION_KEY = 'encryptionKey';
export const SESSION_COOKIE_NAME = 'session-cookie';

async function encryptedSession(fastify) {
const { COOKIE_SECRET, SESSION_SECRET, NODE_ENV } = fastify.config;
const { COOKIE_SECRET, SESSION_SECRET } = fastify.config;

await fastify.register(fastifyCookie);

Expand All @@ -31,8 +31,9 @@ async function encryptedSession(fastify) {
cookie: {
path: '/',
httpOnly: true,
sameSite: 'lax',
secure: NODE_ENV === 'production',
sameSite: "None", // cross-site cookies are needed for the session to work when embedded. By setting CORS to None and CSP.frame-anchestors we restrict the api calls from the browser that contain the cookies to originating from our site only.
partitioned: true, // use for modern isolation of third party cookies when embedded, every embedded iframe (or not embedded) gets its own cookie partition
secure: true,
maxAge: 60 * 60 * 24 * 7, // 7 days
},
});
Expand All @@ -43,8 +44,9 @@ async function encryptedSession(fastify) {
cookie: {
path: '/',
httpOnly: true,
sameSite: 'lax',
secure: NODE_ENV === 'production',
sameSite: "None", // see secureSession cookie for explanation
partitioned: true, // see secureSession cookie for explanation
secure: true,
maxAge: 60 * 60 * 24 * 7, // 7 days
},
});
Expand Down
Loading