Conversation
|
Caution Review failedThe pull request is closed. ℹ️ Recent review infoConfiguration used: Path: .coderabbit.yaml Review profile: CHILL Plan: Pro 📒 Files selected for processing (4)
WalkthroughOAuth2 인증 성공 흐름을 Redis 기반 일회용 코드로 전환하고, API 버전 마커( Changes
Sequence Diagram(s)sequenceDiagram
participant Client as Client
participant OAuth2 as OAuth2SuccessHandler
participant Redis as Redis
participant AuthCtrl as AuthCodeController
participant AuthSvc as AuthCodeService
Client->>OAuth2: OAuth 인증 완료(redirect)
OAuth2->>Redis: UUID 생성 및 "access::refresh" 저장<br/>TTL=30s
Redis-->>OAuth2: 저장 완료
OAuth2-->>Client: Deep link 리다이렉트 (code=UUID)
Client->>AuthCtrl: POST /api/v1/auth/token/exchange?code=UUID
AuthCtrl->>AuthSvc: exchangeCode(UUID)
AuthSvc->>Redis: UUID로 값 조회 및 삭제
Redis-->>AuthSvc: "access::refresh"
AuthSvc-->>AuthCtrl: TokenPair(access, refresh)
AuthCtrl-->>Client: ApiResponse(TokenPair)
Estimated code review effort🎯 4 (Complex) | ⏱️ ~50 minutes Possibly related PRs
Suggested reviewers
Poem
🚥 Pre-merge checks | ✅ 2 | ❌ 1❌ Failed checks (1 warning)
✅ Passed checks (2 passed)
✏️ Tip: You can configure your own custom pre-merge checks in the settings. ✨ Finishing Touches
🧪 Generate unit tests (beta)
Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out. Comment |
There was a problem hiding this comment.
Actionable comments posted: 4
Caution
Some comments are outside the diff and can’t be posted inline due to platform limitations.
⚠️ Outside diff range comments (1)
src/main/java/com/umc/linkyou/config/security/SecurityConfig.java (1)
42-56:⚠️ Potential issue | 🟠 Major
/api/v1/auth/**공개 경로 누락
Line 45의 permitAll 목록에/api/v1/auth/**가 빠져 있어/api/v1/auth/token/exchange가 인증 필요로 막힐 수 있습니다. 소셜 로그인 교환 흐름이 끊기니 공개 경로에 추가해주세요.🛠️ 제안 변경
- "/api/v1/users/**", + "/api/v1/users/**", + "/api/v1/auth/**",🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@src/main/java/com/umc/linkyou/config/security/SecurityConfig.java` around lines 42 - 56, The permitAll list in SecurityConfig's authorizeHttpRequests requestMatchers is missing the public auth endpoint; update the requestMatchers in SecurityConfig (where .authorizeHttpRequests(...).requestMatchers(...) is defined) to include "/api/v1/auth/**" alongside the other paths so endpoints like /api/v1/auth/token/exchange are publicly accessible during the social login exchange flow.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.
Inline comments:
In `@src/main/java/com/umc/linkyou/apiPayload/code/status/ErrorStatus.java`:
- Around line 46-52: The enum constant _INVALID_AUTH_CODE in ErrorStatus has a
message missing a sentence terminator; update the third parameter for
_INVALID_AUTH_CODE (in the ErrorStatus enum) from "유효하지 않거나 만료된 인증 코드" to "유효하지
않거나 만료된 인증 코드입니다." so it matches the style of other messages.
In `@src/main/java/com/umc/linkyou/oauth/OAuth2SuccessHandler.java`:
- Around line 119-120: The log currently writes the full deep-link URL
(redirectUrl) including the sensitive OAuth `code`; update OAuth2SuccessHandler
to sanitize the URL before logging by removing or masking the `code` query
parameter (e.g., parse redirectUrl, strip or replace the value of the "code"
param with a fixed mask like "[REDACTED]" into a sanitizedRedirectUrl) and use
sanitizedRedirectUrl in the log.info call instead of redirectUrl; ensure any
helper logic lives near the existing method handling redirect generation so
references like redirectUrl and the log.info call are updated accordingly.
In `@src/main/java/com/umc/linkyou/service/users/AuthCodeService.java`:
- Around line 28-29: The code in AuthCodeService currently splits the token
string into tokens and accesses tokens[1] without validating the format, which
can throw when the input is malformed; update the token-parsing logic (the place
that creates UserResponseDTO.TokenPair from String[] tokens) to check that token
is not null/empty and that tokens.length >= 2 after token.split("::"), and if
the check fails return or throw the INVALID_AUTH_CODE error path instead of
accessing tokens[1]; ensure the same validation is applied wherever the tokens
variable and UserResponseDTO.TokenPair(...) construction are used.
- Around line 18-27: The exchangeCode method currently uses non-atomic get +
delete on Redis and accesses token.split("::") without checking length; change
the Redis access in exchangeCode to use
stringRedisTemplate.opsForValue().getAndDelete(redisKey) so the read-and-delete
is atomic (preventing reuse/race conditions), and after retrieving token
validate it is non-null and that token.split("::") has the expected number of
segments before indexing into the array—if validation fails, delete/ensure key
removal as needed and throw the same
GeneralException(ErrorStatus._INVALID_AUTH_CODE) (or an appropriate error) to
avoid ArrayIndexOutOfBoundsException.
---
Outside diff comments:
In `@src/main/java/com/umc/linkyou/config/security/SecurityConfig.java`:
- Around line 42-56: The permitAll list in SecurityConfig's
authorizeHttpRequests requestMatchers is missing the public auth endpoint;
update the requestMatchers in SecurityConfig (where
.authorizeHttpRequests(...).requestMatchers(...) is defined) to include
"/api/v1/auth/**" alongside the other paths so endpoints like
/api/v1/auth/token/exchange are publicly accessible during the social login
exchange flow.
ℹ️ Review info
Configuration used: Path: .coderabbit.yaml
Review profile: CHILL
Plan: Pro
📒 Files selected for processing (9)
src/main/java/com/umc/linkyou/apiPayload/code/status/ErrorStatus.javasrc/main/java/com/umc/linkyou/config/WebConfig.javasrc/main/java/com/umc/linkyou/config/security/SecurityConfig.javasrc/main/java/com/umc/linkyou/oauth/OAuth2SuccessHandler.javasrc/main/java/com/umc/linkyou/service/users/AuthCodeService.javasrc/main/java/com/umc/linkyou/validation/annotation/ApiV1.javasrc/main/java/com/umc/linkyou/validation/annotation/ApiV2.javasrc/main/java/com/umc/linkyou/web/controller/user/AuthCodeController.javasrc/main/java/com/umc/linkyou/web/controller/user/UserController.java
| _AUTH_ACCOUNT_SAVE_FAILED(HttpStatus.INTERNAL_SERVER_ERROR, "OAUTH5001", "소셜 계정 연결에 실패했습니다."), | ||
| _USER_SOCIAL_CREATION_FAILED(HttpStatus.INTERNAL_SERVER_ERROR, "OAUTH5002", "소셜 사용자 생성에 실패했습니다."), | ||
| _SOCIAL_EMAIL_REQUIRED(HttpStatus.BAD_REQUEST, "OAUTH4003", "소셜 로그인에 이메일이 필요합니다."), | ||
| _SOCIAL_UNSUPPORTED_PROVIDER(HttpStatus.BAD_REQUEST, "OAUTH4004", "지원하지 않는 소셜 제공자입니다."), | ||
| _SOCIAL_PROFILE_EXPIRED(HttpStatus.BAD_REQUEST, "OAUTH4005", "임시 프로필이 만료되었습니다."), | ||
| _SOCIAL_PROFILE_NOT_REQUIRED(HttpStatus.BAD_REQUEST, "OAUTH4006", "임시 프로필이 필요하지 않습니다."), | ||
| _INVALID_AUTH_CODE(HttpStatus.BAD_REQUEST, "OAUTH4007", "유효하지 않거나 만료된 인증 코드"), |
There was a problem hiding this comment.
에러 메시지 문장 마무리 누락
_INVALID_AUTH_CODE 메시지에 종결 어미가 빠져 사용자 노출 문구 품질이 떨어집니다. 다른 메시지와 통일해 “입니다.”를 붙이는 게 좋겠습니다.
✏️ 수정 제안
- _INVALID_AUTH_CODE(HttpStatus.BAD_REQUEST, "OAUTH4007", "유효하지 않거나 만료된 인증 코드"),
+ _INVALID_AUTH_CODE(HttpStatus.BAD_REQUEST, "OAUTH4007", "유효하지 않거나 만료된 인증 코드입니다."),📝 Committable suggestion
‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.
| _AUTH_ACCOUNT_SAVE_FAILED(HttpStatus.INTERNAL_SERVER_ERROR, "OAUTH5001", "소셜 계정 연결에 실패했습니다."), | |
| _USER_SOCIAL_CREATION_FAILED(HttpStatus.INTERNAL_SERVER_ERROR, "OAUTH5002", "소셜 사용자 생성에 실패했습니다."), | |
| _SOCIAL_EMAIL_REQUIRED(HttpStatus.BAD_REQUEST, "OAUTH4003", "소셜 로그인에 이메일이 필요합니다."), | |
| _SOCIAL_UNSUPPORTED_PROVIDER(HttpStatus.BAD_REQUEST, "OAUTH4004", "지원하지 않는 소셜 제공자입니다."), | |
| _SOCIAL_PROFILE_EXPIRED(HttpStatus.BAD_REQUEST, "OAUTH4005", "임시 프로필이 만료되었습니다."), | |
| _SOCIAL_PROFILE_NOT_REQUIRED(HttpStatus.BAD_REQUEST, "OAUTH4006", "임시 프로필이 필요하지 않습니다."), | |
| _INVALID_AUTH_CODE(HttpStatus.BAD_REQUEST, "OAUTH4007", "유효하지 않거나 만료된 인증 코드"), | |
| _AUTH_ACCOUNT_SAVE_FAILED(HttpStatus.INTERNAL_SERVER_ERROR, "OAUTH5001", "소셜 계정 연결에 실패했습니다."), | |
| _USER_SOCIAL_CREATION_FAILED(HttpStatus.INTERNAL_SERVER_ERROR, "OAUTH5002", "소셜 사용자 생성에 실패했습니다."), | |
| _SOCIAL_EMAIL_REQUIRED(HttpStatus.BAD_REQUEST, "OAUTH4003", "소셜 로그인에 이메일이 필요합니다."), | |
| _SOCIAL_UNSUPPORTED_PROVIDER(HttpStatus.BAD_REQUEST, "OAUTH4004", "지원하지 않는 소셜 제공자입니다."), | |
| _SOCIAL_PROFILE_EXPIRED(HttpStatus.BAD_REQUEST, "OAUTH4005", "임시 프로필이 만료되었습니다."), | |
| _SOCIAL_PROFILE_NOT_REQUIRED(HttpStatus.BAD_REQUEST, "OAUTH4006", "임시 프로필이 필요하지 않습니다."), | |
| _INVALID_AUTH_CODE(HttpStatus.BAD_REQUEST, "OAUTH4007", "유효하지 않거나 만료된 인증 코드입니다."), |
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.
In `@src/main/java/com/umc/linkyou/apiPayload/code/status/ErrorStatus.java` around
lines 46 - 52, The enum constant _INVALID_AUTH_CODE in ErrorStatus has a message
missing a sentence terminator; update the third parameter for _INVALID_AUTH_CODE
(in the ErrorStatus enum) from "유효하지 않거나 만료된 인증 코드" to "유효하지 않거나 만료된 인증 코드입니다."
so it matches the style of other messages.
| log.info("OAuth2 딥링크 리다이렉트: {} (user={}, status={})", | ||
| redirectUrl, email, user.getStatus()); |
There was a problem hiding this comment.
딥링크 로그에 code 노출
Line 119-120에서 code가 포함된 URL이 그대로 로그에 남습니다. 로그 접근만으로 30초 내 교환이 가능하니 마스킹/제거 후 기록해주세요.
🛡️ 마스킹 예시
- log.info("OAuth2 딥링크 리다이렉트: {} (user={}, status={})",
- redirectUrl, email, user.getStatus());
+ String safeRedirectUrl = redirectUrl.replaceAll("code=[^&]+", "code=REDACTED");
+ log.info("OAuth2 딥링크 리다이렉트: {} (user={}, status={})",
+ safeRedirectUrl, email, user.getStatus());📝 Committable suggestion
‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.
| log.info("OAuth2 딥링크 리다이렉트: {} (user={}, status={})", | |
| redirectUrl, email, user.getStatus()); | |
| String safeRedirectUrl = redirectUrl.replaceAll("code=[^&]+", "code=REDACTED"); | |
| log.info("OAuth2 딥링크 리다이렉트: {} (user={}, status={})", | |
| safeRedirectUrl, email, user.getStatus()); |
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.
In `@src/main/java/com/umc/linkyou/oauth/OAuth2SuccessHandler.java` around lines
119 - 120, The log currently writes the full deep-link URL (redirectUrl)
including the sensitive OAuth `code`; update OAuth2SuccessHandler to sanitize
the URL before logging by removing or masking the `code` query parameter (e.g.,
parse redirectUrl, strip or replace the value of the "code" param with a fixed
mask like "[REDACTED]" into a sanitizedRedirectUrl) and use sanitizedRedirectUrl
in the log.info call instead of redirectUrl; ensure any helper logic lives near
the existing method handling redirect generation so references like redirectUrl
and the log.info call are updated accordingly.
| public UserResponseDTO.TokenPair exchangeCode(String code) { | ||
| String redisKey = AUTH_CODE_KEY + code; | ||
| String token = stringRedisTemplate.opsForValue().get(redisKey); | ||
|
|
||
| if (token == null) { | ||
| log.warn("AuthCodeService: 유효하지 않거나 만료된 code={}", code); | ||
| throw new GeneralException(ErrorStatus._INVALID_AUTH_CODE); | ||
| } | ||
|
|
||
| stringRedisTemplate.delete(redisKey); //1회용 즉시 삭제 |
There was a problem hiding this comment.
🧩 Analysis chain
🏁 Script executed:
find . -name "AuthCodeService.java" -type fRepository: LinkYou-2025/LinkU_backend
Length of output: 136
🏁 Script executed:
find . -name "pom.xml" -o -name "build.gradle" -o -name "build.gradle.kts" | head -5Repository: LinkYou-2025/LinkU_backend
Length of output: 84
🏁 Script executed:
find . -type f \( -name "pom.xml" -o -name "build.gradle" -o -name "build.gradle.kts" \) -exec grep -l "spring" {} \;Repository: LinkYou-2025/LinkU_backend
Length of output: 84
🏁 Script executed:
cat -n ./build.gradle | head -100Repository: LinkYou-2025/LinkU_backend
Length of output: 3755
🏁 Script executed:
cat -n ./src/main/java/com/umc/linkyou/service/users/AuthCodeService.javaRepository: LinkYou-2025/LinkU_backend
Length of output: 1436
🌐 Web query:
Spring Data Redis StringRedisTemplate getAndDelete method availability version
💡 Result:
getAndDelete is available in Spring Data Redis 2.6+ (it was introduced on ValueOperations/BoundValueOperations as getAndDelete(K key); StringRedisTemplate accesses it via stringRedisTemplate.opsForValue().getAndDelete(key)). [1]
This was added as part of Spring Data Redis 2.6’s support for the Redis GETDEL command. [2]
Sources:
[1] Spring Data Redis Javadoc (ValueOperations#getAndDelete), shows Since: 2.6. (docs.spring.io)
[2] Spring Data Redis 2.6 reference docs (“New in Spring Data Redis 2.6” includes GETDEL support). (docs.spring.io)
Citations:
- 1: https://docs.spring.io/spring-data-redis/reference/api/java/org/springframework/data/redis/core/ValueOperations.html?utm_source=openai
- 2: https://docs.spring.io/spring-data/redis/docs/2.6.6/reference/html/?utm_source=openai
🏁 Script executed:
# Check if there are any usages of getAndDelete in the codebase
rg "getAndDelete" --type javaRepository: LinkYou-2025/LinkU_backend
Length of output: 52
비원자적 get+delete로 인한 일회용 코드 재사용 가능성
Line 20-27의 get 후 delete 패턴은 원자적이지 않아 경합 조건 발생 시 여러 요청이 동일한 토큰을 획득할 수 있습니다. StringRedisTemplate.opsForValue().getAndDelete(key)로 변경하여 원자적 조회+삭제를 보장해주세요.
수정 예시
- String token = stringRedisTemplate.opsForValue().get(redisKey);
-
- if (token == null) {
- log.warn("AuthCodeService: 유효하지 않거나 만료된 code={}", code);
- throw new GeneralException(ErrorStatus._INVALID_AUTH_CODE);
- }
-
- stringRedisTemplate.delete(redisKey); //1회용 즉시 삭제
+ String token = stringRedisTemplate.opsForValue().getAndDelete(redisKey);
+
+ if (token == null) {
+ log.warn("AuthCodeService: 유효하지 않거나 만료된 code={}", code);
+ throw new GeneralException(ErrorStatus._INVALID_AUTH_CODE);
+ }추가로 Line 28-29에서 token.split("::") 결과의 길이 검증 없이 배열 접근 시 ArrayIndexOutOfBoundsException이 발생할 수 있습니다.
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.
In `@src/main/java/com/umc/linkyou/service/users/AuthCodeService.java` around
lines 18 - 27, The exchangeCode method currently uses non-atomic get + delete on
Redis and accesses token.split("::") without checking length; change the Redis
access in exchangeCode to use
stringRedisTemplate.opsForValue().getAndDelete(redisKey) so the read-and-delete
is atomic (preventing reuse/race conditions), and after retrieving token
validate it is non-null and that token.split("::") has the expected number of
segments before indexing into the array—if validation fails, delete/ensure key
removal as needed and throw the same
GeneralException(ErrorStatus._INVALID_AUTH_CODE) (or an appropriate error) to
avoid ArrayIndexOutOfBoundsException.
| String[] tokens = token.split("::"); | ||
| return new UserResponseDTO.TokenPair(tokens[0], tokens[1]); |
There was a problem hiding this comment.
토큰 포맷 검증 없이 split 인덱스 접근
Line 28-29에서 값이 깨지면 tokens[1] 접근 시 500이 납니다. 길이 검증 후 INVALID_AUTH_CODE로 처리하는 게 안전합니다.
🛡️ 포맷 검증 추가 예시
- String[] tokens = token.split("::");
- return new UserResponseDTO.TokenPair(tokens[0], tokens[1]);
+ String[] tokens = token.split("::", -1);
+ if (tokens.length != 2) {
+ log.warn("AuthCodeService: invalid token format for code={}", code);
+ throw new GeneralException(ErrorStatus._INVALID_AUTH_CODE);
+ }
+ return new UserResponseDTO.TokenPair(tokens[0], tokens[1]);🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.
In `@src/main/java/com/umc/linkyou/service/users/AuthCodeService.java` around
lines 28 - 29, The code in AuthCodeService currently splits the token string
into tokens and accesses tokens[1] without validating the format, which can
throw when the input is malformed; update the token-parsing logic (the place
that creates UserResponseDTO.TokenPair from String[] tokens) to check that token
is not null/empty and that tokens.length >= 2 after token.split("::"), and if
the check fails return or throw the INVALID_AUTH_CODE error path instead of
accessing tokens[1]; ensure the same validation is applied wherever the tokens
variable and UserResponseDTO.TokenPair(...) construction are used.
There was a problem hiding this comment.
Actionable comments posted: 1
Caution
Some comments are outside the diff and can’t be posted inline due to platform limitations.
⚠️ Outside diff range comments (1)
src/main/java/com/umc/linkyou/config/security/SecurityConfig.java (1)
43-57:⚠️ Potential issue | 🔴 Critical
/api/v1/auth/**경로가permitAll에 누락되어 토큰 교환 엔드포인트가 동작하지 않습니다.AuthCodeController는
@ApiV1어노테이션으로 인해 WebConfig에서/api/v1경로 프리픽스가 추가되므로, 실제 엔드포인트는/api/v1/auth/token/exchange입니다. 그러나 현재 SecurityConfig의permitAll목록에/api/v1/auth/**가 없으므로.anyRequest().authenticated()규칙에 의해 인증이 필요합니다.OAuth2 인증 코드를 토큰으로 교환하는 시점에는 클라이언트가 아직 토큰을 보유하지 않으므로, 이 엔드포인트는 반드시 인증 없이 접근 가능해야 합니다. 현재 구성에서는 토큰 교환이 401로 차단되므로 OAuth2 흐름이 완성되지 않습니다.
🔧 수정 제안
.requestMatchers( "/", "/css/**", "/api/v1/users/**", + "/api/v1/auth/**", "/swagger-ui/**", "/v3/api-docs/**",🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@src/main/java/com/umc/linkyou/config/security/SecurityConfig.java` around lines 43 - 57, The SecurityConfig authorizeHttpRequests permit list is missing the OAuth token-exchange path, causing AuthCodeController endpoints (prefixed by /api/v1 via WebConfig) like /api/v1/auth/** to be blocked; update the .requestMatchers(...) in SecurityConfig (where authorizeHttpRequests(...) and .permitAll() are configured) to include "/api/v1/auth/**" so the token exchange endpoint is accessible without authentication.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.
Inline comments:
In `@src/main/java/com/umc/linkyou/config/security/SecurityConfig.java`:
- Around line 63-73: SecurityConfig currently sets
SessionCreationPolicy.STATELESS while still using
HttpSessionOAuth2AuthorizationRequestRepository in
oauth2Login.authorizationRequestRepository, which breaks OAuth2 state in
multi-instance deployments; replace the session-backed repository with a
stateless one (e.g., implement and register a Redis-backed
AuthorizationRequestRepository or use a cookie-based repository) and wire it
into oauth2Login.authorizationRequestRepository instead of new
HttpSessionOAuth2AuthorizationRequestRepository(); ensure the new repository
(e.g., RedisAuthorizationRequestRepository or
HttpCookieOAuth2AuthorizationRequestRepository) is a Spring bean and uses your
existing RedisTemplate/serializer or cookie utilities so OAuth2 state persists
across instances without creating server sessions.
---
Outside diff comments:
In `@src/main/java/com/umc/linkyou/config/security/SecurityConfig.java`:
- Around line 43-57: The SecurityConfig authorizeHttpRequests permit list is
missing the OAuth token-exchange path, causing AuthCodeController endpoints
(prefixed by /api/v1 via WebConfig) like /api/v1/auth/** to be blocked; update
the .requestMatchers(...) in SecurityConfig (where authorizeHttpRequests(...)
and .permitAll() are configured) to include "/api/v1/auth/**" so the token
exchange endpoint is accessible without authentication.
🔗 관련 이슈
#240 #208
📌 작업 내용
1️⃣ Non-Functional Requirement
@ApiV1/@ApiV2커스텀 어노테이션 기반 API 버전 관리 체계 도입WebConfig.addPathPrefix()로 컨트롤러 클래스 단위 버전 prefix 자동 적용validation/annotation/패키지에ApiV1,ApiV2어노테이션 추가web/controller/user/하위 패키지로 컨트롤러 구조 정리ErrorStatus소셜 로그인 관련 에러 코드 prefixUSERS→OAUTH로 정리2️⃣ Functional Requirement
?accessToken=eyJ...&refreshToken=eyJ...URL에 직접 노출?code=a1b2c3d4...30초 TTL 1회용 코드만 URL에 노출POST /api/v1/auth/token/exchange?code=API로 실제 토큰 교환OAuth2SuccessHandler— Redis에auth:code:{uuid}키로 토큰 임시 저장 (TTL 30초)AuthCodeService신규 추가 — 코드 검증, 1회용 삭제, 토큰 반환 로직AuthCodeController신규 추가 —POST /api/v1/auth/token/exchangehmac()UTF-8 charset 고정 —token.getBytes()→token.getBytes(StandardCharsets.UTF_8)SecurityConfigpermitAll 경로/api/users/**→/api/v1/users/**,/api/v1/auth/**추가UserController@RequestMapping("api/users")→@RequestMapping("/users")+@ApiV1적용🧪 테스트 결과
POST /api/v1/auth/token/exchange?code={code}— 정상 코드 →accessToken+refreshToken반환 확인OAUTH4007에러 반환 확인OAUTH4007에러 반환 확인code만 포함되는지 확인POST /api/v1/users/login,GET /api/v1/users/me등 기존 API 정상 동작 확인📸 스크린샷 (선택)
📎 참고 사항
AUTH_CODE_TTL_SECONDS = 30L현재 하드코딩 → 추후application.properties의auth.code.ttl-seconds로 이전해야 할까요?App Links(android:autoVerify="true") 설정 별도 협의 필요Summary by CodeRabbit
새로운 기능
개선 사항