JWT (JSON Web Tokens)는 인터넷 표준인 RFC 7519에 정의된, 두 개체 사이에서 JSON 객체를 사용하여 안전하게 정보를 교환하기 위한 컴팩트하고 자가 수용적인(self-contained) 방식입니다. JWT는 클라이언트와 서버 사이의 인증 및 정보 교환에 널리 사용되며, 특히 웹 애플리케이션과 모바일 애플리케이션에서 RESTful API의 보안을 관리하는 데 유용합니다.
JWT Token는 3가지로 구분됩니다.
- 헤더(Header): 토큰의 타입(보통 JWT)과 사용된 해시 알고리즘(예: HMAC SHA256 또는 RSA)을 지정하는 JSON 객체입니다.
- 페이로드(Payload): 토큰에 포함할 클레임(claims)을 담는 JSON 객체입니다. 클레임은 토큰에 대한 정보를 제공하는 속성이며, 세 가지 유형(등록된, 공개, 비공개 클레임)이 있습니다. 등록된 클레임은 서비스에서 필요로 하는 정보(예: 사용자 식별 정보, 발급자, 만료 시간 등)를 제공하고, 공개 및 비공개 클레임은 사용자 정의 정보를 포함할 수 있습니다.
-
- 등록된(Registered) Claims: JWT 표준에서 사전 정의한 일련의 클레임들로, 선택적으로 사용할 수 있습니다. 이들은 토큰에 대한 일반적인 정보를 제공하며, 모든 등록된 클레임들은 이름이 세 글자로 된 짧은 형태입니다. 예를 들어:
- iss : 토큰을 발급한 발급자.
- sub (Subject): 토큰의 주제(또는 사용자).
- aud (Audience): 토큰의 수신자.
- exp (Expiration Time): 토큰의 만료 시간.
- nbf (Not Before): 토큰이 활성화되기 전의 시간.
- iat (Issued At): 토큰이 발급된 시간.
- jti (JWT ID): 토큰의 고유 식별자.
- 공개(Public) Claims: 이 클레임들은 충돌을 방지하기 위해 IANA JSON Web Token Registry에 등록되거나, URI 형식을 사용하여 정의될 수 있습니다. 공개 클레임들은 사용자 정의 정보를 포함할 수 있으며, 공개적으로 사용되는 클레임 이름을 중복 사용하지 않기 위한 목적으로 등록되거나 정의됩니다.
- 비공개(Private) Claims: 두 개체 사이의 합의 하에 사용되는 클레임들로, 사용자 정의 데이터를 전달하는 데 사용됩니다. 이 클레임들은 특정 애플리케이션 또는 조직 내에서만 의미가 있으며, 고유한 이름을 사용하여 충돌이 발생하지 않도록 해야 합니다. 사용자의 ID, 역할(role) 또는 이메일과 같은 정보를 비공개 클레임으로 토큰에 포함시켜, API 요청 시 해당 정보를 서버에서 쉽게 추출하고 검증할 수 있습니다.
- 등록된(Registered) Claims: JWT 표준에서 사전 정의한 일련의 클레임들로, 선택적으로 사용할 수 있습니다. 이들은 토큰에 대한 일반적인 정보를 제공하며, 모든 등록된 클레임들은 이름이 세 글자로 된 짧은 형태입니다. 예를 들어:
- 서명(Signature): 헤더의 인코딩된 값과 페이로드의 인코딩된 값을 합친 후, 제공된 비밀키로 해시하여 생성됩니다. 서명은 메시지의 무결성과 정보의 검증을 보장하기 위해 사용됩니다.
이 세 부분은 각각 Base64URL로 인코딩되어 .으로 구분되어 연결된 문자열 형태로 제공됩니다. 예를 들어, JWT는 다음과 같이 보일 수 있습니다:
localStorage에 저장되어있는 accessToken을 사용해 정보를 수집하며, accessToken이 만료되었을 때 refreshToken으로 accessToken을 생성해 사용자가 번거롭게 로그인을 하지 않도록 합니다.
구현.
- 로그인하여 토큰을 발급받는 경우
const accessToken = await JWT.sign(
{ email },
process.env.ACCESS_TOKEN_SECRET,
{
expiresIn: “10s”,
}
);
// Refresh token
const refreshToken = await JWT.sign(
{ email },
process.env.REFRESH_TOKEN_SECRET,
{
expiresIn: “1m”,
}
);
// Set refersh token in refreshTokens array
refreshTokens.push(refreshToken); //db에 refreshToken을 저장함.
res.json({
accessToken,
refreshToken,
});
- Client: accessToken과 refreshToken을 localstorage에 저장함.
- 회원 인증이 필요한 경우 route에 미들웨어로 auth함수를 추가함.
router.get(“/private”, authToken, (req, res) => {
res.json(privatePosts);
});
- authToken: req에 user의 email을 얹어주고 다음으로 넘어갈 수 있게 해줌.
const token = req.header(“x-auth-token”);
try {
const user = await jwt.verify(token, process.env.ACCESS_TOKEN_SECRET);
req.user = user.email;
next();
}
5. accessToken만료 시 refreshToken으로 accessToken생성(refreshToken은 DB에 저장되어있어야함)
const refreshToken = req.header(“x-auth-token”);
try {
const user = await JWT.verify(
refreshToken,
process.env.REFRESH_TOKEN_SECRET
);
const { email } = user;
const accessToken = await JWT.sign(
{ email },
process.env.ACCESS_TOKEN_SECRET,
{ expiresIn: “10s” }
);
res.json({ accessToken });
}