your session

main
alex 2024-01-27 21:04:16 +01:00
parent edfd6b08f3
commit c8ba965329
7 changed files with 153 additions and 4 deletions

19
package-lock.json generated
View File

@ -18,6 +18,7 @@
"dotenv": "^16.3.1",
"express": "^4.18.2",
"express-session": "^1.17.3",
"express-useragent": "^1.0.15",
"fs-extra": "^11.2.0",
"mariadb": "^3.2.3",
"passport": "^0.7.0",
@ -36,6 +37,7 @@
"@types/cors": "^2.8.17",
"@types/express": "^4.17.21",
"@types/express-session": "^1.17.10",
"@types/express-useragent": "^1.0.5",
"@types/fs-extra": "^11.0.4",
"@types/passport-google-oauth20": "^2.0.14",
"@types/swagger-jsdoc": "^6.0.4",
@ -206,6 +208,15 @@
"@types/express": "*"
}
},
"node_modules/@types/express-useragent": {
"version": "1.0.5",
"resolved": "https://registry.npmjs.org/@types/express-useragent/-/express-useragent-1.0.5.tgz",
"integrity": "sha512-G1zPW6jDj7oGJvMvB8UCIAWznCOafcksYzOg8LINfYmvrXlSGJ6nsJ7O7GlmYMjrgGYvobYaI8IhRCkAKslPJA==",
"dev": true,
"dependencies": {
"@types/express": "*"
}
},
"node_modules/@types/fs-extra": {
"version": "11.0.4",
"resolved": "https://registry.npmjs.org/@types/fs-extra/-/fs-extra-11.0.4.tgz",
@ -1068,6 +1079,14 @@
"node": ">= 0.6"
}
},
"node_modules/express-useragent": {
"version": "1.0.15",
"resolved": "https://registry.npmjs.org/express-useragent/-/express-useragent-1.0.15.tgz",
"integrity": "sha512-eq5xMiYCYwFPoekffMjvEIk+NWdlQY9Y38OsTyl13IvA728vKT+q/CSERYWzcw93HGBJcIqMIsZC5CZGARPVdg==",
"engines": {
"node": ">=4.5"
}
},
"node_modules/express/node_modules/body-parser": {
"version": "1.20.1",
"resolved": "https://registry.npmjs.org/body-parser/-/body-parser-1.20.1.tgz",

View File

@ -20,6 +20,7 @@
"dotenv": "^16.3.1",
"express": "^4.18.2",
"express-session": "^1.17.3",
"express-useragent": "^1.0.15",
"fs-extra": "^11.2.0",
"mariadb": "^3.2.3",
"passport": "^0.7.0",
@ -38,6 +39,7 @@
"@types/cors": "^2.8.17",
"@types/express": "^4.17.21",
"@types/express-session": "^1.17.10",
"@types/express-useragent": "^1.0.5",
"@types/fs-extra": "^11.0.4",
"@types/passport-google-oauth20": "^2.0.14",
"@types/swagger-jsdoc": "^6.0.4",

View File

@ -6,6 +6,7 @@ import cors from "cors";
import GoogleStrategy from "passport-google-oauth20";
import cookieParser from "cookie-parser";
import session from "express-session";
import useragent from "express-useragent";
import calendarRoutes from "./src/routes/calendarRoutes";
import storeRoutes from "./src/routes/storeRoutes";
@ -51,8 +52,6 @@ const options = {
apis: ["./src/routes/*.ts"],
};
app.use(cookieParser());
app.use(
session({
secret: "keyboard cat",
@ -108,6 +107,8 @@ passport.deserializeUser(function (user: User, cb) {
// TODO: setup cors
app.use(cors());
app.use(cookieParser());
app.use(useragent.express());
app.use(bodyParser.json());
app.use("/api/v1/calendar", calendarRoutes);

View File

@ -114,7 +114,7 @@ export async function SignUp(req: Request, res: Response) {
);
// create session
saveSession(res, user.user_id, user.username);
saveSession(req, res, user.user_id, user.username);
})
.catch((err) => {
logger.error(err);
@ -181,7 +181,7 @@ export async function Login(req: Request, res: Response) {
}
// create session
saveSession(res, user.user_id, user.username);
saveSession(req, res, user.user_id, user.username);
} catch (error) {
logger.error(error);
res.status(500).send({ err: "invalid request" });
@ -272,6 +272,19 @@ export async function GetUser(req: Request, res: Response) {
);
}
// update user session last_used
Session.update(
{
last_used: new Date(),
},
{
where: {
session_id: session.session_id,
},
}
);
res.status(200).send(respData);
} catch (error) {
logger.error(error);
@ -470,3 +483,73 @@ export async function UpdateUserProfilePassword(req: Request, res: Response) {
res.status(500).send({ err: "invalid request" });
}
}
export async function GetUserProfileSessions(req: Request, res: Response) {
try {
const session = await getUserSession(req);
if (!session) {
return res.status(401).send({ err: "unauthorized" });
}
const sessions = await Session.findAll({
where: {
user_id: session.user_id,
},
attributes: ["session_id", "id", "browser", "os", "last_used"],
});
// set last_used to now if session is the user's current session
let currentSession = sessions.find(
(sess) => sess.session_id === session.session_id
)?.id;
// remove session_id from sessions for security reasons
const sessionsList = sessions.map((sess) => {
return {
id: sess.id,
browser: sess.browser,
os: sess.os,
last_used: sess.last_used,
};
});
res.status(200).json({
sessions: sessionsList,
currentSession: currentSession,
});
} catch (error) {
logger.error(error);
res.status(500).send({ err: "invalid request" });
}
}
export async function DeleteUserProfileSession(req: Request, res: Response) {
try {
const { id } = req.params;
if (!id) {
return res.status(400).send({ err: "invalid request" });
}
const session = await getUserSession(req);
if (!session) {
return res.status(401).send({ err: "unauthorized" });
}
await Session.destroy({
where: {
id: id,
user_id: session.user_id,
},
});
res.status(200).send({ msg: "session deleted" });
} catch (error) {
logger.error(error);
res.status(500).send({ err: "invalid request" });
}
}

View File

@ -4,12 +4,21 @@ import sequelize from "../database/database";
interface SessionAttributes {
session_id: string;
user_id: string;
// needed in the ui to delete the session
id: string;
browser: string;
os: string;
last_used: Date;
expires: Date;
}
class Session extends Model<SessionAttributes> implements SessionAttributes {
declare session_id: string;
declare user_id: string;
declare id: string;
declare browser: string;
declare os: string;
declare last_used: Date;
declare expires: Date;
}
@ -25,6 +34,22 @@ Session.init(
type: DataTypes.STRING,
allowNull: false,
},
id: {
type: DataTypes.STRING,
allowNull: false,
},
browser: {
type: DataTypes.STRING,
allowNull: false,
},
os: {
type: DataTypes.STRING,
allowNull: false,
},
last_used: {
type: DataTypes.DATE,
allowNull: false,
},
expires: {
type: DataTypes.DATE,
allowNull: false,

View File

@ -24,5 +24,15 @@ router.post(
sessionProtection,
userController.UpdateUserProfilePassword
);
router.get(
"/profile/sessions",
sessionProtection,
userController.GetUserProfileSessions
);
router.delete(
"/profile/sessions/:id",
sessionProtection,
userController.DeleteUserProfileSession
);
export default router;

View File

@ -41,7 +41,12 @@ export function newUserSession() {
return crypto.randomBytes(USER_SESSION_LENGTH).toString("hex");
}
export function newSessionId() {
return uuidv4();
}
export async function saveSession(
req: Request,
res: Response,
userId: string,
username: string
@ -52,6 +57,10 @@ export async function saveSession(
await Session.create({
user_id: userId,
session_id: userSession,
id: newSessionId(),
browser: req.useragent?.browser as string,
os: req.useragent?.os as string,
last_used: new Date(),
expires: new Date(Date.now() + DEFAULT_SESSION_EXPIRY),
});