2023. 11. 22. 00:23ㆍJavascript Node.js
Signin and Login code
// ./routers/auth.router.js
import { Router } from 'express';
import db from '../models/index.cjs';
import bcrypt from 'bcrypt';
import {
PASSWORD_HASH_SALT_ROUNDS,
JWT_ACCESS_TOKEN_SECRET,
JWT_ACCESS_TOKEN_EXPIRES_IN,
} from '../constants/security.constant.js';
import jwt from 'jsonwebtoken';
const { Users } = db;
const authRouter = Router();
// 회원가입 /api/auth/siginup
authRouter.post('/signup', async (req, res) => {
try {
const { email, password, passwordConfirm, name } = req.body;
if (!email) {
return res.status(400).json({
success: false,
message: '이메일 형식이 맞지 않습니다.',
});
}
if (!password) {
return res.status(400).json({
success: false,
message: '비밀번호 입력이 필요합니다.',
});
}
if (!passwordConfirm) {
return res.status(400).json({
success: false,
message: '비밀번호 확인 입력이 필요합니다.',
});
}
if (!name) {
return res.status(400).json({
success: false,
message: '이름입력이 필요합니다.',
});
}
if (password !== passwordConfirm) {
return res.status(400).json({
success: false,
message: '입력한 비밀번호가 서로 일치하지 않습니다.',
});
}
if (password.length < 6) {
return res.status(400).json({
success: false,
message: '비밀번호는 최소 6자리 이상입니다.',
});
}
let emailValidationRegex = new RegExp('[a-z0-9._]+@[a-z]+.[a-z]{2,3}');
const isValidEmail = emailValidationRegex.test(email);
if (!isValidEmail) {
return res.status(400).json({
success: false,
message: '올바른 이메일 형식이 아닙니다.',
});
}
const existedUser = await Users.findOne({ where: { email } });
if (existedUser) {
return res.status(400).json({
success: false,
message: '이미 가입된 이메일 입니다.',
});
}
const hashedPassword = bcrypt.hashSync(password, PASSWORD_HASH_SALT_ROUNDS);
const newUser = (
await Users.create({ email, password: hashedPassword, name })
).toJSON();
delete newUser.password;
return res.status(201).json({
success: true,
message: '회원가입에 성공했습니다.',
data: newUser,
});
} catch (error) {
console.error(error);
return res.status(500).json({
success: true,
message: '예상치 못한 에러가 발생하였습니다. 관리자에게 문의하세요',
});
}
});
// 로그인
authRouter.post('/signin', async (req, res) => {
try {
const { email, password } = req.body;
if (!email) {
return res.status(400).json({
success: false,
message: '이메일 입력이 필요합니다.',
});
}
if (!password) {
return res.status(400).json({
success: false,
message: '비밀번호 입력이 필요합니다.',
});
}
const user = (await Users.findOne({ where: { email } }))?.toJSON();
const hashedPassword = user?.password;
// isPsswordMatched 의 bcrypt.compareSync(password, hashedPassword); 값이 같으면 true를 반환
// 반대로 반대 값이 나오면 false 즉 같지 않다는 의미
const isPasswordMatched = bcrypt.compareSync(password, hashedPassword);
const isCorrectUser = user && isPasswordMatched;
if (!isCorrectUser) {
return res.status(401).json({
success: false,
message: '일치하는 인증 정보가 없습니다.',
});
}
// accessToken 발급
// 회원가입시 사용했던 id를 userid이름으로 변경
// jwt.sign()메서드는 별도의 promise를 반환하지 않고 바로 string을 반환하기 때문에 await을 붙일 필요가 없다.
const accessToken = jwt.sign(
{ /* payload값 */ userId: user.id },
/* secret key */ JWT_ACCESS_TOKEN_SECRET,
{ /* options-시간설정 */ expiresIn: JWT_ACCESS_TOKEN_EXPIRES_IN },
);
return res.status(200).json({
success: true,
message: '로그인에 성공했습니다.',
data: { accessToken },
});
} catch (error) {
console.error(error);
return res.status(500).json({
success: true,
message: '예상치 못한 에러가 발생하였습니다. 관리자에게 문의하세요',
});
}
});
export { authRouter };
User Infromation code
// ./middleware/need-signin.middleware.js
import jwt from 'jsonwebtoken';
import { JWT_ACCESS_TOKEN_SECRET } from '../constants/security.constant.js';
import db from '../models/index.cjs';
const { Users } = db;
export const needSignIn = async (req, res, next) => {
try {
const authorizationHeader = req.headers.authorization;
// 인증 정보가 아예 없는 경우
if (!authorizationHeader) {
return res.status(400).json({
success: true,
message: '인증정보가 없습니다.',
});
}
// JWT 기본적인 형태 -> Authorization: Bearer <token>
const [tokenType, accessToken] = authorizationHeader?.split(' ');
// 토큰형식이 일치하지 않는 경우
if (tokenType !== 'Bearer') {
return res.status(400).json({
success: true,
message: '지원하지 않는 인증 방식입니다.',
});
}
// AccessToken이 존재하지 않는 경우
if (!accessToken) {
return res.status(400).json({
success: true,
message: 'AccessToken이 없습니다.',
});
}
const decodedPayload = jwt.verify(
accessToken,
JWT_ACCESS_TOKEN_SECRET + '1',
);
const { userId } = decodedPayload;
console.log({ decodedPayload });
// 일치하는 userId가 없는 경우
const user = (await Users.findByPk(userId)).toJSON();
if (!user) {
return res.status(400).json({
success: true,
message: '존재하지 않는 사용자 입니다.',
});
}
delete user.password;
res.locals.user = user;
next();
} catch (error) {
console.error(error);
let statusCode = 500;
let errorMessage = '';
// switch case 로 분기처리
switch (error.message) {
// JWT 유효기간이 지난 경우
case 'jwt expired':
statusCode = 401;
errorMessage = '인증 정보 유효기간이 지났습니다.';
break;
// 검증에 실패한 경우
case 'invalid signature':
statusCode = 401;
errorMessage = '유효하지 않는 인증정보입니다.';
break;
default:
statusCode = 500;
errorMessage =
'예상치 못한 에러가 발생하였습니다. 관리자에게 문의하세요';
break;
}
return res.status(statusCode).json({
success: true,
message: errorMessage,
});
}
};
Associated modules
// ./models/index.cjs
// Sequelize ORM 구조
// sequelize는 ORM(Object-Relational Mapping)로 분류
// ORM이란 객체와 관계형 데이터베이스의 관계를 매핑 해주는 도구이다.
'use strict';
const fs = require('fs');
const path = require('path');
const Sequelize = require('sequelize');
const process = require('process');
const basename = path.basename(__filename);
const env = process.env.NODE_ENV || 'development';
// config 파일을 .cjs로 변경한 점이 특이점이다. 이유는 현재 프로젝트는 model type으로 진행
const config = require(__dirname + '/../config/config.cjs')[env];
const db = {};
let sequelize;
if (config.use_env_variable) {
sequelize = new Sequelize(process.env[config.use_env_variable], config);
} else {
sequelize = new Sequelize(
config.database,
config.username,
config.password,
config,
);
}
fs.readdirSync(__dirname)
.filter((file) => {
return (
file.indexOf('.') !== 0 &&
file !== basename &&
file.slice(-4) === '.cjs' &&
file.indexOf('.test.js') === -1
);
})
.forEach((file) => {
const model = require(path.join(__dirname, file))(
sequelize,
Sequelize.DataTypes,
);
db[model.name] = model;
});
Object.keys(db).forEach((modelName) => {
if (db[modelName].associate) {
db[modelName].associate(db);
}
});
db.sequelize = sequelize;
db.Sequelize = Sequelize;
module.exports = db;
// ./constants/security.constant.js
import 'dotenv/config';
// 환경변수의 값을 불러올 때에는 기본적으로 String으로 불러오게 된다
// 따라서 강제적으로 항변환을 해줘야한다.
export const PASSWORD_HASH_SALT_ROUNDS = Number.parseInt(
process.env.PASSWORD_HASH_SALT_ROUNDS,
10,
);
export const JWT_ACCESS_TOKEN_SECRET = process.env.JWT_ACCESS_TOKEN_SECRET;
export const JWT_ACCESS_TOKEN_EXPIRES_IN = '12h';
Additional webpage information
https://velog.io/@from_numpy/NestJS-How-to-implement-Refresh-Token-with-JWT
[NestJS] How to implement Refresh-Token using JWT?
오랜만에 작성하는 포스팅인 것 같다. 최근에 개인적인 일도 있고, 뭔가 쉬어가고 싶어서 천천히 공부를 하며 어떤 주제를 다뤄보며 좋을까 고민을 했었다. 그러던 와중, 예전부터 들어만보았지
velog.io
[GIT] github branch를 활용해서 협업하기
과제에 대한 피드백에서 repo에 push할때 기능별로 파트를 분담해서 브랜치를 생성하고 코드를 관리하라는 내용을 받았다.사실 팀프로젝트 경험이 거의 없고, 브랜치가 무엇인지 잘 몰랐기 때문
velog.io
https://sequelize.org/docs/v6/core-concepts/model-querying-finders/
Model Querying - Finders | Sequelize
Finder methods are the ones that generate SELECT queries.
sequelize.org
JWT.IO
JSON Web Tokens are an open, industry standard RFC 7519 method for representing claims securely between two parties.
jwt.io
https://developer.mozilla.org/ko/docs/Web/HTTP/Status
HTTP 상태 코드 - HTTP | MDN
HTTP 응답 상태 코드는 특정 HTTP 요청이 성공적으로 완료되었는지 알려줍니다. 응답은 5개의 그룹으로 나누어집니다: 정보를 제공하는 응답, 성공적인 응답, 리다이렉트, 클라이언트 에러, 그리고
developer.mozilla.org
https://developer.mozilla.org/ko/docs/Web/JavaScript/Reference/Operators/Optional_chaining
Optional chaining - JavaScript | MDN
optional chaining 연산자 (?.) 는 체인의 각 참조가 유효한지 명시적으로 검증하지 않고, 연결된 객체 체인 내에 깊숙이 위치한 속성 값을 읽을 수 있다.
developer.mozilla.org
https://www.avast.com/random-password-generator#pc
Random Password Generator | Create Strong Passwords | Avast
You are putting yourself at risk if you are entrusting an unknown online random sequence generator tool for your passwords. If you use a free password generator online, the site might be decrypted or presenting compromised information, meaning hackers coul
www.avast.com
https://www.npmjs.com/package/jsonwebtoken
jsonwebtoken
JSON Web Token implementation (symmetric and asymmetric). Latest version: 9.0.2, last published: 3 months ago. Start using jsonwebtoken in your project by running `npm i jsonwebtoken`. There are 25694 other projects in the npm registry using jsonwebtoken.
www.npmjs.com
'Javascript Node.js' 카테고리의 다른 글
Javascript: Optional chaing (?.) (0) | 2023.12.11 |
---|---|
Project: present a web page about the blog. (0) | 2023.12.08 |
How to use Access Token and Refresh Token? (0) | 2023.11.27 |
Nodejs: Signing up and signing in with a refresh token and an access token (1) | 2023.11.22 |
Nodejs: The order of web development (0) | 2023.11.20 |