refactor: handle prefix on login

- post-login redirect should account for prefix
- scope cookies to each path by prefix
This commit is contained in:
Carlos Valente
2025-12-18 14:20:40 +01:00
committed by Carlos Valente
parent deb1f043c7
commit 9c5116f197
3 changed files with 31 additions and 19 deletions
+1
View File
@@ -40,6 +40,7 @@ override.css
# working stuff
**/TODO.md
**.local.**
# docker utils
ontime-db
+2 -1
View File
@@ -17,7 +17,7 @@ import { consoleSuccess, consoleHighlight, consoleError } from './utils/console.
// Import middleware configuration
import { bodyParser } from './middleware/bodyParser.js';
import { compressedStatic } from './middleware/staticGZip.js';
import { loginRouter, makeAuthenticateMiddleware } from './middleware/authenticate.js';
import { makeLoginRouter, makeAuthenticateMiddleware } from './middleware/authenticate.js';
// Import Routers
import { appRouter } from './api-data/index.js';
@@ -83,6 +83,7 @@ app.options('*splat', cors()); // enable pre-flight cors
app.use(bodyParser);
app.use(cookieParser());
const { authenticate, authenticateAndRedirect } = makeAuthenticateMiddleware(prefix);
const loginRouter = makeLoginRouter(prefix);
// implement health check route
app.get(`${prefix}/health`, (_req, res) => {
+28 -18
View File
@@ -21,24 +21,33 @@ const publicAssets = new Set([
'/site.webmanifest',
]);
export const loginRouter = express.Router();
/**
* Creates a login router with the provided prefix
* @param {string} prefix - Prefix is used for the client hashes in Ontime Cloud
*/
export function makeLoginRouter(prefix: string) {
const router = express.Router();
// serve static files at root
loginRouter.use('/', express.static(srcFiles.login));
// serve static files at root
router.use('/', express.static(srcFiles.login));
// verify password and set cookies + redirect appropriately
loginRouter.post('/', (req, res) => {
res.clearCookie('token');
const { password: reqPassword, redirect } = req.body;
// verify password and set cookies + redirect appropriately
router.post('/', (req, res) => {
res.clearCookie('token', { path: prefix || '/' });
const { password: reqPassword, redirect } = req.body;
if (hashPassword(reqPassword) === hashedPassword) {
setSessionCookie(res, hashedPassword);
res.redirect(redirect || '/');
return;
}
if (hashPassword(reqPassword) === hashedPassword) {
setSessionCookie(res, hashedPassword, prefix);
// If redirect is provided, use it; otherwise redirect to the prefix root
res.redirect(redirect || (prefix ? `${prefix}/` : '/'));
return;
}
res.status(401).send('Unauthorized');
});
res.status(401).send('Unauthorized');
});
return router;
}
/**
* Express middleware to authenticate requests
@@ -77,7 +86,8 @@ export function makeAuthenticateMiddleware(prefix: string) {
}
// we shouldnt be here in the login route
if (req.originalUrl.startsWith('/login')) {
const loginPath = prefix ? `${prefix}/login` : '/login';
if (req.originalUrl.startsWith(loginPath)) {
return next();
}
@@ -93,7 +103,7 @@ export function makeAuthenticateMiddleware(prefix: string) {
// if the user gives is a token in the query params, we set the cookie to be used in further requests
if (req.query.token === hashedPassword) {
if (hashedPassword !== undefined) {
setSessionCookie(res, hashedPassword);
setSessionCookie(res, hashedPassword, prefix);
}
return next();
}
@@ -148,11 +158,11 @@ export function authenticateSocket(_ws: WebSocket, req: IncomingMessage, next: (
* Sets a cookie with the provided token
* We currently add a full 'rw' permission scope, this should be filtered when dealing with presets
*/
function setSessionCookie(res: Response, token: string) {
function setSessionCookie(res: Response, token: string, prefix: string) {
res.cookie('token', JSON.stringify({ token, scope: 'rw' }), {
httpOnly: false, // allow websocket to access cookie
secure: true,
path: '/', // allow cookie to be accessed from any path
path: prefix || '/', // allow cookie to be accessed from prefix path or root
sameSite: 'none', // allow cookies to be sent in cross-origin requests (e.g., iframes)
});
}