🔒 feat: Authenticated Image Requests (#2389)

* 🔒 feat: Authenticated Image Requests

* fix: reserved keyword `static`
This commit is contained in:
Danny Avila 2024-04-11 02:50:57 -04:00 committed by GitHub
parent c19dfddd0f
commit 9277e2a0c5
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
7 changed files with 59 additions and 6 deletions

View file

@ -6,6 +6,8 @@ module.exports = {
clientPath: path.resolve(__dirname, '..', '..', 'client'),
dist: path.resolve(__dirname, '..', '..', 'client', 'dist'),
publicPath: path.resolve(__dirname, '..', '..', 'client', 'public'),
fonts: path.resolve(__dirname, '..', '..', 'client', 'public', 'fonts'),
assets: path.resolve(__dirname, '..', '..', 'client', 'public', 'assets'),
imageOutput: path.resolve(__dirname, '..', '..', 'client', 'public', 'images'),
structuredTools: path.resolve(__dirname, '..', 'app', 'clients', 'tools', 'structured'),
pluginManifest: path.resolve(__dirname, '..', 'app', 'clients', 'tools', 'manifest.json'),

View file

@ -76,14 +76,14 @@ const refreshController = async (req, res) => {
}
try {
let payload;
payload = jwt.verify(refreshToken, process.env.JWT_REFRESH_SECRET);
const userId = payload.id;
const user = await User.findOne({ _id: userId });
const payload = jwt.verify(refreshToken, process.env.JWT_REFRESH_SECRET);
const user = await User.findOne({ _id: payload.id });
if (!user) {
return res.status(401).redirect('/login');
}
const userId = payload.id;
if (process.env.NODE_ENV === 'CI') {
const token = await setAuthTokens(userId, res);
const userObj = user.toJSON();
@ -118,6 +118,6 @@ module.exports = {
getUserController,
refreshController,
registrationController,
resetPasswordRequestController,
resetPasswordController,
resetPasswordRequestController,
};

View file

@ -6,6 +6,7 @@ const axios = require('axios');
const express = require('express');
const passport = require('passport');
const mongoSanitize = require('express-mongo-sanitize');
const validateImageRequest = require('./middleware/validateImageRequest');
const errorController = require('./controllers/ErrorController');
const { jwtLogin, passportLogin } = require('~/strategies');
const configureSocialLogins = require('./socialLogins');
@ -43,7 +44,8 @@ const startServer = async () => {
app.use(mongoSanitize());
app.use(express.urlencoded({ extended: true, limit: '3mb' }));
app.use(express.static(app.locals.paths.dist));
app.use(express.static(app.locals.paths.publicPath));
app.use(express.static(app.locals.paths.fonts));
app.use(express.static(app.locals.paths.assets));
app.set('trust proxy', 1); // trust first proxy
app.use(cors());
@ -82,6 +84,7 @@ const startServer = async () => {
app.use('/api/config', routes.config);
app.use('/api/assistants', routes.assistants);
app.use('/api/files', await routes.files.initialize());
app.use('/images/', validateImageRequest, routes.staticRoute);
app.use((req, res) => {
res.status(404).sendFile(path.join(app.locals.paths.dist, 'index.html'));

View file

@ -14,6 +14,7 @@ const concurrentLimiter = require('./concurrentLimiter');
const validateMessageReq = require('./validateMessageReq');
const buildEndpointOption = require('./buildEndpointOption');
const validateRegistration = require('./validateRegistration');
const validateImageRequest = require('./validateImageRequest');
const moderateText = require('./moderateText');
const noIndex = require('./noIndex');
@ -33,6 +34,7 @@ module.exports = {
validateMessageReq,
buildEndpointOption,
validateRegistration,
validateImageRequest,
validateModel,
moderateText,
noIndex,

View file

@ -0,0 +1,37 @@
const cookies = require('cookie');
const jwt = require('jsonwebtoken');
const { logger } = require('~/config');
/**
* Middleware to validate image request
*/
function validateImageRequest(req, res, next) {
const refreshToken = req.headers.cookie ? cookies.parse(req.headers.cookie).refreshToken : null;
if (!refreshToken) {
logger.warn('[validateImageRequest] Refresh token not provided');
return res.status(401).send('Unauthorized');
}
let payload;
try {
payload = jwt.verify(refreshToken, process.env.JWT_REFRESH_SECRET);
} catch (err) {
logger.warn('[validateImageRequest]', err);
return res.status(403).send('Access Denied');
}
const currentTimeInSeconds = Math.floor(Date.now() / 1000);
if (payload.exp < currentTimeInSeconds) {
logger.warn('[validateImageRequest] Refresh token expired');
return res.status(403).send('Access Denied');
}
if (req.path.includes(payload.id)) {
logger.debug('[validateImageRequest] Image request validated');
next();
} else {
res.status(403).send('Access Denied');
}
}
module.exports = validateImageRequest;

View file

@ -17,6 +17,7 @@ const user = require('./user');
const config = require('./config');
const assistants = require('./assistants');
const files = require('./files');
const staticRoute = require('./static');
module.exports = {
search,
@ -38,4 +39,5 @@ module.exports = {
config,
assistants,
files,
staticRoute,
};

View file

@ -0,0 +1,7 @@
const express = require('express');
const paths = require('~/config/paths');
const router = express.Router();
router.use(express.static(paths.imageOutput));
module.exports = router;