🎬 구현한 동작 시나리오
🔶: 사용자 시점
🔷: 서버 시점
🔶 쇼핑몰 사이트에서 사용자가 id를 입력 후 로그인 버튼을 클릭
🔷 userId (사용자가 입력한 id)를 가지고 access token과 refresh token을 발급하여 JWT를 cookie에 저장
🔶 사용자는 로그인을 했으니 자기 계정의 장바구니를 클릭
🔷 클라이언트의 cookie를 받아 JWT의 access token이 만료되지는 않았는지, 서버가 발행한 토큰이 맞는지 검증
🔹 access token이 모두 정상이라면, 자연스레 통과
🔹 access token이 유효하지 않은 토큰이라면, cookie의 jwt를 지우고 사용자에게 재로그인 요청
🔹 access token이 만료되었다면, refresh token을 대상으로 만료되지는 않았는지, 서버가 발행한 토큰이 맞는지 검증
🔹 refresh token이 모두 정상이라면, access token을 새로 발급하여 cookie에 저장
🔹 refresh token이 유효하지 않은 토큰이라면, cookie의 jwt를 지우고 사용자에게 재로그인 요청
🔹 refresh token이 만료되었다면, cookie의 jwt를 지우고 사용자에게 재로그인 요청
🔶 사용자는 token의 상태에 따라 자연스레 장바구니를 들어갈 수도, 재로그인을 해야 할 수도 있음
코드
코드 설명에 앞서 Passport.js 라이브러리를 사용하지 않았고,
Express.js 환경에서 jsonwebtoken 모듈을 사용하여 구현하였다.
JWT 발행
사용자가 처음 로그인을 하거나 재로그인을 해야 할 경우 JWT를 새로 발행한다.
const jwt = {
accessToken : getToken().accessToken(userId),
refreshToken: getToken().refreshToken(userId),
};
res.cookie("jwt", jwt);
userId로 access token, refresh token을 발행하여 cookie에 저장하는 모습이다.
const jwt = require("jsonwebtoken");
export const getToken = () => {
return {
accessToken(userId) {
return jwt.sign(
{
userId
},
process.env.ACCESS_TOKEN_SECRET,
{
expiresIn: "20m",
issuer: "you",
}
);
},
refreshToken(userId) {
return jwt.sign(
{
userId
},
process.env.REFRESH_TOKEN_SECRET,
{
expiresIn: "30 days",
issuer: "you",
}
);
},
};
};
jwt.sign() 메서드는 npm의 jsonwebtoken 모듈 명세를 참고하여
담고 싶은 정보, secret key, 만료 기간, 발행자를 저장하여 암호화 후 생성했다.
이 외에도 여러가지 옵션을 줄 수 있다.
JWT 검증
사용자가 로그인 한 상태에서 장바구니, 채팅 목록 등 private 한 동작을 하려 할 때 JWT를 검증한다.
아래 코드들은 모두 하나의 함수안에 담긴 코드 조각들인데, 뜯어서 차근차근 설명하겠다.
let loginFlag = false;
const jwt = req.cookies?.jwt;
if (jwt === undefined) {
res.send({ loginFlag });
return;
}
loginFlag는 이 함수의 return 값이며,
true라면 유효한 사용자이기 때문에 자연스레 넘어가고,
false라면 어떠한 이유로 유효하지 않다 판단하여 cookie의 JWT를 지우고 재로그인을 요청한다.
보다시피 cookie의 JWT를 추출하여 없다면 false를 return 한다.
const { accessToken, refreshToken } = jwt;
const accessTokenStateCode = getTokenStateCode(accessToken, TOKEN_TYPE.access);
access token을 검증하여 토큰 상태 코드를 저장한다.
토큰 상태 코드는 총 3가지로 나타내도록 구현했다.
VALID: 만료되지 않았고, 발행자가 유효함
INVALID_ISSUER: 발행자가 유효하지 않음
EXPIRED: 토큰이 만료됨
if (accessTokenStateCode === TOKEN_STATE_CODE.VALID) {
loginFlag = true;
}
access token이 유효하다면 자연스럽게 넘어간다.
if (accessTokenStateCode === TOKEN_STATE_CODE.INVALID_ISSUER) {
res.clearCookie("jwt");
loginFlag = false;
}
access token의 발행자가 유효하지 않다면, cookie의 jwt를 지우고 재로그인을 요청한다.
if (accessTokenStateCode === TOKEN_STATE_CODE.EXPIRED) {
// refresh token 상태 코드
const refreshTokenStateCode = getTokenStateCode(refreshToken, TOKEN_TYPE.refresh);
// refresh token 유효 - 새 access token 발행 (재로그인 X)
if (refreshTokenStateCode === TOKEN_STATE_CODE.VALID) {
const newAccessToken = getNewAccessToken(refreshToken);
const newJwt = {
accessToken: newAccessToken,
refreshToken: refreshToken,
};
res.clearCookie("jwt").cookie("jwt", newJwt);
loginFlag = true;
}
// refresh token이 만료되었거나, 발행자가 유효하지 않음 - 재로그인
else {
res.clearCookie("jwt");
loginFlag = false;
}
}
access token이 만료되었다면, refresh token을 검증한다.
access token과 마찬가지로 refresh token의 상태 코드를 받아서,
VALID라면 새 refresh token으로 새 access token을 발급하여 기존의 cookie를 지우고 새 cookie를 구워 jwt를 저장한다.
EXPIRED이거나 INVALID_ISSUER라면, cookie를 지우고 재로그인을 요청한다.
JWT.IO
JSON Web Tokens are an open, industry standard RFC 7519 method for representing claims securely between two parties.
jwt.io
https://www.npmjs.com/package/jsonwebtoken
jsonwebtoken
JSON Web Token implementation (symmetric and asymmetric). Latest version: 8.5.1, last published: 4 years ago. Start using jsonwebtoken in your project by running `npm i jsonwebtoken`. There are 20995 other projects in the npm registry using jsonwebtoken.
www.npmjs.com
'기억 저장소' 카테고리의 다른 글
[nCloud] 서버와 DB 따로 배포 후 연동 (2) | 2022.10.14 |
---|---|
[TypeORM] Cannot add or update a child row: a foreign key constraint fails 오류 해결 (0) | 2022.10.09 |
TypeScript + Express 초간단 세팅 (0) | 2022.10.05 |
[Node.js] 초간단 Github OAuth 인증 구현 (0) | 2022.10.02 |
nCloud 서버 배포 에러 해결 모음집 (0) | 2022.09.24 |
댓글