Skip to content

Commit

Permalink
fix: access_token, refresh_token 만료 시 오류 응답 값이 다르게 나오도록 변경 (#478)
Browse files Browse the repository at this point in the history
* test: refreshSecret 키 불일치 수정

* fix: accessToken 만료 시 401 코드를 응답하도록 수정

* chore: 불필요한 파일 삭제

* fix: POSIX 오류 적용
  • Loading branch information
fromitive authored and ChooSeoyeon committed Oct 11, 2024
1 parent 37fcc2b commit 81cc985
Show file tree
Hide file tree
Showing 4 changed files with 71 additions and 17 deletions.
3 changes: 3 additions & 0 deletions backend/.gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -41,3 +41,6 @@ out/

### RestDocs ###
openapi3.yaml

### Intellij IDEA ###
.idea
Original file line number Diff line number Diff line change
Expand Up @@ -11,13 +11,14 @@
public enum AuthErrorCode implements ErrorResponse {

INVALID_TOKEN(HttpStatus.UNAUTHORIZED, "유효하지 않은 토큰입니다."),
EXPIRED_TOKEN(HttpStatus.FORBIDDEN, "만료된 토큰입니다."),
EXPIRED_REFRESH_TOKEN(HttpStatus.FORBIDDEN, "만료된 토큰입니다."),
INVALID_COOKIE(HttpStatus.UNAUTHORIZED, "유효하지 않은 쿠키입니다."),
COOKIE_NOT_EXIST(HttpStatus.UNAUTHORIZED, "쿠키가 존재하지 않습니다."),
INVALID_PASSWORD(HttpStatus.NOT_FOUND, "가입하지 않은 회원입니다."),
DUPLICATED_MEMBER(HttpStatus.CONFLICT, "이미 가입한 회원입니다."),
CLIENT_TIME_OUT(HttpStatus.INTERNAL_SERVER_ERROR, "시간이 초과되어 로그인 요청에 실패했습니다."),
KAKAO_LOGIN_INTERNAL_SERVER_ERROR(HttpStatus.INTERNAL_SERVER_ERROR, "카카오 로그인에 실패했습니다.");
KAKAO_LOGIN_INTERNAL_SERVER_ERROR(HttpStatus.INTERNAL_SERVER_ERROR, "카카오 로그인에 실패했습니다."),
EXPIRED_ACCESS_TOKEN(HttpStatus.UNAUTHORIZED, "만료된 토큰입니다.");

private final HttpStatus status;
private final String message;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -55,28 +55,42 @@ private Date calculateExpiredAt(Duration expired) {
}

public void validateAccessToken(String token) {
getClaims(token, accessSecretKey).getSubject();
getClaimsAccessToken(token, accessSecretKey).getSubject();
}

public Long getMemberIdByAccessToken(String token) {
String memberId = getClaims(token, accessSecretKey).getSubject();
String memberId = getClaimsAccessToken(token, accessSecretKey).getSubject();
return Long.valueOf(memberId);
}

private Claims getClaimsAccessToken(String token, String accessSecretKey) {
try {
return getClaims(token, accessSecretKey);
} catch (ExpiredJwtException e) {
throw new MarketException(AuthErrorCode.EXPIRED_ACCESS_TOKEN);
} catch (JwtException | IllegalArgumentException e) {
throw new MarketException(AuthErrorCode.INVALID_TOKEN);
}
}

private Claims getClaims(String token, String key) {
return Jwts.parser()
.setSigningKey(key)
.setClock(() -> Date.from(clock.instant()))
.parseClaimsJws(token)
.getBody();
}

public Long getMemberIdByRefreshToken(String token) {
String memberId = getClaims(token, refreshSecretKey).getSubject();
String memberId = getClaimsRefreshToken(token, refreshSecretKey).getSubject();
return Long.valueOf(memberId);
}

private Claims getClaims(String token, String key) {
private Claims getClaimsRefreshToken(String token, String refreshSecretKey) {
try {
return Jwts.parser()
.setSigningKey(key)
.setClock(() -> Date.from(clock.instant()))
.parseClaimsJws(token)
.getBody();
return getClaims(token, refreshSecretKey);
} catch (ExpiredJwtException e) {
throw new MarketException(AuthErrorCode.EXPIRED_TOKEN);
throw new MarketException(AuthErrorCode.EXPIRED_REFRESH_TOKEN);
} catch (JwtException | IllegalArgumentException e) {
throw new MarketException(AuthErrorCode.INVALID_TOKEN);
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,7 @@
import io.jsonwebtoken.SignatureAlgorithm;
import io.restassured.http.ContentType;
import java.time.Duration;
import java.util.Base64;
import java.util.Date;
import java.util.List;
import org.junit.jupiter.api.BeforeEach;
Expand Down Expand Up @@ -88,9 +89,9 @@ void should_loginSuccess_when_givenMemberCI() {
}
}

@DisplayName("토큰 재발급")
@DisplayName("토큰 관리")
@Nested
class Refresh {
class ManageToken {

List<HeaderDescriptorWithType> responseHeaderDescriptors = List.of(
headerWithName("Set-Cookie").description("""
Expand All @@ -111,9 +112,15 @@ class Refresh {
@Value("${security.jwt.token.refresh-secret-key}")
String refreshSecretKey;

@Value("${security.jwt.token.access-secret-key}")
String accessSecretKey;

@Value("${security.jwt.token.refresh-token-expired}")
Duration refreshTokenExpired;

@Value("${security.jwt.token.access-token-expired}")
Duration accessTokenExpired;

MemberEntity member;
Date now;

Expand All @@ -123,6 +130,35 @@ void setUp() {
now = Date.from(clock.instant());
}

@DisplayName("만료된 accessToken 경우 예외 발생 후 401 코드를 반환한다.")
@Test
void should_throwException_when_givenExpiredAccessToken() {
Date alreadyExpiredAt = new Date(now.getTime() - accessTokenExpired.toMillis());
String expiredToken = Jwts.builder()
.setSubject(member.getId().toString())
.setExpiration(alreadyExpiredAt)
.signWith(SignatureAlgorithm.HS256, Base64.getEncoder().encodeToString(accessSecretKey.getBytes()))
.compact();

given(spec).log().all()
.filter(document("access-fail-expired-token", resource(failedSnippets)))
.cookie("access_token", expiredToken)
.when().get("/offerings")
.then().log().all()
.statusCode(401);
}

@DisplayName("유효하지 않은 accessToken인 경우 예외가 발생한다.")
@Test
void should_throwException_when_givenInvalidAccessToken() {
given(spec).log().all()
.filter(document("refresh-fail-invalid-token", resource(failedSnippets)))
.cookie("access_token", "invalidRefreshToken")
.when().post("/offerings")
.then().log().all()
.statusCode(401);
}

@DisplayName("refreshToken으로 accessToken과 refreshToken을 재발급 한다.")
@Test
void should_refreshSuccess_when_givenRefreshToken() {
Expand All @@ -147,22 +183,22 @@ void should_throwException_when_givenInvalidRefreshToken() {
.statusCode(401);
}

@DisplayName("만료된 refeshToken인 경우 예외가 발생한다.")
@DisplayName("만료된 refeshToken인 경우 예외 발생 후 403 코드를 반환한다.")
@Test
void should_throwException_when_givenExpiredRefreshToken() {
Date alreadyExpiredAt = new Date(now.getTime() - refreshTokenExpired.toMillis());
String expiredToken = Jwts.builder()
.setSubject(member.getId().toString())
.setExpiration(alreadyExpiredAt)
.signWith(SignatureAlgorithm.HS256, refreshSecretKey)
.signWith(SignatureAlgorithm.HS256, Base64.getEncoder().encodeToString(refreshSecretKey.getBytes()))
.compact();

given(spec).log().all()
.filter(document("refresh-fail-expired-token", resource(failedSnippets)))
.cookie("refresh_token", expiredToken)
.when().post("/auth/refresh")
.then().log().all()
.statusCode(401);
.statusCode(403);
}
}
}

0 comments on commit 81cc985

Please sign in to comment.