mirror of
https://github.com/cpvalente/ontime.git
synced 2026-09-04 22:09:10 +00:00
refactor: set token from query params
This commit is contained in:
committed by
Carlos Valente
parent
7fe5223af9
commit
0450eb9169
@@ -84,9 +84,9 @@ app.use(cookieParser());
|
|||||||
const { authenticate, authenticateAndRedirect } = makeAuthenticateMiddleware(prefix);
|
const { authenticate, authenticateAndRedirect } = makeAuthenticateMiddleware(prefix);
|
||||||
|
|
||||||
// Implement route endpoints
|
// Implement route endpoints
|
||||||
|
app.use(`${prefix}/login`, loginRouter); // router for login flow
|
||||||
app.use(`${prefix}/data`, authenticate, appRouter); // router for application data
|
app.use(`${prefix}/data`, authenticate, appRouter); // router for application data
|
||||||
app.use(`${prefix}/api`, authenticate, integrationRouter); // router for integrations
|
app.use(`${prefix}/api`, authenticate, integrationRouter); // router for integrations
|
||||||
app.use(`${prefix}/login`, loginRouter); // router for login flow
|
|
||||||
|
|
||||||
// serve static external files
|
// serve static external files
|
||||||
app.use(`${prefix}/external`, express.static(publicDir.externalDir));
|
app.use(`${prefix}/external`, express.static(publicDir.externalDir));
|
||||||
|
|||||||
@@ -15,6 +15,8 @@ export const environment = isTest ? 'test' : env;
|
|||||||
export const isDocker = env === 'docker';
|
export const isDocker = env === 'docker';
|
||||||
export const isProduction = isDocker || (env === 'production' && !isTest);
|
export const isProduction = isDocker || (env === 'production' && !isTest);
|
||||||
export const isOntimeCloud = Boolean(process.env.IS_CLOUD);
|
export const isOntimeCloud = Boolean(process.env.IS_CLOUD);
|
||||||
|
export const password = process.env.SESSION_PASSWORD;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Updates the router prefix in the index.html file
|
* Updates the router prefix in the index.html file
|
||||||
* This is only needed in the cloud environment where the client is not at the root segment
|
* This is only needed in the cloud environment where the client is not at the root segment
|
||||||
|
|||||||
@@ -8,8 +8,7 @@ import { parse as parseCookie } from 'cookie';
|
|||||||
import { hashPassword } from '../utils/hash.js';
|
import { hashPassword } from '../utils/hash.js';
|
||||||
import { srcFiles } from '../setup/index.js';
|
import { srcFiles } from '../setup/index.js';
|
||||||
import { logger } from '../classes/Logger.js';
|
import { logger } from '../classes/Logger.js';
|
||||||
// import { password } from '../externals.js';
|
import { password } from '../externals.js';
|
||||||
const password = 'test';
|
|
||||||
import { noopMiddleware } from './noop.js';
|
import { noopMiddleware } from './noop.js';
|
||||||
|
|
||||||
export const hasPassword = Boolean(password);
|
export const hasPassword = Boolean(password);
|
||||||
@@ -19,7 +18,13 @@ const hashedPassword = hasPassword ? hashPassword(password) : '';
|
|||||||
* List of public assets that can be accessed without authentication
|
* List of public assets that can be accessed without authentication
|
||||||
* should match the files in client/public
|
* should match the files in client/public
|
||||||
*/
|
*/
|
||||||
const publicAssets = ['/favicon.ico', '/manifest.json', '/ontime-logo.png', '/robots.txt', '/site.webmanifest'];
|
const publicAssets = new Set([
|
||||||
|
'/favicon.ico',
|
||||||
|
'/manifest.json',
|
||||||
|
'/ontime-logo.png',
|
||||||
|
'/robots.txt',
|
||||||
|
'/site.webmanifest',
|
||||||
|
]);
|
||||||
|
|
||||||
export const loginRouter = express.Router();
|
export const loginRouter = express.Router();
|
||||||
|
|
||||||
@@ -31,23 +36,8 @@ loginRouter.post('/', (req, res) => {
|
|||||||
res.clearCookie('token');
|
res.clearCookie('token');
|
||||||
const { password: reqPassword, redirect } = req.body;
|
const { password: reqPassword, redirect } = req.body;
|
||||||
|
|
||||||
if (!hasPassword) {
|
|
||||||
res.redirect(redirect || '/');
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!reqPassword) {
|
|
||||||
res.status(401).send('Unauthorized');
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (hashPassword(reqPassword) === hashedPassword) {
|
if (hashPassword(reqPassword) === hashedPassword) {
|
||||||
res.cookie('token', hashedPassword, {
|
setSessionCookie(res, hashedPassword);
|
||||||
httpOnly: false, // allow websocket to access cookie
|
|
||||||
secure: true,
|
|
||||||
path: '/', // allow cookie to be accessed from any path
|
|
||||||
sameSite: 'strict',
|
|
||||||
});
|
|
||||||
res.redirect(redirect || '/');
|
res.redirect(redirect || '/');
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
@@ -76,12 +66,12 @@ export function makeAuthenticateMiddleware(prefix: string) {
|
|||||||
|
|
||||||
function authenticateAndRedirect(req: Request, res: Response, next: NextFunction) {
|
function authenticateAndRedirect(req: Request, res: Response, next: NextFunction) {
|
||||||
// Allow access to specific public assets without authentication
|
// Allow access to specific public assets without authentication
|
||||||
if (publicAssets.includes(req.originalUrl)) {
|
if (publicAssets.has(req.originalUrl)) {
|
||||||
return next();
|
return next();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// we shouldnt be here in the login route
|
||||||
if (req.originalUrl.startsWith('/login')) {
|
if (req.originalUrl.startsWith('/login')) {
|
||||||
// cannot authenticate the login route
|
|
||||||
return next();
|
return next();
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -93,20 +83,11 @@ export function makeAuthenticateMiddleware(prefix: string) {
|
|||||||
// we use query params for generating authenticated URLs and for clients like the companion module
|
// we use query params for generating authenticated URLs and for clients like the companion module
|
||||||
// if the user gives is a token in the query params, we set the cookie to be used in further requests
|
// 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 (req.query.token === hashedPassword) {
|
||||||
res.cookie('token', hashedPassword, {
|
setSessionCookie(res, hashedPassword);
|
||||||
httpOnly: false, // allow websocket to access cookie
|
|
||||||
secure: true,
|
|
||||||
path: '/', // allow cookie to be accessed from any path
|
|
||||||
sameSite: 'strict',
|
|
||||||
});
|
|
||||||
return next();
|
return next();
|
||||||
}
|
}
|
||||||
|
|
||||||
const redirect = req.originalUrl.startsWith('/login')
|
res.redirect(`${prefix}/login?redirect=${req.originalUrl}`);
|
||||||
? `${prefix}/login`
|
|
||||||
: `${prefix}/login?redirect=${req.originalUrl}`;
|
|
||||||
|
|
||||||
res.redirect(redirect);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
return { authenticate, authenticateAndRedirect };
|
return { authenticate, authenticateAndRedirect };
|
||||||
@@ -139,3 +120,12 @@ export function authenticateSocket(_ws: WebSocket, req: IncomingMessage, next: (
|
|||||||
logger.warning(LogOrigin.Client, 'Unauthorized WebSocket connection attempt');
|
logger.warning(LogOrigin.Client, 'Unauthorized WebSocket connection attempt');
|
||||||
return next(new Error('Unauthorized'));
|
return next(new Error('Unauthorized'));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function setSessionCookie(res: Response, token: string) {
|
||||||
|
res.cookie('token', token, {
|
||||||
|
httpOnly: false, // allow websocket to access cookie
|
||||||
|
secure: true,
|
||||||
|
path: '/', // allow cookie to be accessed from any path
|
||||||
|
sameSite: 'strict',
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user