Web Project/market (server)

(Back) Sign in with Refresh Token Rotation

hmmmmmmmmmmmm 2025. 3. 14. 01:12

 

routes > auth.ts

import { Router } from "express";
import { createNewUser, signIn, verifyEmail } from "src/controllers/auth";
import validate from "src/middleware/validator";
import { newUserSchema, verifyTokenSchema } from "src/utils/validationSchema";

const authRouter = Router();
authRouter.post("/sign-in", signIn);

export default authRouter;

 

 

controllers > auth.ts

import { RequestHandler } from "express";
import UserModel from "src/models/user";
import crypto from "crypto";
import AuthVerificationTokenModel from "src/models/authVeirficationToken";
import mail from "src/utils/mail";
import { sendErrorRes } from "src/utils/helper";
import jwt from "jsonwebtoken";
import dotenv from "dotenv";

dotenv.config();

const JWT_SECRET = process.env.JWT_SECRET!;


// 로그인
export const signIn: RequestHandler = async (req, res) => {
 
  // 1. 이메일과 비밀번호를 입력 데이터로 받음
  const { email, password } = req.body;

  // 2. 제공된 이메일로 사용자 검색
  const user = await UserModel.findOne({ email });
  if (!user) return sendErrorRes(res, "Email/Password mismatch!", 403);

  // 3. 비밀번호가 유효한지 확인 (암호화된 형태로 저장되어 있음)
  const isMatched = await user.comparePassword(password);
  if (!isMatched) return sendErrorRes(res, "Email/Password mismatch!", 403);

  // 4. 유효하면 액세스 토큰과 리프레시 토큰 생성
  const payload = { // JWT에 포함할 데이터(페이로드) -> 사용자 ID (사용자 식별에 사용)
    id: user._id 
  };

  const accessToken = jwt.sign(payload, JWT_SECRET, {
    expiresIn: "15m",
  });
  const refreshToken = jwt.sign(payload, JWT_SECRET);

  // 5. 리프레시 토큰을 DB에 저장
  if (!user.tokens) {
    user.tokens = [refreshToken];
  }
  else {
    user.tokens.push(refreshToken);
  }

  await user.save();

  // 6. 두 토큰을 사용자에게 전송
  res.json({
    profile: {
      id: user._id,
      email: user.email,
      name: user.name,
      verified: user.verified,
    },
    tokens: { 
      refresh: refreshToken, 
      access: accessToken 
    },
  });

}