diff --git a/server/api/auth.js b/server/api/auth.js index c33308e..1a5a3b7 100644 --- a/server/api/auth.js +++ b/server/api/auth.js @@ -3,14 +3,59 @@ const prisma = new Prisma.PrismaClient(); import { log } from "../logs.js"; +async function isLoggedIn(req) { + // If AUTH_PASSWORD env variable is not present don't require any login + if (!process.env.AUTH_PASSWORD) { + return true; + } + // If no session cookie is set and no token query + // parameter is provided the user can't be logged in + const token = req.query.token || req.cookies.session; + if (!token) { + return false; + } + // If there is a session cookie check it + const session = await prisma.session.findUnique({ + where: { + token, + }, + }); + // If no session is found (the session probably + // exired) the user is not logged in + if (!session) { + return false; + } + // If no checks failed the user is logged in + return session; +} + +async function renewSession(session) { + await prisma.session.update({ + where: { + token: session.token, + }, + data: { + // 14 Days from now on + validUntil: new Date(Date.now() + 1000 * 60 * 60 * 24 * 14), + }, + }); +} + async function login(req, res) { + // Check if the user is already logged in + let session = await isLoggedIn(req); + if (session) { + renewSession(session); + res.redirect("/"); + return; + } // Check password if (!req.body.password || req.body.password != process.env.AUTH_PASSWORD) { res.redirect("/login"); return; } // Create a new auth session - const session = await prisma.session.create({ + session = await prisma.session.create({ data: { // Expires after 14 days of inactivity validUntil: new Date(Date.now() + 1000 * 60 * 60 * 24 * 14), @@ -25,44 +70,66 @@ async function login(req, res) { res.redirect("/"); } -async function checkLogin(req, res, next) { - // If AUTH_PASSWORD env variable is not present don't require any login - if (!process.env.AUTH_PASSWORD) { - next(); - return; - } - // If no session cookie is set send 401 Unauthorized - // so the app redirects to the login page - if (!req.cookies.session) { - res.sendStatus(401); - return; - } - // Check the provided session cookie - const session = await prisma.session.findUnique({ - where: { - token: req.cookies.session, - }, - }); - // If no session is found also send 401 +async function logout(req, res) { + const session = await isLoggedIn(req); if (!session) { res.sendStatus(401); return; } - // Renew session expiration date - await prisma.session.update({ + await prisma.session.deleteMany({ where: { token: session.token, }, + }); + log("API / Auth", `Removed session: ${session.token}`); + res.redirect("/"); +} + +async function checkLogin(req, res, next) { + // Allow requests to `/api/token` + if (req.path == "/token") { + next(); + return; + } + const session = await isLoggedIn(req); + if (!session) { + // send 401 Unauthorized so the + // app redirects to the login page + res.sendStatus(401); + return; + } + next(); +} + +async function token(req, res) { + if (!req.query.password || req.query.password != process.env.AUTH_PASSWORD) { + res.status(401).send({ + success: false, + error: "wrong_auth", + message: "Wrong password", + }); + return; + } + // Create a new auth session + const session = await prisma.session.create({ data: { - validUntil: new Date(Date.now() + 1000 * 60 * 60 * 24 * 14), + // API token expires after 1 hour + validUntil: new Date(Date.now() + 1000 * 60 * 60), }, }); - next(); + log("API / Auth", `New token: ${session.token}`); + + res.send({ + success: true, + token: session.token, + }); } export default { login, + logout, checkLogin, + token, }; // Clean up expired sessions every hour diff --git a/server/index.js b/server/index.js index 1146741..4ea1817 100644 --- a/server/index.js +++ b/server/index.js @@ -34,7 +34,8 @@ new Parser( ); // Create new Auth class to store sessions -app.post("/login", auth.login); +app.post("/auth/login", auth.login); +app.get("/auth/logout", auth.logout); // Check login for every API request app.use("/api", auth.checkLogin); // Provide check endpoint so the frontend @@ -48,6 +49,7 @@ app.get("/api/timetable", getTimetable); app.get("/api/substitutions", getSubstitutions); app.get("/api/history", getHistory); app.get("/api/classes", getClasses); +app.post("/api/token", auth.token); // Respond with 400 for non-existent endpoints app.get("/api/*", (_req, res) => { res.sendStatus(400); diff --git a/src/views/LoginView.vue b/src/views/LoginView.vue index f11234b..97c5d23 100644 --- a/src/views/LoginView.vue +++ b/src/views/LoginView.vue @@ -1,7 +1,7 @@