From 51ab809286122c48c5a3a6cba7850ffa8dfc82bd Mon Sep 17 00:00:00 2001 From: OKK Date: Fri, 24 Feb 2023 17:36:46 +0300 Subject: [PATCH] Add JWT-Cookies-RefreshToken --- api/routes/auth.js | 57 ++++++++++++++++++++++++++-- api/utils/verifyRefreshToken.js | 40 +++++++++++++++++++ api/utils/verifyToken.js | 5 ++- api/verifyToken.js | 18 --------- client/src/App.js | 27 ++++++++++++- client/src/components/SinglePost.jsx | 8 ++-- client/src/pages/Settings.jsx | 1 + client/src/pages/Write.jsx | 18 +++++---- 8 files changed, 140 insertions(+), 34 deletions(-) create mode 100644 api/utils/verifyRefreshToken.js delete mode 100644 api/verifyToken.js diff --git a/api/routes/auth.js b/api/routes/auth.js index 88efdbab..353deec3 100644 --- a/api/routes/auth.js +++ b/api/routes/auth.js @@ -4,7 +4,7 @@ const bcrypt = require("bcrypt"); const jwt = require("jsonwebtoken"); const verify = require("../utils/verifyToken"); -//REGISTERs +//REGISTER router.post("/register", async (req, res) => { try { const userExist = await User.findOne({ email: req.body.email }); @@ -39,18 +39,29 @@ router.post("/login", async (req, res) => { const accessToken = jwt.sign( { id: user._id, username: user.username }, process.env.MY_SECRET_KEY, - { expiresIn: "30d" } + { expiresIn: "30s" } + ); + + const refreshToken = jwt.sign( + { id: user._id, username: user.username }, + process.env.MY_SECRET_REFRESH ); if (req.cookies["accessToken"]) { req.cookies["accessToken"] = ""; } + if (req.cookies["refreshToken"]) { + req.cookies["refreshToken"] = ""; + } const { password, ...others } = user._doc; res .cookie("accessToken", accessToken, { httpOnly: true, }) + .cookie("refreshToken", refreshToken, { + httpOnly: true, + }) .status(200) .json(others); } catch (err) { @@ -58,13 +69,53 @@ router.post("/login", async (req, res) => { } }); +//REFRESH +router.get("/refresh", async (req, res, next) => { + const prevToken = req.cookies.refreshToken; + if (prevToken) { + jwt.verify(prevToken, process.env.MY_SECRET_REFRESH, (err, user) => { + if (err) res.status(403).json("Token is not valid!"); + res.clearCookie("accessToken"); + res.clearCookie("refreshToken"); + + const newaccesstoken = jwt.sign( + { id: user._id, username: user.username }, + process.env.MY_SECRET_KEY, + { expiresIn: "30s" } //30 seconds + ); + console.log("Regenerated accessToken\n", newaccesstoken); + const newrefreshToken = jwt.sign( + { id: user._id, username: user.username }, + process.env.MY_SECRET_REFRESH + ); + console.log("Regenerated refreshToken\n", newrefreshToken); + console.log("$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$\n"); + + res.cookie("accessToken", newaccesstoken, { + expires: new Date(Date.now() + 1000 * 30), // 30 seconds + httpOnly: true, + }); + res + .cookie("refreshToken", newrefreshToken, { + httpOnly: true, + }) + .sendStatus(200); + req.user = user; + next(); + }); + } else { + return res.status(401).json("You are not authenticated!"); + } +}); + +//LOGOUT router.post("/logout", verify, async (req, res) => { const token = req.cookies.accessToken; if (token) { jwt.verify(token, process.env.MY_SECRET_KEY, (err, user) => { if (err) return res.status(403).json("Token is not valid!"); res.clearCookie("accessToken"); - req.cookies["accessToken"] = ""; + res.clearCookie("refreshToken"); return res.status(200).json("Successfully Logged Out."); }); } else { diff --git a/api/utils/verifyRefreshToken.js b/api/utils/verifyRefreshToken.js new file mode 100644 index 00000000..3dcff870 --- /dev/null +++ b/api/utils/verifyRefreshToken.js @@ -0,0 +1,40 @@ +const jwt = require("jsonwebtoken"); + +function verifyRefreshToken(req, res, next) { + const prevToken = req.cookies.refreshToken; + if (prevToken) { + jwt.verify(prevToken, process.env.MY_SECRET_REFRESH, (err, user) => { + if (err) res.status(403).json("Token is not valid!"); + res.clearCookie("accessToken"); + res.clearCookie("refreshToken"); + + const newaccesstoken = jwt.sign( + { id: user._id, username: user.username }, + process.env.MY_SECRET_KEY, + { expiresIn: "30s" } // 30 seconds + ); + console.log("Regenerated accessToken\n", newaccesstoken); + const newrefreshToken = jwt.sign( + { id: user._id, username: user.username }, + process.env.MY_SECRET_REFRESH + ); + console.log("Regenerated refreshToken\n", newrefreshToken); + + res.cookie("accessToken", newaccesstoken, { + expires: new Date(Date.now() + 1000 * 30), // 30 seconds + httpOnly: true, + }); + res + .cookie("refreshToken", newrefreshToken, { + httpOnly: true, + }) + .sendStatus(200); + req.user = user; + next(); + }); + } else { + return res.status(401).json("You are not authenticated!"); + } +} + +module.exports = verifyRefreshToken; diff --git a/api/utils/verifyToken.js b/api/utils/verifyToken.js index 2ba0caf4..c5dbe335 100644 --- a/api/utils/verifyToken.js +++ b/api/utils/verifyToken.js @@ -4,7 +4,10 @@ function verify(req, res, next) { const token = req.cookies.accessToken; if (token) { jwt.verify(token, process.env.MY_SECRET_KEY, (err, user) => { - if (err) res.status(403).json("Token is not valid!"); + if (err) { + res.status(403).json("Token is not valid!"); + console.log("hataburada1"); + } req.user = user; next(); }); diff --git a/api/verifyToken.js b/api/verifyToken.js deleted file mode 100644 index aec6ec57..00000000 --- a/api/verifyToken.js +++ /dev/null @@ -1,18 +0,0 @@ -const jwt = require("jsonwebtoken"); - -function verify(req, res, next) { - const authHeader = req.headers.token; - if (authHeader) { - const token = authHeader.split(" ")[1]; - - jwt.verify(token, process.env.MY_SECRET_KEY, (err, user) => { - if (err) res.status(403).json("Token is not valid!"); - req.user = user; - next(); - }); - } else { - return res.status(401).json("You are not authenticated!"); - } -} - -module.exports = verify; diff --git a/client/src/App.js b/client/src/App.js index 49e19b12..a4fd7db9 100644 --- a/client/src/App.js +++ b/client/src/App.js @@ -9,12 +9,37 @@ import Contact from "pages/Contact"; import Single from "pages/Single"; import Page404 from "pages/Page404"; import { Routes, Route } from "react-router-dom"; -import { useContext } from "react"; +import { useContext, useEffect } from "react"; import { Context } from "context/AuthContext"; +import axios from "axios"; function App() { const { user } = useContext(Context); + const credentials = { + withCredentials: true, + }; + + const refreshToken = async () => { + try { + await axios.get("/auth/refresh", credentials); + } catch (err) { + console.log(err); + } + }; + + useEffect(() => { + if (user) { + const interval = setInterval(() => { + refreshToken(); + }, 1000 * 29); + + return () => { + clearInterval(interval); + }; + } + }); + return ( <> diff --git a/client/src/components/SinglePost.jsx b/client/src/components/SinglePost.jsx index ae78d123..7ef0a09d 100644 --- a/client/src/components/SinglePost.jsx +++ b/client/src/components/SinglePost.jsx @@ -16,10 +16,6 @@ export default function SinglePost() { const [desc, setDesc] = useState(""); const [updateMode, setUpdateMode] = useState(false); - const credentials = { - withCredentials: true, - }; - useEffect(() => { const getPost = async () => { const res = await axios.get("/posts/" + path); @@ -30,6 +26,10 @@ export default function SinglePost() { getPost(); }, [path]); + const credentials = { + withCredentials: true, + }; + const handleDelete = async () => { try { await axios.delete(`/posts/${post._id}`, credentials); diff --git a/client/src/pages/Settings.jsx b/client/src/pages/Settings.jsx index c6764eee..1e6527e9 100644 --- a/client/src/pages/Settings.jsx +++ b/client/src/pages/Settings.jsx @@ -18,6 +18,7 @@ export default function Settings() { const credentials = { withCredentials: true, }; + const handleSubmit = async (e) => { e.preventDefault(); dispatch({ type: "UPDATE_START" }); diff --git a/client/src/pages/Write.jsx b/client/src/pages/Write.jsx index 9fbf2985..0530c33a 100644 --- a/client/src/pages/Write.jsx +++ b/client/src/pages/Write.jsx @@ -11,6 +11,10 @@ export default function Write() { const [file, setFile] = useState(null); const { user } = useContext(Context); + const credentials = { + withCredentials: true, + }; + const handleSubmit = async (e) => { e.preventDefault(); const newPost = { @@ -29,15 +33,15 @@ export default function Write() { } catch (err) {} } try { - const res = await axios.post("/posts", newPost, { - headers: { - token: - "Bearer " + JSON.parse(localStorage.getItem("user")).accessToken, - }, - }); + const res = await axios.post("/posts", newPost, credentials); + console.log("bug1"); window.location.replace("/post/" + res.data._id); - } catch (err) {} + console.log("bug2"); + } catch (err) { + console.log(err); + } }; + return (