From e69fe69d908ae4f100d1c495636f598694c46a4b Mon Sep 17 00:00:00 2001 From: ak Date: Wed, 27 Sep 2023 16:50:58 -0700 Subject: [PATCH] revised to use httpOnly cookie to store jwt comments can no longer be updated or deleted - out of scope users can now be created, updated and deleted --- README.md | 6 +- app.js | 6 +- controllers/comment.js | 104 ++------------------- controllers/login.js | 23 ++--- controllers/post.js | 179 +++++++++++++++++++++---------------- controllers/user.js | 164 +++++++++++++++++++++++++++++++++ models/comment.js | 3 +- models/post.js | 2 +- package-lock.json | 100 +++++---------------- package.json | 7 +- routes/api.js => routes.js | 27 +++--- strategy/jwt.js | 20 ----- 12 files changed, 333 insertions(+), 308 deletions(-) create mode 100644 controllers/user.js rename routes/api.js => routes.js (57%) delete mode 100644 strategy/jwt.js diff --git a/README.md b/README.md index 81144fc..bd5bd69 100644 --- a/README.md +++ b/README.md @@ -1,5 +1,5 @@ # express-blog-api -RESTful blog API implemented in Express. Has POST/GET/PUT/DELETE methods for all posts and comments. -Post Creation, Updates and Deletion require jwt token returned when post completed at /api/login. -Hosted on Deta Space at https://expressblogapi-1-v2871156.deta.app/api/ +RESTful blog API implemented in Express. Has POST/GET/PUT/DELETE methods for posts and users. Comments cannot be updated or deleted. +Post Creation, Updates and Deletion require jwt token returned when login completed at /login. User Updates and Deletion require a matching jwt token. +Hosted on Deta Space at https://expressblogapi-1-v2871156.deta.app/ diff --git a/app.js b/app.js index 83188c4..94b5d27 100644 --- a/app.js +++ b/app.js @@ -2,9 +2,10 @@ const express = require("express"); const path = require("path"); const cors = require("cors"); const mongoose = require("mongoose"); +const cookieParser = require("cookie-parser"); require("dotenv").config(); -const apiRouter = require("./routes/api.js"); +const router = require("./routes.js"); const app = express(); @@ -28,6 +29,7 @@ app.set("views", path.join(__dirname, "views")); app.set("view engine", "ejs"); // parsing +app.use(cookieParser()); app.use(express.json()); app.use(express.urlencoded({ extended: false })); app.use(express.static(path.join(__dirname, "public"))); @@ -36,6 +38,6 @@ app.use(express.static(path.join(__dirname, "public"))); app.use(cors()); // routing -app.use("/api", apiRouter); +app.use("/", router); module.exports = app; diff --git a/controllers/comment.js b/controllers/comment.js index d71980e..385501d 100644 --- a/controllers/comment.js +++ b/controllers/comment.js @@ -15,12 +15,6 @@ exports.post = [ .trim() .escape(), - // Validate and sanitize password - body("password", "Please enter password for future comment modification!") - .isLength({ min: 1 }) - .trim() - .escape(), - // Process request after authentication, validation and sanitization asyncHandler(async (req, res, next) => { const errors = validationResult(req); @@ -40,7 +34,6 @@ exports.post = [ author: req.body.author, post: req.params.postID, _id: new mongoose.Types.ObjectId(), - password: req.body.password, }); // save to DB @@ -60,96 +53,9 @@ exports.get = asyncHandler(async (req, res, next) => { return res.status(200).json({ comment }); }); -// updates comment - U -exports.put = [ - // Validate and sanitize text - body("text", "Please enter comment!").isLength({ min: 1 }).trim().escape(), +// no U(pdate) +// comments cannot be edited as they can be made by anyone +// best to prevent impersonation - // Validate and sanitize author name - body("author", "Please enter comment author!") - .isLength({ min: 1 }) - .trim() - .escape(), - - // Validate and sanitize password - body("password", "Please enter password to modify comment!") - .isLength({ min: 1 }) - .trim() - .escape(), - - // Process request after sanitization and validation - asyncHandler(async (req, res, next) => { - const errors = validationResult(req); - - // if there are validation errors, render with errors - if (!errors.isEmpty()) { - return res.status(400).json({ - message: "Comment produced validation errors!", - errors: errors.array(), - }); - } - - const dbComment = await Comment.findOne({ _id: req.params.commentID }) - .lean() - .exec(); - - if (req.body.author === dbComment.author) { - if (req.body.password === dbComment.password) { - const comment = { - date: new Date(), - text: req.body.text, - author: req.body.author, - post: dbComment.post, - _id: dbComment._id, - password: dbComment.password, - }; - - await comment.save(); - return res.status(200).json({ - message: "Post updated!", - }); - } - return res.status(401).json({ - message: "Comments can only be updated with their original password!", - }); - } - return res.status(401).json({ - message: "Comments can only be updated by their original author!", - }); - }), -]; - -// deletes a comment - D -exports.delete = [ - // Validate and sanitize password - body("password", "Please enter comment deletion password!") - .isLength({ min: 1 }) - .trim() - .escape(), - - // Process request after authentication, validation and sanitization - asyncHandler(async (req, res, next) => { - const errors = validationResult(req); - - // if there are validation errors, render with errors - if (!errors.isEmpty()) { - return res.status(400).json({ - message: "Comment password produced validation errors!", - errors: errors.array(), - }); - } - - const dbComment = await Comment.findOne({ _id: req.params.commentID }) - .lean() - .exec(); - - if (req.body.password === dbComment.password) { - await Comment.findByIdAndDelete({ _id: req.params.commentID }).exec(); - return res.status(200).json({ message: "Comment deleted!" }); - } - - return res.status(401).json({ - message: "Incorrect password!", - }); - }), -]; +// no D(elete) +// again, would require authorization that is out of scope for this project diff --git a/controllers/login.js b/controllers/login.js index 2b35f13..7efbd72 100644 --- a/controllers/login.js +++ b/controllers/login.js @@ -6,19 +6,22 @@ const User = require("../models/user.js"); let opts = {}; exports.post = asyncHandler(async (req, res, next) => { - const admin = await User.findOne({}).lean().exec(); // only one user in DB - admin. pull it up, jamie! const { username, password } = req.body; // get fields from body - if (username === admin.username) { - console.log(); - const match = await bcrypt.compare(password, admin.password); // compare bcrypt hashed passwords + const user = await User.findOne({ username: username }).lean().exec(); // gets user based on username + if (user) { + const match = await bcrypt.compare(password, user.password); // compare bcrypt hashed passwords if (match) { - opts.expiresIn = 120; + opts.expiresIn = "1d"; const token = jwt.sign({ username }, process.env.SECRET_KEY, opts); // create token and return below - return res.status(200).json({ - message: "Authentication complete", - token, - }); + return res + .cookie("JWT_TOKEN", token, { + httpOnly: true, + }) + .status(200) + .json({ + message: "Authentication complete", // a winrar is you + }); } } - return res.status(401).json({ message: "Authentication failed" }); // get rekt nerd + return res.status(401).json({ message: "Authentication failed" }); // epic fail }); diff --git a/controllers/post.js b/controllers/post.js index c56ff3b..de9aa1c 100644 --- a/controllers/post.js +++ b/controllers/post.js @@ -5,10 +5,6 @@ const { body, validationResult } = require("express-validator"); const Post = require("../models/post.js"); const Comment = require("../models/comment.js"); -const passport = require("passport"); -const jwtStrategy = require("../strategy/jwt.js"); -passport.use(jwtStrategy); - // returns json object with ALL posts and comments exports.index = asyncHandler(async (req, res, next) => { const dbPosts = await Post.find().lean().exec(); @@ -30,50 +26,54 @@ exports.index = asyncHandler(async (req, res, next) => { }); // makes new post - C -(exports.post = passport.authenticate("jwt", { session: false })), - [ - // Validate and sanitize title - body("title", "Please enter blog post title!") - .isLength({ min: 1 }) - .trim() - .escape(), +exports.post = [ + // Validate and sanitize title + body("title", "Please enter blog post title!") + .isLength({ min: 1 }) + .trim() + .escape(), - // Validate and sanitize text - body("text", "Please enter blog post text!") - .isLength({ min: 1 }) - .trim() - .escape(), + // Validate and sanitize text + body("text", "Please enter blog post text!") + .isLength({ min: 1 }) + .trim() + .escape(), - // Process request after authentication, validation and sanitization - asyncHandler(async (req, res, next) => { - const errors = validationResult(req); - - // if there are validation errors, render with errors - if (!errors.isEmpty()) { - res.render("/admin/create"), - { - errors: errors.array(), - }; - return; - } - - // else data is valid, make post object - const post = new Post({ - title: req.body.title, - date: new Date(), - text: req.body.text, - author: req.user.username, - _id: new mongoose.Types.ObjectId(), + 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!", }); + } - // save to DB - await post.save(); - - return res.status(200).json({ - message: "Post created!", + // then return any validation errors + const errors = validationResult(req); + if (!errors.isEmpty()) { + return res.status(400).json({ + message: "Comment produced validation errors!", + errors: errors.array(), }); - }), - ]; + } + + // else data is valid, make post object + const post = new Post({ + title: req.body.title, + date: new Date(), + text: req.body.text, + author: req.user.username, + _id: new mongoose.Types.ObjectId(), + }); + + // save to DB + await post.save(); + + return res.status(200).json({ + message: "Post created!", + }); + }), +]; // returns post in json format - R exports.get = asyncHandler(async (req, res, next) => { @@ -82,43 +82,66 @@ exports.get = asyncHandler(async (req, res, next) => { }); // updates post - U -(exports.put = passport.authenticate("jwt", { session: false })), - [ - // Validate and sanitize title - body("title", "Please enter blog post title!") - .isLength({ min: 1 }) - .trim() - .escape(), +exports.put = [ + // Validate and sanitize title + body("title", "Please enter blog post title!") + .isLength({ min: 1 }) + .trim() + .escape(), - // Validate and sanitize text - body("text", "Please enter blog post text!") - .isLength({ min: 1 }) - .trim() - .escape(), + // Validate and sanitize text + body("text", "Please enter blog post text!") + .isLength({ min: 1 }) + .trim() + .escape(), - // Process request after sanitization and validation - asyncHandler(async (req, res, next) => { - const dbPost = await Post.findOne({ _id: req.params.postID }) - .lean() - .exec(); - const post = { - title: req.body.title, - date: new Date(), - text: req.body.text, - author: dbPost.author, - published: dbPost.published, - _id: dbPost._id, - }; - await post.save(); - return res.status(200).json({ - message: "Post updated!", + // Process request after sanitization and validation + 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 + const errors = validationResult(req); + if (!errors.isEmpty()) { + return res.status(400).json({ + message: "Comment produced validation errors!", + errors: errors.array(), + }); + } + + // else data is valid, update post object + const dbPost = await Post.findOne({ _id: req.params.postID }).lean().exec(); + const post = new Post({ + title: req.body.title, + date: new Date(), + text: req.body.text, + author: dbPost.author, + published: dbPost.published, + _id: dbPost._id, + }); + + // save to DB + await Post.findByIdAndUpdate(dbPost._id, post, {}); + + return res.status(200).json({ + message: "Post updated!", + }); + }), +]; // deletes a post - D -(exports.delete = passport.authenticate("jwt", { session: false })), - asyncHandler(async (req, res, next) => { - await Post.findByIdAndDelete({ _id: req.params.postID }).exec(); - return res.status(200).json({ message: "Post deleted!" }); - }); +exports.delete = asyncHandler(async (req, res, next) => { + const token = req.cookies.JWT_TOKEN; + 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!" }); +}); diff --git a/controllers/user.js b/controllers/user.js new file mode 100644 index 0000000..b4d9806 --- /dev/null +++ b/controllers/user.js @@ -0,0 +1,164 @@ +const asyncHandler = require("express-async-handler"); +const bcrypt = require("bcryptjs"); +const User = require("../models/user.js"); +const jwt = require("jsonwebtoken"); + +// C +exports.put = [ + // Validate and sanitize username + body("username", "Please enter username!") + .isLength({ min: 1 }) + .trim() + .escape(), + + // Validate and sanitize password + body("password", "Please enter password!") + .isLength({ min: 1 }) + .trim() + .escape(), + + asyncHandler(async (req, res, next) => { + const errors = validationResult(req); + // if there are validation errors, return them + if (!errors.isEmpty()) { + return res.status(400).json({ + message: "Comment produced validation errors!", + errors: errors.array(), + }); + } + const { username, password } = req.body; // get fields from body + const dbUser = await User.findOne({ username: req.params.username }) + .lean() + .exec(); + const exists = await User.findOne({ username: username }).lean().exec(); + + if (exists) { + return res.status(409).json({ + message: "Username is taken!", + }); + } + // else + const user = new User({ + username: username, + password: await bcrypt.hash(password, 10), + _id: dbUser._id, + }); + + await User.findByIdAndUpdate(dbUser._id, user, {}); + + return res.status(200).json({ + message: "User updated!", + }); + }), +]; + +// R +exports.get = asyncHandler(async (req, res, next) => { + const user = await User.findOne({ username: req.params.username }) + .lean() + .exec(); // gets user based on username + return res.status(200).json({ + user: user, + }); +}); + +// U +exports.post = [ + // Validate and sanitize username + body("username", "Please enter username!") + .isLength({ min: 1 }) + .trim() + .escape(), + + // Validate and sanitize password + body("password", "Please enter password!") + .isLength({ min: 1 }) + .trim() + .escape(), + + 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!", + }); + } + + // then return any validation errors + const errors = validationResult(req); + // if there are validation errors, return them + if (!errors.isEmpty()) { + return res.status(400).json({ + message: "Comment produced validation errors!", + errors: errors.array(), + }); + } + + // check for duplicates + const { username, password } = req.body; // get fields from body + const exists = await User.findOne({ username: originalUsername }) + .lean() + .exec(); + if (exists) { + return res.status(409).json({ + message: "Username is taken!", + }); + } + + // otherwise update user + const user = new User({ + username: username, + password: await bcrypt.hash(password, 10), + }); + await user.save(); // make and save user + return res.status(200).json({ + message: "User created!", + }); + }), +]; + +// D +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 + await User.findOneAndDelete({ username: originalUsername }).exec(); + return res.status(200).json({ message: "Post deleted!" }); +}); diff --git a/models/comment.js b/models/comment.js index 5d8bb05..4daec79 100644 --- a/models/comment.js +++ b/models/comment.js @@ -7,13 +7,12 @@ const CommentSchema = new Schema({ author: { type: String, required: true }, post: { type: mongoose.ObjectId, required: true }, _id: { type: mongoose.ObjectId, required: true }, - password: { type: String, required: true }, }); // Virtual for comment RESTful functions CommentSchema.virtual("url").get(function () { // We don't use an arrow function as we'll need the this object - return `/api/${post}/${this._id}`; + return `/${post}/${this._id}`; }); // Export model diff --git a/models/post.js b/models/post.js index a8956cc..f3ce3c2 100644 --- a/models/post.js +++ b/models/post.js @@ -13,7 +13,7 @@ const PostSchema = new Schema({ // Virtual for message URL PostSchema.virtual("url").get(function () { // We don't use an arrow function as we'll need the this object - return `/api/${this._id}`; + return `/${this._id}`; }); // Export model diff --git a/package-lock.json b/package-lock.json index 66b8927..7f37be0 100644 --- a/package-lock.json +++ b/package-lock.json @@ -10,6 +10,7 @@ "dependencies": { "bcryptjs": "^2.4.3", "bootstrap": "^5.3.1", + "cookie-parser": "^1.4.6", "cors": "^2.8.5", "debug": "~2.6.9", "dotenv": "^16.3.1", @@ -20,11 +21,7 @@ "express-session": "^1.17.3", "express-validator": "^7.0.1", "jsonwebtoken": "^9.0.2", - "mongoose": "^7.5.1", - "passport": "^0.6.0", - "passport-jwt": "^4.0.1", - "passport-local": "^1.0.0", - "passport-local-mongoose": "^8.0.0" + "mongoose": "^7.5.1" } }, "node_modules/@mongodb-js/saslprep": { @@ -394,6 +391,26 @@ "node": ">= 0.6" } }, + "node_modules/cookie": { + "version": "0.4.1", + "resolved": "https://registry.npmjs.org/cookie/-/cookie-0.4.1.tgz", + "integrity": "sha512-ZwrFkGJxUR3EIoXtO+yVE69Eb7KlixbaeAWfBQB9vVsNn/o+Yw69gBWSSDK825hQNdN+wF8zELf3dFNl/kxkUA==", + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/cookie-parser": { + "version": "1.4.6", + "resolved": "https://registry.npmjs.org/cookie-parser/-/cookie-parser-1.4.6.tgz", + "integrity": "sha512-z3IzaNjdwUC2olLIB5/ITd0/setiaFMLYiZJle7xg5Fe9KWAceil7xszYfHHBtDFYLSgJduS2Ty0P1uJdPDJeA==", + "dependencies": { + "cookie": "0.4.1", + "cookie-signature": "1.0.6" + }, + "engines": { + "node": ">= 0.8.0" + } + }, "node_modules/cookie-signature": { "version": "1.0.6", "resolved": "https://registry.npmjs.org/cookie-signature/-/cookie-signature-1.0.6.tgz", @@ -829,11 +846,6 @@ "resolved": "https://registry.npmjs.org/function-bind/-/function-bind-1.1.1.tgz", "integrity": "sha512-yIovAzMX49sF8Yl58fSCWJ5svSLuaibPxXQJFLmBObTuCr0Mf1KiPopGM9NiFjiYBCbfaa2Fh6breQ6ANVTI0A==" }, - "node_modules/generaterr": { - "version": "1.5.0", - "resolved": "https://registry.npmjs.org/generaterr/-/generaterr-1.5.0.tgz", - "integrity": "sha512-JgcGRv2yUKeboLvvNrq9Bm90P4iJBu7/vd5wSLYqMG5GJ6SxZT46LAAkMfNhQ+EK3jzC+cRBm7P8aUWYyphgcQ==" - }, "node_modules/get-caller-file": { "version": "2.0.5", "resolved": "https://registry.npmjs.org/get-caller-file/-/get-caller-file-2.0.5.tgz", @@ -1373,64 +1385,6 @@ "node": ">= 0.8" } }, - "node_modules/passport": { - "version": "0.6.0", - "resolved": "https://registry.npmjs.org/passport/-/passport-0.6.0.tgz", - "integrity": "sha512-0fe+p3ZnrWRW74fe8+SvCyf4a3Pb2/h7gFkQ8yTJpAO50gDzlfjZUZTO1k5Eg9kUct22OxHLqDZoKUWRHOh9ug==", - "dependencies": { - "passport-strategy": "1.x.x", - "pause": "0.0.1", - "utils-merge": "^1.0.1" - }, - "engines": { - "node": ">= 0.4.0" - }, - "funding": { - "type": "github", - "url": "https://github.com/sponsors/jaredhanson" - } - }, - "node_modules/passport-jwt": { - "version": "4.0.1", - "resolved": "https://registry.npmjs.org/passport-jwt/-/passport-jwt-4.0.1.tgz", - "integrity": "sha512-UCKMDYhNuGOBE9/9Ycuoyh7vP6jpeTp/+sfMJl7nLff/t6dps+iaeE0hhNkKN8/HZHcJ7lCdOyDxHdDoxoSvdQ==", - "dependencies": { - "jsonwebtoken": "^9.0.0", - "passport-strategy": "^1.0.0" - } - }, - "node_modules/passport-local": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/passport-local/-/passport-local-1.0.0.tgz", - "integrity": "sha512-9wCE6qKznvf9mQYYbgJ3sVOHmCWoUNMVFoZzNoznmISbhnNNPhN9xfY3sLmScHMetEJeoY7CXwfhCe7argfQow==", - "dependencies": { - "passport-strategy": "1.x.x" - }, - "engines": { - "node": ">= 0.4.0" - } - }, - "node_modules/passport-local-mongoose": { - "version": "8.0.0", - "resolved": "https://registry.npmjs.org/passport-local-mongoose/-/passport-local-mongoose-8.0.0.tgz", - "integrity": "sha512-jgfN/B0j11WT5f96QlL5EBvxbIwmzd+tbwPzG1Vk8hzDOF68jrch5M+NFvrHjWjb3lfAU0DkxKmNRT9BjFZysQ==", - "dependencies": { - "generaterr": "^1.5.0", - "passport-local": "^1.0.0", - "scmp": "^2.1.0" - }, - "engines": { - "node": ">= 8.0.0" - } - }, - "node_modules/passport-strategy": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/passport-strategy/-/passport-strategy-1.0.0.tgz", - "integrity": "sha512-CB97UUvDKJde2V0KDWWB3lyf6PC3FaZP7YxZ2G8OAtn9p4HI9j9JLP9qjOGZFvyl8uwNT8qM+hGnz/n16NI7oA==", - "engines": { - "node": ">= 0.4.0" - } - }, "node_modules/path-to-regexp": { "version": "0.1.7", "resolved": "https://registry.npmjs.org/path-to-regexp/-/path-to-regexp-0.1.7.tgz", @@ -1444,11 +1398,6 @@ "node": ">=8" } }, - "node_modules/pause": { - "version": "0.0.1", - "resolved": "https://registry.npmjs.org/pause/-/pause-0.0.1.tgz", - "integrity": "sha512-KG8UEiEVkR3wGEb4m5yZkVCzigAD+cVEJck2CzYZO37ZGJfctvVptVO192MwrtPhzONn6go8ylnOdMhKqi4nfg==" - }, "node_modules/picomatch": { "version": "2.3.1", "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-2.3.1.tgz", @@ -1652,11 +1601,6 @@ "resolved": "https://registry.npmjs.org/safer-buffer/-/safer-buffer-2.1.2.tgz", "integrity": "sha512-YZo3K82SD7Riyi0E1EQPojLz7kpepnSQI9IyPbHHg1XXXevb5dJI7tpyN2ADxGcQbHG7vcyRHk0cbwqcQriUtg==" }, - "node_modules/scmp": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/scmp/-/scmp-2.1.0.tgz", - "integrity": "sha512-o/mRQGk9Rcer/jEEw/yw4mwo3EU/NvYvp577/Btqrym9Qy5/MdWGBqipbALgd2lrdWTJ5/gqDusxfnQBxOxT2Q==" - }, "node_modules/semver": { "version": "7.5.4", "resolved": "https://registry.npmjs.org/semver/-/semver-7.5.4.tgz", diff --git a/package.json b/package.json index e30d2ef..592dfba 100644 --- a/package.json +++ b/package.json @@ -8,6 +8,7 @@ "dependencies": { "bcryptjs": "^2.4.3", "bootstrap": "^5.3.1", + "cookie-parser": "^1.4.6", "cors": "^2.8.5", "debug": "~2.6.9", "dotenv": "^16.3.1", @@ -18,10 +19,6 @@ "express-session": "^1.17.3", "express-validator": "^7.0.1", "jsonwebtoken": "^9.0.2", - "mongoose": "^7.5.1", - "passport": "^0.6.0", - "passport-jwt": "^4.0.1", - "passport-local": "^1.0.0", - "passport-local-mongoose": "^8.0.0" + "mongoose": "^7.5.1" } } diff --git a/routes/api.js b/routes.js similarity index 57% rename from routes/api.js rename to routes.js index 9bcdd4f..215900d 100644 --- a/routes/api.js +++ b/routes.js @@ -1,15 +1,28 @@ 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 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 - should work with json +// 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); @@ -28,10 +41,4 @@ router.post("/:postID/new_comment", comment_controller.post); // comment get router.get("/:postID/:commentID", comment_controller.get); -// comment put -router.put("/:postID/:commentID", comment_controller.put); - -// comment delete -router.delete("/:postID/:commentID", comment_controller.delete); - module.exports = router; diff --git a/strategy/jwt.js b/strategy/jwt.js deleted file mode 100644 index 61cb9f9..0000000 --- a/strategy/jwt.js +++ /dev/null @@ -1,20 +0,0 @@ -const User = require("../models/user.js"); -const JwtStrategy = require("passport-jwt").Strategy; -const ExtractJwt = require("passport-jwt").ExtractJwt; - -const opts = {}; -opts.jwtFromRequest = ExtractJwt.fromAuthHeaderAsBearerToken(); -opts.secretOrKey = process.env.SECRET_KEY; - -const getAdmin = async () => { - const admin = await User.findOne({}).lean().exec(); // only one user in DB - admin. pull it up, jamie! - return admin; -}; - -module.exports = new JwtStrategy(opts, (jwt_payload, done) => { - const admin = getAdmin(); - if (jwt_payload.username === admin.username) { - return done(null, true); - } - return done(null, false); -});