일 | 월 | 화 | 수 | 목 | 금 | 토 |
---|---|---|---|---|---|---|
1 | 2 | 3 | 4 | 5 | ||
6 | 7 | 8 | 9 | 10 | 11 | 12 |
13 | 14 | 15 | 16 | 17 | 18 | 19 |
20 | 21 | 22 | 23 | 24 | 25 | 26 |
27 | 28 | 29 | 30 |
- 신입 이력서
- 기획
- 계획
- 대학졸업
- MONGOOSE
- 개발자 이력서
- 개발
- 개인프로젝트
- react
- Next.js
- 프론트엔드
- 회고
- aws s3
- 캐플라이어
- 공부
- Javascript
- TypeScript
- Next.js 13
- 삶
- 개인 프로젝트
- 신입 프론트엔드
- 신입 개발자
- 신입
- s3 bucket
- CSS
- 구상
- 이력서
- 백엔드
- Today
- Total
개발 마라톤
[개발] Next.js 13 JWT 발급 본문

Next.js 13 JWT 발급
JWT란?
JSON Web Token Introduction - jwt.io
JWT.IO
JSON Web Tokens are an open, industry standard RFC 7519 method for representing claims securely between two parties.
jwt.io
JWT(JSON Web Token)는 JSON 객체로 당사자 간에 정보를 안전하게 전송하기 위한 간결하고 독립적인 방법을 정의하는 개방형 표준(RFC 7519)입니다. 이 정보는 디지털 서명되어 있으므로 확인하고 신뢰할 수 있습니다
공식 문서에서의 권한 부여의 예시에서는 로그인 기능 자체를 예시로 들었으며,
나도 이번에 JWT를 활용하여 로그인 기능을 구현하려고 한다.
JWT의 구조
JWT 토큰의 구조는 간단하게 다음과 같다.
간결한 형태의 JSON 웹 토큰은 다음과 같은 점()으로 구분된 세 부분으로 구성됩니다..
머리글(헤더).
페이로드(내용).
서명
따라서 JWT는 일반적으로 다음과 같습니다.
xxxxx.yyyyy.zzzzz
헤더, 페이로드, 서명에 관한 내용은 각각 다음과 같다.
헤더 | 헤더는 일반적으로 토큰 유형(JWT)과 사용 중인 서명 알고리즘(예: HMAC SHA256 또는 RSA)의 두 부분으로 구성됩니다. { "alg": "HS256", "typ": "JWT" } |
페이로드 | 토큰의 두 번째 부분은 클레임을 포함하는 페이로드(내용)입니다. 클레임은 메타 데이터와 같은 정보의 정보입니다. Registered claims : 필수는 아니지만 권장되는 미리 정의된 클레임 집합입니다. 그 중 일부는 iss(발급자), exp(만료 시간), sub(제목), aud(청중) 등입니다. Public claims :JWT를 사용하는 사용자가 마음대로 정의할 수 있습니다. Private claims : 사용에 동의한 당사자 간에 정보를 공유하기 위해 만든 사용자 지정 소유권 주장 { "sub": "1234567890", "name": "John Doe", "admin": true } |
서명 | 예를 들어 HMAC SHA256 알고리즘을 사용하려는 경우 다음과 같은 방식으로 서명이 만들어집니다. HMACSHA256( base64UrlEncode(header) + "." + base64UrlEncode(payload), secret) 서명은 메시지가 도중에 변경되지 않았는지 확인하는 데 사용되며, 개인 키로 서명된 토큰의 경우 JWT의 발신자가 해당 발신자인지 확인할 수도 있습니다. |
결과적으로는 다음과 같은 내용을 나타낸다.

구현 시 확인 사항
JWT의 경우 로그인이 되면 단순히 서명된 토큰을 보내주는 메커니즘이다.
서버에 그 로그인 정보를 저장하는 Express.js 의 Session 기능에 비해 가볍지만, 토큰이 탈취되는 순간 심각한 보안 문제가 발생할 수 있다.
본 문서에서도 다음과 같은 주의를 한다.
일반적으로 토큰을 필요 이상으로 오래 보관해서는 안 됩니다.
또한 보안 부족으로 인해 민감한 세션 데이터를 브라우저 저장소에 저장해서는 안 됩니다.
오래 보관해서는 안되므로, 유효 기간을 충분히 짧게 설정해줄 필요가 있다.
또한 브라우저 저장소는 localStorage, sessionStorage를 말하는데,
HTML5 Security - OWASP Cheat Sheet Series
HTML5 Security - OWASP Cheat Sheet Series
HTML5 Security Cheat Sheet Introduction The following cheat sheet serves as a guide for implementing HTML 5 in a secure fashion. Communication APIs Web Messaging Web Messaging (also known as Cross Domain Messaging) provides a means of messaging between doc
cheatsheetseries.owasp.org
해당 문서를 확인하면 브라우저의 저장소는 localStorage.getItem과 같은 스크립트를 이용한 XSS 공격에 취약하므로 사용을 권장하지 않는 것 이다.
반면 쿠키는 httpOnly의 플래그를 통해 브라우저에서 보이지 않는 http 상의 쿠키를 지정할 수 있으므로 해당 방법을 권장하는 듯 하다.
추가적으로 JWT를 통한 인증 방법은 다음과 같이 헤더를 사용하길 원한다.
사용자가 보호된 경로 또는 리소스에 액세스하려고 할 때마다 사용자 에이전트는 일반적으로 Bearer 스키마를 사용하여 Authorization 헤더에서 JWT를 보내야 합니다.
Authorization: Bearer <token>
RFC 6750 - The OAuth 2.0 Authorization Framework: Bearer Token Usage (ietf.org)
해당 헤더는 국제 인터넷 표준화 기구에서IETF RFC 6750 로 규정한 표준화 규칙이다.
JWT 구현
JWT로 로그인 및 로그인 유지하기(feat. Next JS) (velog.io)
JWT로 로그인 및 로그인 유지하기(feat. Next JS)
클라이언트와 서버 사이에 통신을 할 때 권한을 위해 사용하는 토큰이다. 암호화된 상태로 주고 받을 수 있다. 클라이언트가 토큰이 없으면 서버에서 거부하고, 토큰이 있다면 그 토큰의 종류에
velog.io
해당 블로그 글을 중심으로 구현하도록 하겠다.
구현 흐름을 간략하게 정리해보겠다.
- POST /api/session (로그인) API에서 로그인 정보 체크
- JWT을 생성하는 유틸 함수에서 { accessToken, refreshToken } 을 반환
- 만료된 accessToken의 경우 refreshToken을 확인하여 재 발급함
이후에는 middleware.ts 를 통해 사용자 로그인 여부를 확인 하는 등의 확장 흐름을 기대해볼 수 있겠다.
백엔드 코드
- JWT 생성
JWT 생성을 위한 유틸 함수를 만들어보자.
JWT을 두 가지 생성할 건데 그 내용은 다음과 같다.
토큰 종류 | 내용 |
accessToken | 사용자 인증을 위한 토큰. 유효 기간을 짧게 설정하여 상대적으로 탈취되어도 덜 위험이 되도록 설계되었다. 보안을 생각하지 않은 단순한 사용자 인증을 위한 토큰이다. |
refreshToken | accessToken 재발급을 위한 토큰. 유효 기간을 길게 설정하고, accessToken과 다르게 서버의 DB에 저장해둔다. 탈취되면 안되기 때문에 httpOnly, secure 설정의 쿠키로 전달될 것이다. 보안만을 위한 토큰이고, 사용자 인증은 전적으로 accessToken이 담당한다. |
해당 설계에 맞도록 구현해보도록 하자.
우선은 npm 을 통해 jwt 모듈을 설치한다.
npm i --save jsonwebtoken
타입스크립트의 경우 타입도 설치
pm i --save-dev @types/jsonwebtoken
jsonwebtoken - npm (npmjs.com)
jsonwebtoken
JSON Web Token implementation (symmetric and asymmetric). Latest version: 9.0.2, last published: 2 months ago. Start using jsonwebtoken in your project by running `npm i jsonwebtoken`. There are 25572 other projects in the npm registry using jsonwebtoken.
www.npmjs.com
주요로 사용하는 것은 두 가지 함수로, 간단하게 JWT을 생성, 검사가 가능하다.
함수 | 내용 |
jwt.sign() | jwt.sign(payload, secretOrPrivateKey, [options, callback]) payload : 내용 secretOrPrivateKey : 암호화 키 options에는 암호화 방식 등을 지정할 수 있다. |
jwt.verify() | jwt.verify(token, secretOrPublicKey, [options, callback]) token : 검증 할 JWT secretOrPrivateKey : 암호화 키 검증되지 않는 JWT의 경우 error를 발생 검증된 JWT의 경우 payload를 반환 받음 |
// tokenManager.ts
import jwt from 'jsonwebtoken';
const SECRET = process.env.JWT_SECRET_KEY || '';
const ACCESS_EXPIRES_IN = process.env.JWT_ACCESS_EXPIRES_IN || '';
const REFRESH_EXPIRES_IN = process.env.JWT_REFRESH_EXPIRES_IN || '';
// access Token 발급
const generateAccessToken = (userId: string) => {
return jwt.sign({ userId: userId }, SECRET, {
algorithm: 'HS256', // 암호화 알고리즘
expiresIn: ACCESS_EXPIRES_IN, // 유효기간
});
};
// access Token 검증
const verifyAccessToken = (token: string) => {
let decoded: any = null;
try {
decoded = jwt.verify(token, SECRET);
return {
ok: true,
userId: decoded.userId,
};
} catch (error: any) {
return {
ok: false,
message: error.message,
};
}
};
// refresh Token 발급
const generateRefreshToken = (userId: string) => {
return jwt.sign({ userId: userId }, SECRET, {
algorithm: 'HS256',
expiresIn: REFRESH_EXPIRES_IN, // 유효기간
});
};
// refresh Token 검증
const verifyRefreshToken = (token: string) => {
try {
jwt.verify(token, SECRET);
return true;
} catch (error) {
return false;
}
};
export {
generateAccessToken,
verifyAccessToken,
generateRefreshToken,
verifyRefreshToken,
};
주석을 통해 각 함수가 어떤 기능을 하는지 적어두었다.
간단하게 정리하자면
jwt.sign() 함수로 토큰을 발급한다.
algorithm값으로 으로 암호화 알고리즘을 'HS256'으로 적용하였으며, expiresIn으로 유효기간을 정해주었다.
또한 jwt.verify() 함수로는 토큰을 검증한다.
accessToken과 refreshToken이 검증할 수 있는 토큰인지 확인한다.
- 로그인 API
로그인 API에서는 토큰을 전송하는 부분만 확인해보도록 하겠다.
return compareResult
? NextResponse.json(
{
accessToken: generateAccessToken(userId),
},
{ status: 200 }
).cookies.set({
name: 'refreshToken',
value: generateRefreshToken(userId),
maxAge: REFRESH_EXPIRES_IN * 60 * 60 * 24, // 1일 기준으로 곱함, 문자 값이 들어가지 않도록 주의 !
path: '/',
httpOnly: true,
secure: PROTOCOL === 'https',
domain: DOMAIN,
})
: NextResponse.json(
{ message: '비밀번호가 일치하지 않습니다.' },
{ status: 500 }
);
성공적으로 로그인 정보를 확인했을때, accessToken과 refreshToken을 발급한다.
accessToken은 일반적인 body data로 전송하도록 하였다. 유효기간이 짧기 때문에 상대적으로 탈취에 덜 취약하기 때문.
refreshToken은 cookies의 취약점인 XSS공격과 CSRF공격에 대한 최소한의 방지를 해주기 위해 보안설정을 해주었다.
[프로젝트] Cookie의 옵션 (tistory.com)
[프로젝트] Cookie의 옵션
Cookie 쿠키는 브라우저에 데이터를 저장하기 위한 수단 중 하나이다. 서버가 요청에 대한 응답으로 Set-Cookie 헤더가 포함되어 있는 경우 브라우저는 이 헤더에 있는 데이터(쿠키)를 저장한다. HTTP
pomo0703.tistory.com
maxAge 옵션은 쿠키 값을 일정 상대 시간이 지나면 삭제되도록 하는 설정이다.
이미 JWT 설정에서 만료 시간이 있으나, 불필요하게 토큰을 남겨두지 않도록 설정해둔다.
httpOnly 옵션은 쿠키 값이 브라우저 상에서 열리지 않기 때문에 XSS를 방지할 수 있다.
단, http의 경우 통신 상에서 쿠키 정보가 암호화되지 않으므로 https 통신을 추천하며,
secure 옵션은 https 통신 상에서만 쿠키가 전송되도록 하는 설정이다.
https의 경우 통신 상에서도 쿠키 정보가 암호화되므로 통신이 탈취되더라도 더 안전하다.
개발 환경의 경우 http를 사용하기 때문에, 우선 프로토콜이 https인 경우에만 secure 옵션을 활성화 시켜주었다.
domain 의 경우 해당 도메인에만 쿠키가 전송되도록 하는 설정이다.
CSRF의 경우 유사 도메인의 주소로 사용자가 들어가 쿠키 정보를 탈취하는 경우가 있으므로
domain 설정을 통해 정확한 도메인의 경우에만 쿠키가 전송되도록 설정하여 최소한의 방편을 해두고자 하였다.
로그인이 성공한 경우, 사용자는 body data에서 accessToken을 얻어서 브라우저 상에 저장할 수 있고,
refreshToken은 http 통신 상에서 전달될 수 있도록 하였다.
'--- Project --- > CharFlyer : 캐플라이어' 카테고리의 다른 글
[개발] Next.js 13 JWT 재발급 (작성중) (0) | 2023.11.10 |
---|---|
11/4 - easyFetch 함수 추가 (0) | 2023.11.05 |
10/31 - Server Component / Client Component 분할 (0) | 2023.10.31 |
10/25 - Next.js 13 깔끔한 폴더 구조 (0) | 2023.10.25 |
10/24 - RESTful API 고민하기 (0) | 2023.10.24 |