I tried to apply my frontend pages to 2 different servers: Django and Express.js. In Django I used csrf tokens and so I want to use same frontend pages for 2 servers. So I tried to apply csrf tokens to my express server, and I got error when I tried to move to my admin page. In Django all works fine, but smth wrong in Express server. My Express version is 4.19.2. Also I don’t know how to use same html and script.js files for both Django server and Express
So here is my app.js with all settings:
const express = require('express');
const mongoose = require('mongoose');
const session = require('express-session');
const crypto = require('crypto');
const path = require('path');
const routes = require('./routes');
const dotenv = require('dotenv');
const csrf = require('csurf');
const cookieParser = require('cookie-parser');
const bodyParser = require('body-parser');
dotenv.config();
const app = express();
const port = 3000;
const secretKey = crypto.randomBytes(32).toString('hex');
const mongoURI = process.env.MONGODB_URI || 'mongodb://127.0.0.1:27017/mydatabase';
mongoose.connect(mongoURI, {
useNewUrlParser: true,
useUnifiedTopology: true,
});
const db = mongoose.connection;
db.on('error', console.error.bind(console, 'MongoDB connection error:'));
db.once('open', async () => {
console.log('Connected to MongoDB');
});
app.use(express.static(path.join(__dirname, '..','public')));
app.use(session({
secret: secretKey,
resave: false,
saveUninitialized: true,
cookie: { secure: false },
}));
app.use(cookieParser());
const csrfProtection = csrf({ cookie: true });
app.use(bodyParser.urlencoded({ extended: false }));
app.use(bodyParser.json());
app.use(csrfProtection);
app.use((req, res, next) => {
res.locals.csrfToken = req.csrfToken();
res.locals.using_django = false;
next();
});
app.use('/', routes);
app.get('/public/styles.css', (req, res) => {
res.sendFile(path.join(__dirname, '..','public', 'styles.css'));
});
app.get('/public/script.js', (req, res) => {
res.sendFile(path.join(__dirname, '..','public', 'script.js'));
});
app.get('/', (req, res) => {
res.sendFile(path.join(__dirname, '..', 'views', 'main.html'));
});
app.get('/modify-gifs', csrfProtection, (req, res) => {
if (!req.session.isAuthenticated) {
res.redirect('/');
return;
}
res.sendFile(path.join(__dirname, '..', 'views', 'Modify-GIF.html'));
});
app.use((req, res, next) => {
res.status(404).sendFile(path.join(__dirname, '..', 'views', '404.html'));
});
app.listen(port, () => {
console.log(`Server is running on port ${port}`);
});
authentification route in routes.js:
router.post('/check-auth', (req, res) => {
const { login, password } = req.body;
if (login === process.env.ADMIN_LOGIN && password === process.env.ADMIN_PASSWORD) {
req.session.isAuthenticated = true;
res.status(200).json({ isAuthenticated: true, message: 'Authentication successful' });
} else {
req.session.isAuthenticated = false;
res.status(401).json({ isAuthenticated: false, message: 'Invalid login or password' });
}
});
script.js:
let isAuthenticated = false;
let csrfToken = '';
document.addEventListener('DOMContentLoaded', function () {
csrfToken = getCsrfToken();
function getCsrfToken() {
const tokenElement = document.querySelector('meta[name="csrf-token"]');
if (tokenElement) {
return tokenElement.getAttribute('content');
} else {
console.error('CSRF token not found');
return '';
}
}
});
function authenticateAndShowTab() { //TODO: fix with sessions
const login = prompt('Enter login:');
const password = prompt('Enter password:');
fetch('/check-auth', {
method: 'POST',
headers: {
'Content-Type': 'application/json',
'X-CSRFToken': csrfToken,
},
body: JSON.stringify({ login, password }),
})
.then(response => {
console.log('Response status:', response.status);
return response.json();
})
.then(data => {
console.log('Response data:', data);
if (data.isAuthenticated) {
isAuthenticated = true;
sessionStorage.setItem('isAuthenticated', 'true');
showTab('modGif');
} else {
isAuthenticated = false;
alert('Invalid login or password. Please try again.');
}
})
.catch(error => {
console.error('Error checking authentication:', error);
});
}
function showTab(tabName) {
const savedAuthStatus = sessionStorage.getItem('isAuthenticated');
isAuthenticated = savedAuthStatus === 'true';
if (tabName === 'main') {
window.location.href = '/';
} else if (tabName === 'modGif') {
if (!isAuthenticated){
authenticateAndShowTab();
return;
}
window.location.href = '/modify-gifs';
}
}
Also, here is Modify-Gif.html:
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<meta name="csrf-token" content="{{ csrf_token }}">
<title>Modify GIFs</title>
<link rel="stylesheet" type="text/css" href="/public/styles.css">
<script src="/public/script.js"></script>
</head>
<body>
<div id="tabs">
<button class="tab-button" data-tab="main" onclick="showTab('main')">Main</button>
<button class="tab-button active-tab" data-tab="modGif" onclick="showTab('modGif')">Modify Gifs</button>
</div>
<form id="uploadForm" action="/upload" method="post" enctype="multipart/form-data" style="display: block;">
{% csrf_token %}
<h2>File Upload</h2>
<input type="file" name="file">
<input type="text" name="filename" id="filename" placeholder="Enter GIF name">
<input type="text" name="attributes" id="attributesInput" placeholder="Enter attributes (comma-separated)">
<input type="submit" value="Upload File">
</form>
<div id="ViewGIFForm" style="display: block;">
<h2>View GIF</h2>
<input type="text" id="gifIdInput" placeholder="GIF ID">
<button onclick="openGif()">Open GIF</button>
<button onclick="deleteGif()">Delete GIF</button>
<video id="" controls class="styled-video" style="display: none;"></video>
<img id="gifImage" style="display: none;">
<div id="gifAttributes"></div>
<div id="gifName"></div>
</div>
<script>
displayGifList();
function deleteGif() {
const gifIdInputDel = document.getElementById('gifIdInput');
const gifId = gifIdInputDel.value;
if (!gifId) {
alert('Please enter GIF ID to delete.');
return;
}
fetch(`/gif/${gifId}`, {
method: 'DELETE',
headers: {
'X-CSRFToken': csrfToken
}
})
.then(response => {
if (response.ok) {
console.log('GIF deleted successfully.');
alert('GIF deleted successfully.');
} else if (response.status === 404) {
console.error('GIF not found.');
alert('GIF not found.');
} else {
console.error('Error deleting GIF:', response.status);
alert('Error deleting GIF.');
}
})
.catch(error => {
console.error('Error deleting GIF:', error);
alert('Error deleting GIF.');
});
}
</script>
</body>
</html>
and main.html:
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<meta name="csrf-token" content="{{ csrf_token }}">
<title>All GIF List</title>
<link rel="stylesheet" type="text/css" href="/public/styles.css">
<script src="/public/script.js"></script>
</head>
<body>
<div id="tabs">
<button class="tab-button active-tab" data-tab="main" onclick="showTab('main')">Main</button>
<button class="tab-button" data-tab="modGif" onclick="showTab('modGif')">Modify Gifs</button>
</div>
<div id="ViewAndDownloadGIFForm" style="display: block;">
<h2>View and Download GIF</h2>
<input type="text" id="gifIdInput" placeholder="GIF ID">
<input type="text" id="downloadFileNameInput" name="filename" placeholder="Enter file name (in Latin)">
<button onclick="openGif()">Open GIF</button>
<button onclick="downloadGif()">Download GIF</button>
<video id="" controls class="styled-video" style="display: none;"></video>
<img id="gifImage" style="display: none;">
<div id="gifAttributes"></div>
<div id="gifName"></div>
</div>
</body>
</html>
P.S. Sorry for the possibly stupid question or bad wording. This is my first public question here
I tried to remove the authentication (by commenting out the code) but got the same error when I tried to send a GIF to the database.
Also i tried to include some middleware from this question:
app.use(express.urlencoded({ extended: true }));
To use the same files for different servers, I tried to apply a condition, but it only works on Django (on Express it appears visually on the page):
Modify-Gif.html
{% if using_django %}
{% csrf_token %}
{% else %}
<input type="hidden" name="_csrf" value="{{ csrf_token }}">
{% endif %}
Also i tried to replace this:
<meta name="csrf-token" content="{{ csrf_token }}">
to this:
<meta name="csrf-token" content="{{csrfToken}}">
but still i got same error
And if i keep this change i will get error in Django:
Forbidden (CSRF token from the ‘X-Csrftoken’ HTTP header has incorrect length.): /check-auth
[07/Aug/2024 17:19:18] “POST /check-auth HTTP/1.1” 403 2563
Also i tried to replace some code from answer to this question:
app.use(crsfProtection);
to
app.use(crsfProtection());
But then i got error:
return req[cookieKey]
^
TypeError: Cannot read properties of undefined (reading ‘cookies’)