๐Ÿ˜Ž ๊ณต๋ถ€ํ•˜๋Š” ์ง•์ง•์•ŒํŒŒ์นด๋Š” ์ฒ˜์Œ์ด์ง€?

[E-Commerce App with REST API] (6) cookie-parser & Refresh Token์œผ๋กœ ์žฌ๋ฐœ๊ธ‰ ๋ฐ›๊ธฐ (Access Token ์ฐจ์ด์ ) ๋ณธ๋ฌธ

๐Ÿ‘ฉ‍๐Ÿ’ป ๋ฐฑ์—”๋“œ(Back-End)/Node js

[E-Commerce App with REST API] (6) cookie-parser & Refresh Token์œผ๋กœ ์žฌ๋ฐœ๊ธ‰ ๋ฐ›๊ธฐ (Access Token ์ฐจ์ด์ )

์ง•์ง•์•ŒํŒŒ์นด 2023. 3. 23. 00:13
728x90
๋ฐ˜์‘ํ˜•

<๋ณธ ๋ธ”๋กœ๊ทธ๋Š” Developers Corner ์˜ ์œ ํŠœ๋ธŒ๋ฅผ ์ฐธ๊ณ ํ•ด์„œ ๊ณต๋ถ€ํ•˜๋ฉฐ ์ž‘์„ฑํ•˜์˜€์Šต๋‹ˆ๋‹ค :-)>

=> Node.js E-Commerce App with REST API: Let's Build a Real-Life Example!

 

๐ŸŒท login ์‹œ cookie ์ƒ์„ฑํ•˜๊ธฐ 

Cookie ํ—ค๋”๋ฅผ ํŒŒ์‹ฑํ•˜๊ณ , ์ฟ ํ‚ค ์ด๋ฆ„์— ์˜ํ•ด ํ‚ค๊ฐ€ ์ง€์ •๋œ ๊ฐ์ฒด๋กœ req.cookies๋ฅผ ์ฑ„์›€

secret ๋ฌธ์ž์—ด์„ ์ „๋‹ฌํ•˜์—ฌ ์„ ํƒ์ ์œผ๋กœ ์„œ๋ช…๋œ(signed) ์ฟ ํ‚ค ์ง€์›์„ ํ™œ์„ฑํ™”
secret ๋ฌธ์ž์—ด์€ ๋‹ค๋ฅธ ๋ฏธ๋“ค์›จ์–ด์—์„œ ์‚ฌ์šฉํ•  ์ˆ˜ ์žˆ๋„๋ก req.secret์„ ํ• ๋‹นํ•จ

 npm i cookie-parser

 

๐ŸŒท JWT(JSON Web Token)

์œ ์ €๋ฅผ ์ธ์ฆํ•˜๊ณ  ์‹๋ณ„ํ•˜๊ธฐ ์œ„ํ•œ ํ† ํฐ

ํ† ํฐ์€ ์„ธ์…˜๊ณผ๋Š” ๋‹ฌ๋ฆฌ ์„œ๋ฒ„๊ฐ€ ์•„๋‹Œ ํด๋ผ์ด์–ธํŠธ์— ์ €์žฅ

 

โœ… Refresh token

: ์ƒˆ๋กœ์šด access token์„ ์žฌ๋ฐœ๊ธ‰ ๋ฐ›์„ ์ˆ˜ ์žˆ๋Š” ์œ ํšจ ๊ธฐ๊ฐ„์ด ๊ธด token

: ๊ธด ์œ ํšจ๊ธฐ๊ฐ„์„ ๊ฐ€์ง€๋ฉด์„œ, Access Token์ด ๋งŒ๋ฃŒ๋์„ ๋•Œ ์ƒˆ๋กœ ๋ฐœ๊ธ‰ํ•ด์ฃผ๋Š” ์—ด์‡ 

 

โœ… Access Token

: ์ธ์ฆ์„ ์œ„ํ•œ JWT์ด๋ฉด์„œ, ๋™์‹œ์— ๋ณด์•ˆ์„ ์œ„ํ•ด ์œ ํšจ๊ธฐ๊ฐ„์ด ๋งค์šฐ ์งง๋‹ค

=> Access Token์˜ ์œ ํšจ๊ธฐ๊ฐ„์„ ์ค„์ด๊ณ , Refresh Token์ด๋ผ๋Š” ์ƒˆ๋กœ์šด ํ† ํฐ์„ ๋ฐœ๊ธ‰ํ•จ์œผ๋กœ์จ ํ•ด๊ฒฐ

๐Ÿ‘€ ์ž‘๋™ ๋ฐฉ๋ฒ•
1) ์‚ฌ์šฉ์ž๊ฐ€ ๋กœ๊ทธ์ธ์„ ์‹œ๋„
2) ์„œ๋ฒ„๋Š” DB์— ์‚ฌ์šฉ์ž๊ฐ€ ์ €์žฅ๋˜์–ด ์žˆ๋Š”์ง€ ํ™•์ธ
3) ์‚ฌ์šฉ์ž๊ฐ€ ์กด์žฌํ•˜๋ฉด access token๊ณผ refresh token์„ ๋ฐœ๊ธ‰
4) ๋ฐœ๊ธ‰๋œ refresh token์€ ์‚ฌ์šฉ์ž์˜ id์™€ Token ํ…Œ์ด๋ธ”์— ์ €์žฅ
5) ์„œ๋ฒ„๋Š” client์— access token๊ณผ refresh token์„ ์‘๋‹ต
6) ์‚ฌ์šฉ์ž๊ฐ€ ์„œ๋ฒ„์— ์š”์ฒญ์„ ํ•˜๋ฉด, ์„œ๋ฒ„๋Š” access token์„ ๊ฒ€์ฆ ํ›„ ์š”์ฒญํ•œ ๋ฐ์ดํ„ฐ๋ฅผ ์‘๋‹ต
7) Access Token์ด ๋งŒ๋ฃŒ๋œ ํ›„ ์‚ฌ์šฉ์ž๊ฐ€ ์„œ๋ฒ„์— ์š”์ฒญ์„ ํ•˜๊ฒŒ๋˜๋ฉด, access token์˜ ๋งŒ๋ฃŒ๋จ์„ ์‘๋‹ต
8) Access token์˜ ๋งŒ๋ฃŒ๋ฅผ ํ™•์ธํ•œ client๋Š” ์„œ๋ฒ„์— access token์˜ refresh๋ฅผ ์š”์ฒญ
9) ์„œ๋ฒ„๋Š” ๋งŒ๋ฃŒ๋œ access token์—์„œ ์‚ฌ์šฉ์ž์˜ id๋ฅผ ์–ป์€ ํ›„ id๋กœ DB์— refresh token์ด ์œ ํšจํ•œ์ง€ ํ™•์ธ
10) refresh token์ด ์œ ํšจํ•˜๋‹ค๋ฉด access token์„ ์žฌ๋ฐœ๊ธ‰, client์— ์ „์†ก
11) refresh token๋„ ๋งŒ๋ฃŒ๋˜์—ˆ๋‹ค๋ฉด ์ƒˆ๋กœ ๋กœ๊ทธ์ธ

 

 

๐ŸŒท ์ฝ”๋“œ

โœ… controllers/userCtrl.js

const User = require("../models/User");
const bcrypt = require("bcrypt");
const asyncHandler = require("express-async-handler");
const { generateToken } = require("../config/jwtToken");
const { validateMongodbID } = require("../utils/validateMongodbID");
const { generateRefreshToken } = require("../config/refreshToken");
const jwt = require("jsonwebtoken");

// create a user
const createUser = asyncHandler(async (req, res) => {
  const { firstname, lastname, email, mobile, password } = req.body;
  const findUser = await User.findOne({ email: email });

  // email์ด db์— ์—†๋‹ค๋ฉด
  if (!findUser) {
    // Create a new User
    // 1) ์šฐ์„  ๋น„๋ฐ€๋ฒˆํ˜ธ ํ•ด์‰ฌํ™”(์•”ํ˜ธํ™”)
    const hashedPassword = await bcrypt.hash(password, 10);
    // 2) ์ƒˆ User ์ •๋ณด ๋งŒ๋“ค๊ธฐ
    const newUser = await User.create({
      firstname, lastname, email, mobile, password: hashedPassword
    });

    res.json(newUser);
  } else {
    // User already exists
    throw new Error("User already exists");
  }
});

// login a user
const loginCheck = asyncHandler(async (req, res) => {
  const { email, password } = req.body;
  // check if user exists or not
  const findUser = await User.findOne({ email: email });

  // user ๊ฐ€ ์—†๋‹ค๋ฉด done
  if (!findUser) {
    throw new Error("That email is not registered!");
  }
  // Match password (๊ธฐ์กด ๋น„๋ฐ€๋ฒˆํ˜ธ์™€ ์ž…๋ ฅํ•œ ๋น„๋ฐ€๋ฒˆํ˜ธ ์ฒดํฌ)
  bcrypt.compare(password, findUser.password, async (err, isMatch) => {
    const refreshToken = await generateRefreshToken(findUser?._id);
    const updateUser = await User.findByIdAndUpdate(
      findUser._id, {
      refreshToken: refreshToken,
    }, {
      new: true
    }
    );
    res.cookie("refreshToken", refreshToken, {
      httpOnly: true,
      maxAge: 72 * 60 * 60 * 1000,
    });

    if (isMatch) {
      res.json({
        _id: findUser?._id,
        firstname: findUser?.firstname,
        lastname: findUser?.lastname,
        email: findUser?.email,
        mobile: findUser?.mobile,
        token: generateToken(findUser?._id)
      });
    } else {
      throw new Error("Email or Password is incorrect!");
    }
  });
});

// Handle refresh token
const handleRefreshToken = asyncHandler(async (req, res) => {
  try {
    const cookie = req.cookies;
    // refresh token ์—†์Œ!
    if (!cookie?.refreshToken) {
      throw new Error("No Refresh Token in cookies");
    }
    // refresh token ์ƒ์„ฑ!
    const refreshToken = cookie.refreshToken;
    const user = await User.findOne({ refreshToken });

    if (!user) {
      throw new Error("No Refresh token present in db or not matched");
    }
    // refresh token์„ secret key ๊ธฐ๋ฐ˜์œผ๋กœ ์ƒ์„ฑ
    jwt.verify(refreshToken, process.env.SECRET, (err, decoded) => {
      if (err || user.id !== decoded.id) {
        throw new Error("There is something wrong with refresh token");
      }
      // access Token ๋ฐœ๊ธ‰
      const accessToken = generateToken(user?._id);
      res.json({ accessToken });
    });
  } catch (error) {
    throw new Error(error);
  }
});

// Update a user
const updateUser = asyncHandler(async (req, res) => {
  const { _id } = req.user;
  validateMongodbID(_id);

  try {
    const updateUser = await User.findByIdAndUpdate(
      _id,
      {
        firstname: req?.body?.firstname,
        lastname: req?.body?.lastname,
        email: req?.body?.email,
        mobile: req?.body?.mobile,
      }, {
      new: true,
    }
    );
    res.json(updateUser);
  } catch (error) {
    throw new Error(error);
  }
});


// get all users
const getAllUsers = asyncHandler(async (req, res) => {
  try {
    const getUser = await User.find();
    res.json({ getUser });
  } catch (error) {
    throw new Error(error);
  }
});

// get a single users
const getAUsers = asyncHandler(async (req, res) => {
  const { id } = req.params;
  validateMongodbID(id);

  try {
    const getUser = await User.findById(id);
    res.json({ getUser });
  } catch (error) {
    throw new Error(error);
  }
});

// delete a user
const deleteAUser = asyncHandler(async (req, res) => {
  const { id } = req.params;
  validateMongodbID(id);

  try {
    const deleteAUser = await User.findByIdAndDelete(id);
    res.json({ deleteAUser });
  } catch (error) {
    throw new Error(error);
  }
});

// block user
const blockUser = asyncHandler(async (req, res) => {
  const { id } = req.params;
  validateMongodbID(id);

  try {
    const block = await User.findByIdAndUpdate(
      id,
      {
        isBlocked: true,
      },
      {
        new: true,
      }
    );
    res.json(block);
    // res.json({
    //   message : "User blocked"
    // });
  } catch (error) {
    throw new Error(error);
  }
});

const unblockUser = asyncHandler(async (req, res) => {
  const { id } = req.params;
  validateMongodbID(id);

  try {
    const unblock = await User.findByIdAndUpdate(
      id,
      {
        isBlocked: false,
      },
      {
        new: true,
      }
    );
    res.json({
      message: "User unblocked"
    });
  } catch (error) {
    throw new Error(error);
  }
});


module.exports = {
  createUser,
  loginCheck,
  getAllUsers,
  getAUsers,
  deleteAUser,
  updateUser,
  blockUser,
  unblockUser,
  handleRefreshToken
};

 

โœ… routes/authRoute.js

const express = require("express");
const router = express.Router();
const { createUser, loginCheck, getAllUsers, getAUsers, deleteAUser, updateUser, blockUser, unblockUser, handleRefreshToken } = require("../controllers/userCtrl");
const { authMiddleware, isAdmin } = require("../middlewares/authMiddleware");

router.post("/register", createUser);
router.post("/login", loginCheck);

router.get("/all-users", getAllUsers);
router.get("/refresh", handleRefreshToken);
router.get("/:id", authMiddleware, isAdmin, getAUsers);

router.delete("/:id", deleteAUser);

router.put("/edit-user", authMiddleware, updateUser);
router.put("/block-user/:id", authMiddleware, isAdmin, blockUser);
router.put("/unblock-user/:id", authMiddleware, isAdmin, unblockUser);


module.exports = router;

 

โœ… config/jwtToken.js

const jwt = require("jsonwebtoken");
const generateToken = (id) => {
    return jwt.sign({id}, process.env.SECRET, {expiresIn : "1d"});
};

module.exports = {
    generateToken
};
728x90
๋ฐ˜์‘ํ˜•
Comments