updated authentication scheme
added middleware
This commit is contained in:
parent
7b7bd3f4f0
commit
23314af0d0
10 changed files with 182 additions and 106 deletions
8
app.js
8
app.js
|
|
@ -5,7 +5,9 @@ const mongoose = require("mongoose");
|
||||||
const cookieParser = require("cookie-parser");
|
const cookieParser = require("cookie-parser");
|
||||||
require("dotenv").config();
|
require("dotenv").config();
|
||||||
|
|
||||||
const router = require("./routes.js");
|
const indexRouter = require("./routes/index.js");
|
||||||
|
const postRouter = require("./routes/post.js");
|
||||||
|
const userRouter = require("./routes/user.js");
|
||||||
|
|
||||||
const app = express();
|
const app = express();
|
||||||
|
|
||||||
|
|
@ -38,6 +40,8 @@ app.use(express.static(path.join(__dirname, "public")));
|
||||||
app.use(cors());
|
app.use(cors());
|
||||||
|
|
||||||
// routing
|
// routing
|
||||||
app.use("/", router);
|
app.use("/", indexRouter);
|
||||||
|
app.use("/post", postRouter);
|
||||||
|
app.use("/user", userRouter);
|
||||||
|
|
||||||
module.exports = app;
|
module.exports = app;
|
||||||
|
|
|
||||||
|
|
@ -1,13 +1,39 @@
|
||||||
const { default: mongoose } = require("mongoose");
|
const { default: mongoose } = require("mongoose");
|
||||||
const asyncHandler = require("express-async-handler");
|
const asyncHandler = require("express-async-handler");
|
||||||
const { body, validationResult } = require("express-validator");
|
const { body, validationResult } = require("express-validator");
|
||||||
|
const jwt = require("jsonwebtoken");
|
||||||
|
|
||||||
const Post = require("../models/post.js");
|
const Post = require("../models/post.js");
|
||||||
const Comment = require("../models/comment.js");
|
const Comment = require("../models/comment.js");
|
||||||
|
const hasToken = require("../middleware/hasToken.js");
|
||||||
|
const sameAuthor = require("../middleware/sameAuthor.js");
|
||||||
|
|
||||||
// returns json object with ALL posts and comments
|
|
||||||
exports.index = asyncHandler(async (req, res, next) => {
|
exports.index = asyncHandler(async (req, res, next) => {
|
||||||
const dbPosts = await Post.find().lean().exec();
|
// get all posts
|
||||||
|
const dbPosts = await Post.find({ published: true }).lean().exec();
|
||||||
|
// check for authorization
|
||||||
|
const token = req.cookies.JWT_TOKEN;
|
||||||
|
blocc: if (token) {
|
||||||
|
try {
|
||||||
|
jwt.verify(token, process.env.SECRET_KEY);
|
||||||
|
} catch {
|
||||||
|
break blocc;
|
||||||
|
}
|
||||||
|
// run through unpublished posts
|
||||||
|
const unpublished = await Post.find({ published: false }).lean().exec();
|
||||||
|
for (let z = 0; z < unpublished.length; z++) {
|
||||||
|
const post = unpublished[i];
|
||||||
|
const author = post.author;
|
||||||
|
let opts = {
|
||||||
|
expiresIn: "1d",
|
||||||
|
};
|
||||||
|
const authorToken = jwt.sign({ author }, process.env.SECRET_KEY, opts);
|
||||||
|
// if any are by the current user, append to dbPosts
|
||||||
|
if (token == authorToken) {
|
||||||
|
dbPosts.push(post);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
const posts = [];
|
const posts = [];
|
||||||
for (let i = 0; i < dbPosts.length; i++) {
|
for (let i = 0; i < dbPosts.length; i++) {
|
||||||
const comments = await Comment.find({ post: dbPosts[i]._id });
|
const comments = await Comment.find({ post: dbPosts[i]._id });
|
||||||
|
|
@ -16,7 +42,6 @@ exports.index = asyncHandler(async (req, res, next) => {
|
||||||
date: dbPosts[i].date,
|
date: dbPosts[i].date,
|
||||||
text: dbPosts[i].text,
|
text: dbPosts[i].text,
|
||||||
author: dbPosts[i].author,
|
author: dbPosts[i].author,
|
||||||
published: dbPosts[i].published,
|
|
||||||
_id: dbPosts[i]._id,
|
_id: dbPosts[i]._id,
|
||||||
comments: comments,
|
comments: comments,
|
||||||
};
|
};
|
||||||
|
|
@ -40,14 +65,6 @@ exports.post = [
|
||||||
.escape(),
|
.escape(),
|
||||||
|
|
||||||
asyncHandler(async (req, res, next) => {
|
asyncHandler(async (req, res, next) => {
|
||||||
// begin by authorizing token
|
|
||||||
const token = req.cookies.JWT_TOKEN;
|
|
||||||
if (!token) {
|
|
||||||
return res.status(403).json({
|
|
||||||
message: "Not authorized!",
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
// then return any validation errors
|
// then return any validation errors
|
||||||
const errors = validationResult(req);
|
const errors = validationResult(req);
|
||||||
if (!errors.isEmpty()) {
|
if (!errors.isEmpty()) {
|
||||||
|
|
@ -77,7 +94,14 @@ exports.post = [
|
||||||
|
|
||||||
// returns post in json format - R
|
// returns post in json format - R
|
||||||
exports.get = asyncHandler(async (req, res, next) => {
|
exports.get = asyncHandler(async (req, res, next) => {
|
||||||
const post = await Post.findOne({ _id: req.params.postID }).lean().exec();
|
const post = await Post.findById(req.params.postID).lean().exec();
|
||||||
|
// if post is not published, not publicly visible
|
||||||
|
if (!post.published) {
|
||||||
|
hasToken();
|
||||||
|
sameAuthor();
|
||||||
|
return res.status(200).json({ post });
|
||||||
|
}
|
||||||
|
// otherwise return post if published
|
||||||
return res.status(200).json({ post });
|
return res.status(200).json({ post });
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|
@ -97,15 +121,7 @@ exports.put = [
|
||||||
|
|
||||||
// Process request after sanitization and validation
|
// Process request after sanitization and validation
|
||||||
asyncHandler(async (req, res, next) => {
|
asyncHandler(async (req, res, next) => {
|
||||||
// begin by authorizing token
|
// return any validation errors
|
||||||
const token = req.cookies.JWT_TOKEN;
|
|
||||||
if (!token) {
|
|
||||||
return res.status(403).json({
|
|
||||||
message: "Not authorized!",
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
// then return any validation errors
|
|
||||||
const errors = validationResult(req);
|
const errors = validationResult(req);
|
||||||
if (!errors.isEmpty()) {
|
if (!errors.isEmpty()) {
|
||||||
return res.status(400).json({
|
return res.status(400).json({
|
||||||
|
|
@ -115,13 +131,13 @@ exports.put = [
|
||||||
}
|
}
|
||||||
|
|
||||||
// else data is valid, update post object
|
// else data is valid, update post object
|
||||||
const dbPost = await Post.findOne({ _id: req.params.postID }).lean().exec();
|
const dbPost = await Post.findById(req.params.postID).lean().exec();
|
||||||
const post = new Post({
|
const post = new Post({
|
||||||
title: req.body.title,
|
title: req.body.title,
|
||||||
date: new Date(),
|
date: new Date(),
|
||||||
text: req.body.text,
|
text: req.body.text,
|
||||||
author: dbPost.author,
|
author: dbPost.author,
|
||||||
published: dbPost.published,
|
published: req.body.published,
|
||||||
_id: dbPost._id,
|
_id: dbPost._id,
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|
@ -136,12 +152,6 @@ exports.put = [
|
||||||
|
|
||||||
// deletes a post - D
|
// deletes a post - D
|
||||||
exports.delete = asyncHandler(async (req, res, next) => {
|
exports.delete = asyncHandler(async (req, res, next) => {
|
||||||
const token = req.cookies.JWT_TOKEN;
|
await Post.findByIdAndDelete(req.params.postID).exec();
|
||||||
if (!token) {
|
|
||||||
return res.status(403).json({
|
|
||||||
message: "Not authorized!",
|
|
||||||
});
|
|
||||||
}
|
|
||||||
await Post.findByIdAndDelete({ _id: req.params.postID }).exec();
|
|
||||||
return res.status(200).json({ message: "Post deleted!" });
|
return res.status(200).json({ message: "Post deleted!" });
|
||||||
});
|
});
|
||||||
|
|
|
||||||
|
|
@ -1,7 +1,6 @@
|
||||||
const asyncHandler = require("express-async-handler");
|
const asyncHandler = require("express-async-handler");
|
||||||
const bcrypt = require("bcryptjs");
|
const bcrypt = require("bcryptjs");
|
||||||
const User = require("../models/user.js");
|
const User = require("../models/user.js");
|
||||||
const jwt = require("jsonwebtoken");
|
|
||||||
const { body, validationResult } = require("express-validator");
|
const { body, validationResult } = require("express-validator");
|
||||||
const { default: mongoose } = require("mongoose");
|
const { default: mongoose } = require("mongoose");
|
||||||
|
|
||||||
|
|
@ -60,7 +59,7 @@ exports.get = asyncHandler(async (req, res, next) => {
|
||||||
.lean()
|
.lean()
|
||||||
.exec(); // gets user based on username
|
.exec(); // gets user based on username
|
||||||
return res.status(200).json({
|
return res.status(200).json({
|
||||||
user: user,
|
user,
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|
@ -81,11 +80,6 @@ exports.post = [
|
||||||
asyncHandler(async (req, res, next) => {
|
asyncHandler(async (req, res, next) => {
|
||||||
// begin by authorizing token
|
// begin by authorizing token
|
||||||
const token = req.cookies.JWT_TOKEN;
|
const token = req.cookies.JWT_TOKEN;
|
||||||
if (!token) {
|
|
||||||
return res.status(403).json({
|
|
||||||
message: "Not authorized!",
|
|
||||||
});
|
|
||||||
}
|
|
||||||
// if token is not for this user - compares by creating another token
|
// if token is not for this user - compares by creating another token
|
||||||
let opts = {
|
let opts = {
|
||||||
expiresIn: "1d",
|
expiresIn: "1d",
|
||||||
|
|
@ -137,29 +131,6 @@ exports.post = [
|
||||||
|
|
||||||
// D
|
// D
|
||||||
exports.delete = asyncHandler(async (req, res, next) => {
|
exports.delete = asyncHandler(async (req, res, next) => {
|
||||||
// begin by authorizing token
|
|
||||||
const token = req.cookies.JWT_TOKEN;
|
|
||||||
if (!token) {
|
|
||||||
return res.status(403).json({
|
|
||||||
message: "Not authorized!",
|
|
||||||
});
|
|
||||||
}
|
|
||||||
// if token is not for this user - compares by creating another token
|
|
||||||
let opts = {
|
|
||||||
expiresIn: "1d",
|
|
||||||
};
|
|
||||||
const originalUsername = req.params.username;
|
|
||||||
const userToken = jwt.sign(
|
|
||||||
{ originalUsername },
|
|
||||||
process.env.SECRET_KEY,
|
|
||||||
opts
|
|
||||||
);
|
|
||||||
if (token != userToken) {
|
|
||||||
return res.status(403).json({
|
|
||||||
message: "Not authorized!",
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
// if everything is correct, delete user
|
// if everything is correct, delete user
|
||||||
await User.findOneAndDelete({ username: originalUsername }).exec();
|
await User.findOneAndDelete({ username: originalUsername }).exec();
|
||||||
return res.status(200).json({ message: "Post deleted!" });
|
return res.status(200).json({ message: "Post deleted!" });
|
||||||
|
|
|
||||||
22
middleware/hasToken.js
Normal file
22
middleware/hasToken.js
Normal file
|
|
@ -0,0 +1,22 @@
|
||||||
|
const hasToken = (req, res, next) => {
|
||||||
|
// get token
|
||||||
|
const token = req.cookies.JWT_TOKEN;
|
||||||
|
if (!token) {
|
||||||
|
// if none, error
|
||||||
|
return res.status(403).json({
|
||||||
|
message: "Not authorized!",
|
||||||
|
});
|
||||||
|
}
|
||||||
|
try {
|
||||||
|
jwt.verify(token, process.env.SECRET_KEY);
|
||||||
|
// move forward
|
||||||
|
return next();
|
||||||
|
} catch {
|
||||||
|
// if incorrect, error
|
||||||
|
return res.status(403).json({
|
||||||
|
message: "Not authorized!",
|
||||||
|
});
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
module.exports = hasToken;
|
||||||
26
middleware/sameAuthor.js
Normal file
26
middleware/sameAuthor.js
Normal file
|
|
@ -0,0 +1,26 @@
|
||||||
|
const jwt = require("jsonwebtoken");
|
||||||
|
const asyncHandler = require("express-async-handler");
|
||||||
|
const { default: mongoose } = require("mongoose");
|
||||||
|
const Post = require("../models/post.js");
|
||||||
|
|
||||||
|
const sameAuthor = asyncHandler(async (req, res, next) => {
|
||||||
|
// get token
|
||||||
|
const token = req.cookies.JWT_TOKEN;
|
||||||
|
// make token with identical user information
|
||||||
|
let opts = {
|
||||||
|
expiresIn: "1d",
|
||||||
|
};
|
||||||
|
const post = await Post.findById(req.params.postID).lean().exec();
|
||||||
|
const author = post.author;
|
||||||
|
const userToken = jwt.sign({ author }, process.env.SECRET_KEY, opts);
|
||||||
|
// compare the two, if the token does not match then the action is unauthorized
|
||||||
|
if (token != userToken) {
|
||||||
|
return res.status(403).json({
|
||||||
|
message: "Not authorized!",
|
||||||
|
});
|
||||||
|
}
|
||||||
|
// otherwise all good
|
||||||
|
return next();
|
||||||
|
});
|
||||||
|
|
||||||
|
module.exports = sameAuthor;
|
||||||
22
middleware/sameUser.js
Normal file
22
middleware/sameUser.js
Normal file
|
|
@ -0,0 +1,22 @@
|
||||||
|
const jwt = require("jsonwebtoken");
|
||||||
|
|
||||||
|
const sameUser = (req, res, next) => {
|
||||||
|
// get token
|
||||||
|
const token = req.cookies.JWT_TOKEN;
|
||||||
|
// make token with identical user information
|
||||||
|
let opts = {
|
||||||
|
expiresIn: "1d",
|
||||||
|
};
|
||||||
|
const username = req.params.username;
|
||||||
|
const userToken = jwt.sign({ username }, process.env.SECRET_KEY, opts);
|
||||||
|
// compare the two, if the token does not match then the action is unauthorized
|
||||||
|
if (token != userToken) {
|
||||||
|
return res.status(403).json({
|
||||||
|
message: "Not authorized!",
|
||||||
|
});
|
||||||
|
}
|
||||||
|
// otherwise all good
|
||||||
|
return next();
|
||||||
|
};
|
||||||
|
|
||||||
|
module.exports = sameUser;
|
||||||
44
routes.js
44
routes.js
|
|
@ -1,44 +0,0 @@
|
||||||
const express = require("express");
|
|
||||||
const router = express.Router();
|
|
||||||
const login_controller = require("./controllers/login.js");
|
|
||||||
const post_controller = require("./controllers/post.js");
|
|
||||||
const comment_controller = require("./controllers/comment.js");
|
|
||||||
const user_controller = require("./controllers/user.js");
|
|
||||||
|
|
||||||
// list all posts and append comments to each post based on id, return as json
|
|
||||||
router.get("/", post_controller.index);
|
|
||||||
|
|
||||||
// login page
|
|
||||||
router.post("/login", login_controller.post);
|
|
||||||
|
|
||||||
// user get
|
|
||||||
router.get("/:username", user_controller.get);
|
|
||||||
|
|
||||||
// user post
|
|
||||||
router.post("/new_user", user_controller.post);
|
|
||||||
|
|
||||||
// user put
|
|
||||||
router.put("/:username", user_controller.put);
|
|
||||||
|
|
||||||
// user delete
|
|
||||||
router.delete("/:username", user_controller.delete);
|
|
||||||
|
|
||||||
// post post
|
|
||||||
router.post("/new_post", post_controller.post);
|
|
||||||
|
|
||||||
// post get
|
|
||||||
router.get("/:postID", post_controller.get);
|
|
||||||
|
|
||||||
// post put
|
|
||||||
router.put("/:postID", post_controller.put);
|
|
||||||
|
|
||||||
// post delete
|
|
||||||
router.delete("/:postID", post_controller.delete);
|
|
||||||
|
|
||||||
// comment post
|
|
||||||
router.post("/:postID/new_comment", comment_controller.post);
|
|
||||||
|
|
||||||
// comment get
|
|
||||||
router.get("/:postID/:commentID", comment_controller.get);
|
|
||||||
|
|
||||||
module.exports = router;
|
|
||||||
18
routes/index.js
Normal file
18
routes/index.js
Normal file
|
|
@ -0,0 +1,18 @@
|
||||||
|
const express = require("express");
|
||||||
|
const router = express.Router();
|
||||||
|
const login_controller = require("../controllers/login.js");
|
||||||
|
const post_controller = require("../controllers/post.js");
|
||||||
|
const hasToken = require("../middleware/hasToken.js");
|
||||||
|
|
||||||
|
// list all posts and append comments to each post based on id, return as json
|
||||||
|
router.get("/", post_controller.index);
|
||||||
|
|
||||||
|
// login page
|
||||||
|
router.post("/login", login_controller.post);
|
||||||
|
|
||||||
|
// authentication checking page - used by frontend
|
||||||
|
router.get("/ping", hasToken, (req, res) => {
|
||||||
|
return res.status(200).json({ message: "Authenticated!" });
|
||||||
|
});
|
||||||
|
|
||||||
|
module.exports = router;
|
||||||
26
routes/post.js
Normal file
26
routes/post.js
Normal file
|
|
@ -0,0 +1,26 @@
|
||||||
|
const express = require("express");
|
||||||
|
const router = express.Router();
|
||||||
|
const post_controller = require("../controllers/post.js");
|
||||||
|
const comment_controller = require("../controllers/comment.js");
|
||||||
|
const hasToken = require("../middleware/hasToken.js");
|
||||||
|
const sameAuthor = require("../middleware/sameAuthor.js");
|
||||||
|
|
||||||
|
// post post
|
||||||
|
router.post("/new_post", hasToken, post_controller.post);
|
||||||
|
|
||||||
|
// post get
|
||||||
|
router.get("/:postID", post_controller.get);
|
||||||
|
|
||||||
|
// post put
|
||||||
|
router.put("/:postID", hasToken, sameAuthor, post_controller.put);
|
||||||
|
|
||||||
|
// post delete
|
||||||
|
router.delete("/:postID", hasToken, sameAuthor, post_controller.delete);
|
||||||
|
|
||||||
|
// comment post
|
||||||
|
router.post("/:postID/new_comment", comment_controller.post);
|
||||||
|
|
||||||
|
// comment get
|
||||||
|
router.get("/:postID/:commentID", comment_controller.get);
|
||||||
|
|
||||||
|
module.exports = router;
|
||||||
21
routes/user.js
Normal file
21
routes/user.js
Normal file
|
|
@ -0,0 +1,21 @@
|
||||||
|
const express = require("express");
|
||||||
|
const router = express.Router();
|
||||||
|
const user_controller = require("../controllers/user.js");
|
||||||
|
const hasToken = require("../middleware/hasToken.js");
|
||||||
|
const sameUser = require("../middleware/sameUser.js");
|
||||||
|
|
||||||
|
// hardcoded URLs always go before matchers
|
||||||
|
|
||||||
|
// user post
|
||||||
|
router.post("/new_user", user_controller.post);
|
||||||
|
|
||||||
|
// user get
|
||||||
|
router.get("/:username", hasToken, user_controller.get);
|
||||||
|
|
||||||
|
// user put
|
||||||
|
router.put("/:username", hasToken, sameUser, user_controller.put);
|
||||||
|
|
||||||
|
// user delete
|
||||||
|
router.delete("/:username", hasToken, sameUser, user_controller.delete);
|
||||||
|
|
||||||
|
module.exports = router;
|
||||||
Loading…
Add table
Reference in a new issue