Conversation
|
Important Review skippedAuto incremental reviews are disabled on this repository. Please check the settings in the CodeRabbit UI or the You can disable this status message by setting the Walkthrough이 변경사항은 자동 토큰 갱신 기능을 구현합니다. JWT 유틸리티 모듈을 추가하여 토큰 디코딩 및 만료 여부 판단을 수행하고, Axios 요청 인터셉터를 수정하여 만료된 토큰을 자동으로 갱신합니다. 인증 서비스 및 API에 갱신 기능을 추가하고, 인증 저장소를 리팩토링하여 액세스 토큰, 새로고침 토큰, 만료 시간을 별도로 관리합니다. Firebase 설정에서는 탭 간 이벤트 중복 제거 기능을 추가합니다. Sequence Diagram(s)sequenceDiagram
participant Client
participant Interceptor
participant AuthService
participant Backend
participant AuthStore
Client->>Interceptor: 요청 발송
Interceptor->>Interceptor: 토큰 만료 여부 확인
alt 토큰이 만료됨
Interceptor->>Interceptor: 갱신 중인가?
alt 갱신이 진행 중
Interceptor->>Interceptor: 요청을 대기열에 추가
else 갱신이 필요함
Interceptor->>Interceptor: isRefreshing = true
Interceptor->>AuthService: refresh() 호출
AuthService->>Backend: POST /v1/auth/refresh
Backend-->>AuthService: 새 토큰 반환
AuthService->>AuthStore: accessToken, refreshToken 업데이트
Interceptor->>Interceptor: 대기 중인 요청 모두 처리
Interceptor->>Interceptor: isRefreshing = false
end
Interceptor->>Interceptor: 새 토큰으로 Authorization 헤더 설정
Interceptor->>Backend: 요청 재시도
else 토큰이 유효함
Interceptor->>Interceptor: Authorization 헤더 설정
Interceptor->>Backend: 요청 발송
end
Backend-->>Client: 응답
Estimated code review effort🎯 4 (Complex) | ⏱️ ~45-60 minutes 주의 깊게 검토할 부분:
Pre-merge checks and finishing touches❌ Failed checks (2 warnings, 1 inconclusive)
✅ Passed checks (2 passed)
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: 13
Caution
Some comments are outside the diff and can’t be posted inline due to platform limitations.
⚠️ Outside diff range comments (1)
front/src/shared/firebase/config.js (1)
85-86:postMessage실패에 대한 에러 처리를 추가하는 것을 고려하세요.
BroadcastChannel또는 서비스 워커로의 메시지 전송이 실패할 경우 에러가 발생할 수 있습니다. 현재는 조용히 실패하는데, 최소한 콘솔 로그를 추가하거나 에러를 처리하는 것이 디버깅에 도움이 됩니다.- if (bc) bc.postMessage({ id, ts: now }) - else navigator.serviceWorker?.controller?.postMessage({ id, ts: now }) + try { + if (bc) bc.postMessage({ id, ts: now }) + else navigator.serviceWorker?.controller?.postMessage({ id, ts: now }) + } catch (err) { + console.warn('Failed to broadcast event deduplication:', err) + }
📜 Review details
Configuration used: CodeRabbit UI
Review profile: ASSERTIVE
Plan: Pro
📒 Files selected for processing (7)
front/src/api/axios.js(2 hunks)front/src/features/auth/api.js(1 hunks)front/src/features/auth/authService.js(3 hunks)front/src/features/auth/dto.js(1 hunks)front/src/shared/firebase/config.js(1 hunks)front/src/shared/utils/jwtUtils.js(1 hunks)front/src/stores/useAuthStore.js(1 hunks)
🧰 Additional context used
🧠 Learnings (1)
📓 Common learnings
Learnt from: letsgilit
PR: HI-dle/rouby#230
File: back/src/main/java/com/rouby/batch/config/BatchConfig.java:68-73
Timestamp: 2025-09-09T11:11:40.513Z
Learning: letsgilit prefers simpler configurations over complex ones when the current solution is sufficient for the use case.
🧬 Code graph analysis (5)
front/src/features/auth/api.js (1)
front/src/features/auth/authService.js (2)
refresh(87-101)refresh(87-101)
front/src/api/axios.js (4)
front/src/features/auth/authService.js (2)
refresh(87-101)refresh(87-101)front/src/features/auth/api.js (2)
refresh(47-49)refresh(47-49)front/src/shared/utils/jwtUtils.js (3)
decoded(8-8)decoded(17-17)decodeJwt(1-14)front/src/stores/useAuthStore.js (2)
useAuthStore(6-68)useAuthStore(6-68)
front/src/shared/utils/jwtUtils.js (1)
front/src/api/axios.js (2)
token(61-61)decoded(31-31)
front/src/stores/useAuthStore.js (4)
front/src/features/auth/useLoginForm.js (1)
staySignedIn(11-11)front/src/stores/useScheduleStore.js (1)
reset(94-97)front/src/stores/useDatePickStore.js (1)
reset(41-45)front/src/stores/useUserInfoStore.js (1)
reset(81-93)
front/src/features/auth/authService.js (4)
front/src/features/auth/api.js (2)
refresh(47-49)refresh(47-49)front/src/shared/utils/errorUtils.js (1)
wrapApi(37-45)front/src/features/auth/dto.js (1)
toRefreshPayload(36-40)front/src/shared/utils/jwtUtils.js (1)
getTokenExpiration(16-19)
🔇 Additional comments (4)
front/src/shared/firebase/config.js (4)
63-68: 이벤트 중복 제거 설정이 적절합니다.
MAX_SEEN과TRIM_COUNT값이 메모리 관리에 적절하며,BroadcastChannel의 기능 감지 처리도 잘 되어 있습니다.
71-88: 크로스 탭 중복 제거 로직에서 경쟁 상태 가능성을 확인하세요.두 개의 탭이 거의 동시에 같은 이벤트를 수신하면, 둘 다
seen.has(id)체크를 통과한 후 브로드캐스트를 보내기 전에 이벤트를 처리할 수 있습니다. 이는 짧은 시간 동안 중복 알림이 표시될 수 있는 TOCTOU 경쟁 상태입니다.알림 시스템의 특성상 이는 허용 가능한 수준이지만, 실제 환경에서 중복 알림이 발생하는지 확인해 보시기 바랍니다.
91-99: 탭 간 이벤트 전파 처리가 잘 구현되어 있습니다.
resolvePropagatedEvent는 타임스탬프 비교를 통해 최신 정보를 유지하고,BroadcastChannel과 서비스 워커 모두에 리스너를 등록하여 적절한 폴백을 제공합니다.
108-110: 중복 제거 통합이 깔끔하게 구현되었습니다.
markAndCheckSeen함수를 사용한 조기 반환 패턴이 명확하며, 알림 표시 로직과 잘 통합되어 있습니다.
| function onRefreshed(newAccessToken) { | ||
| refreshSubscribers.forEach((cb) => cb(newAccessToken)) | ||
| refreshSubscribers = [] | ||
| } | ||
|
|
||
| function addRefreshSubscriber(callback) { | ||
| refreshSubscribers.push(callback) | ||
| } |
There was a problem hiding this comment.
refresh 실패 시 대기중인 요청이 영원히 대기 상태로 남음
실패 시 onRefreshed가 호출되지 않아 구독자 Promise가 resolve/reject되지 않습니다. 메모리 누수/무한 대기 위험.
실패 브로드캐스트를 추가하고 구독자 구조를 resolve/reject로 변경하세요:
-let refreshSubscribers = []
+let refreshSubscribers = []
-function onRefreshed(newAccessToken) {
- refreshSubscribers.forEach((cb) => cb(newAccessToken))
- refreshSubscribers = []
-}
+function onRefreshed(newAccessToken) {
+ refreshSubscribers.forEach(({ resolve }) => resolve(newAccessToken))
+ refreshSubscribers = []
+}
+
+function onRefreshFailed(error) {
+ refreshSubscribers.forEach(({ reject }) => reject(error))
+ refreshSubscribers = []
+}
-function addRefreshSubscriber(callback) {
- refreshSubscribers.push(callback)
-}
+function addRefreshSubscriber(handlers /* { resolve, reject } */) {
+ refreshSubscribers.push(handlers)
+}
async function refreshAccessToken(authStore) {
try {
const { accessToken: newAccessToken, refreshToken: newRefreshToken } = await refresh({
refreshToken: authStore.getRefreshToken(),
})
@@
onRefreshed(newAccessToken)
return newAccessToken
} catch (error) {
console.error('Refresh 실패:', error)
+ onRefreshFailed(error)
authStore.reset()
window.location.href = '/login'
throw error
} finally {
isRefreshing = false
}
}Also applies to: 26-52
🤖 Prompt for AI Agents
In front/src/api/axios.js around lines 17-24 (and also apply same change to
lines 26-52), the current refresh subscriber pattern only supports successful
broadcasts so when token refresh fails waiting requests remain pending; change
the subscriber model to register objects with both resolve and reject (e.g.,
push {resolve, reject}) and make addRefreshSubscriber return a Promise that
stores its resolve/reject in that list; implement a new broadcaster function
(e.g., onRefreshFailed) that iterates subscribers calling reject(error) and then
clears the list, and update onRefreshed to call each
subscriber.resolve(newAccessToken) and then clear the list; ensure all branches
(success and failure) always clear subscribers to avoid memory leaks.
| const decoded = decodeJwt(newAccessToken) | ||
| const expiredAt = decoded?.exp ? decoded.exp * 1000 : null | ||
|
|
||
| // 새 토큰 및 만료 시간 저장 | ||
| authStore.setAccessToken(newAccessToken) | ||
| authStore.setAccessTokenExpirationTime(expiredAt) | ||
| if (newRefreshToken) { |
There was a problem hiding this comment.
🧹 Nitpick | 🔵 Trivial
만료시각 계산 유틸 통일 제안
여기서는 decodeJwt로 직접 계산, 다른 곳(authService)은 getTokenExpiration을 사용. 한 유틸로 통일하면 버그 표면적이 줄어듭니다.
- const decoded = decodeJwt(newAccessToken)
- const expiredAt = decoded?.exp ? decoded.exp * 1000 : null
+ const { getTokenExpiration } = await import('@/shared/utils/jwtUtils.js')
+ const expiredAt = getTokenExpiration(newAccessToken)📝 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.
| const decoded = decodeJwt(newAccessToken) | |
| const expiredAt = decoded?.exp ? decoded.exp * 1000 : null | |
| // 새 토큰 및 만료 시간 저장 | |
| authStore.setAccessToken(newAccessToken) | |
| authStore.setAccessTokenExpirationTime(expiredAt) | |
| if (newRefreshToken) { | |
| const { getTokenExpiration } = await import('@/shared/utils/jwtUtils.js') | |
| const expiredAt = getTokenExpiration(newAccessToken) | |
| // 새 토큰 및 만료 시간 저장 | |
| authStore.setAccessToken(newAccessToken) | |
| authStore.setAccessTokenExpirationTime(expiredAt) | |
| if (newRefreshToken) { |
🤖 Prompt for AI Agents
In front/src/api/axios.js around lines 31 to 37, the code computes token expiry
by calling decodeJwt and multiplying exp by 1000, while other modules use
getTokenExpiration; replace the direct decodeJwt usage with the shared
getTokenExpiration utility: import getTokenExpiration from the shared utils,
call expiredAt = getTokenExpiration(newAccessToken) (ensuring it returns null
when unavailable), then continue storing
authStore.setAccessTokenExpirationTime(expiredAt); keep existing handling for
newRefreshToken unchanged.
| console.error('Refresh 실패:', error) | ||
| authStore.reset() | ||
| window.location.href = '/login' |
There was a problem hiding this comment.
🧩 Analysis chain
authStore.reset()가 refreshToken/만료시각을 지우지 않을 가능성
제공된 스토어 스니펫에 따르면 reset이 accessToken만 초기화합니다. 실패 후 refreshToken이 남으면 보안/정합성 문제입니다.
스토어의 reset에서 refreshToken과 accessTokenExpirationTime도 함께 초기화해 주세요. 아래 스크립트로 실제 구현을 확인해 주실 수 있나요?
🏁 Script executed:
#!/bin/bash
# useAuthStore.reset이 어떤 필드를 초기화하는지 확인
set -euo pipefail
# 대상 파일 탐색
fd -t f useAuthStore.js
echo "---- reset 구현 ----"
rg -n -C3 'reset\s*=\s*\(\)\s*=>' $(fd -t f useAuthStore.js)
echo "---- refreshToken 초기화 위치 ----"
rg -n -C1 'refreshToken\.value\s*=' $(fd -t f useAuthStore.js)
echo "---- accessTokenExpirationTime 초기화 위치 ----"
rg -n -C1 'accessTokenExpirationTime\.value\s*=' $(fd -t f useAuthStore.js)Length of output: 495
useAuthStore.reset()에서 refreshToken과 accessTokenExpirationTime 초기화 필요
검증 결과, reset() 함수(line 42-45)는 staySignedIn과 accessToken만 초기화하고 있습니다. refreshToken과 accessTokenExpirationTime은 초기화되지 않고 있습니다.
토큰 갱신 실패 후 logout 흐름에서 이 필드들이 남아있으면 보안 및 데이터 정합성 문제로 이어질 수 있습니다.
수정 사항:
front/src/stores/useAuthStore.js의 reset() 함수에서 다음 필드도 함께 초기화하세요:
refreshToken.value = ''accessTokenExpirationTime.value = 0(또는 적절한 초기값)
🤖 Prompt for AI Agents
In front/src/stores/useAuthStore.js around lines 42-45, the reset() function
only clears staySignedIn and accessToken but leaves refreshToken and
accessTokenExpirationTime populated; update reset() to also set
refreshToken.value = '' and accessTokenExpirationTime.value = 0 (or another
appropriate initial value) so both tokens and their expiration are cleared
during logout/refresh-failure flows.
| authStore.reset() | ||
| window.location.href = '/login' | ||
| throw error |
There was a problem hiding this comment.
🧹 Nitpick | 🔵 Trivial
라이브러리 레벨에서의 하드 리다이렉트 지양
공용 axios 모듈에서 window.location.href를 직접 호출하면 앱 라우터/상태 전환과 충돌할 수 있습니다. 응답 인터셉터 401 처리와 동일하게 라우터 이벤트(예: 전역 이벤트 버스)로 위임하는 방식을 권장합니다.
Also applies to: 96-105
🤖 Prompt for AI Agents
In front/src/api/axios.js around lines 46-48 and 96-105, remove the hard
redirect (window.location.href) in the axios response interceptor; instead
delegate navigation by emitting a global event or calling a provided router
callback so the app router handles redirects. Specifically, replace the direct
window.location.href with emitting an "unauthorized" event (or invoking a
passed-in router.push('/login')) after calling authStore.reset(), and ensure the
interceptor returns/throws the error (Promise.reject(error)) so callers can
handle it; do the same for the other 401 handling block at lines 96-105.
| if (isRefreshing) { | ||
| return new Promise((resolve) => { | ||
| addRefreshSubscriber((newAccessToken) => { | ||
| config.headers.Authorization = `Bearer ${newAccessToken}` | ||
| resolve(config) | ||
| }) | ||
| }) | ||
| } |
There was a problem hiding this comment.
명시적 Authorization 헤더 덮어쓰기 버그 (회원가입 등 깨질 수 있음)
요청 측에서 이미 Authorization을 명시한 경우(예: 가입 검증 토큰) 인터셉터가 Bearer 액세스 토큰으로 덮어씁니다. 결과적으로 API 요구 헤더가 바뀌어 실패할 수 있습니다.
다음과 같이 “이미 설정된 Authorization은 보존”하도록 가드해 주세요:
const token = authStore.getAccessToken()
const expiredAt = authStore.getAccessTokenExpirationTime()
if (!token) return config
+ // 호출자가 명시적으로 Authorization을 설정한 경우(특수 토큰) 덮어쓰지 않음
+ if (config.headers?.Authorization) return config
- const now = Date.now()
+ const now = Date.now()
// 만료된 토큰이면 refresh 시도
- if (expiredAt && now >= expiredAt) {
+ const SKEW_MS = 30_000
+ if (expiredAt && now >= expiredAt - SKEW_MS) {
console.warn('AccessToken 만료 → refresh 요청 시작')
// 이미 갱신 중이라면 기다림
- if (isRefreshing) {
- return new Promise((resolve) => {
- addRefreshSubscriber((newAccessToken) => {
- config.headers.Authorization = `Bearer ${newAccessToken}`
- resolve(config)
- })
- })
- }
+ if (isRefreshing) {
+ return new Promise((resolve, reject) => {
+ addRefreshSubscriber({
+ resolve: (newAccessToken) => {
+ if (!config.headers?.Authorization) {
+ config.headers.Authorization = `Bearer ${newAccessToken}`
+ }
+ resolve(config)
+ },
+ reject,
+ })
+ })
+ }
// refresh 수행
isRefreshing = true
const newAccessToken = await refreshAccessToken(authStore)
- config.headers.Authorization = `Bearer ${newAccessToken}`
+ if (!config.headers?.Authorization) {
+ config.headers.Authorization = `Bearer ${newAccessToken}`
+ }
return config
}
// 만료되지 않았으면 그대로 요청
- config.headers.Authorization = `Bearer ${token}`
+ if (!config.headers?.Authorization) {
+ config.headers.Authorization = `Bearer ${token}`
+ }Also applies to: 82-87, 89-91
🤖 Prompt for AI Agents
In front/src/api/axios.js around lines 73-80 (also apply same change to 82-87
and 89-91): the interceptor unconditionally overwrites
config.headers.Authorization when a refresh is in progress, which clobbers any
explicit Authorization set by the caller (e.g., signup/verification tokens). Fix
by checking if config.headers and config.headers.Authorization are already set
and only replace Authorization when it is missing; for the refresh-subscriber
path, only set Authorization = `Bearer ${newAccessToken}` if no pre-existing
Authorization exists, and do the same guard in the other two similar blocks so
caller-provided Authorization headers are preserved.
| export function toRefreshPayload(form) { | ||
| return { | ||
| refreshToken: form.refreshToken?.trim(), | ||
| } | ||
| } |
There was a problem hiding this comment.
refreshToken 유효성 체크 누락
빈 값/누락 시 서버에 빈 페이로드가 전송될 수 있습니다. 클라이언트에서 즉시 검증/실패시키면 UX와 로깅이 명확해집니다.
다음과 같이 보완을 권고합니다:
export function toRefreshPayload(form) {
- return {
- refreshToken: form.refreshToken?.trim(),
- }
+ const token = typeof form?.refreshToken === 'string' ? form.refreshToken.trim() : ''
+ if (!token) {
+ // wrapApi 에서 적절히 잡혀 사용자 친화적 메시지로 변환됨
+ throw new Error('MISSING_REFRESH_TOKEN')
+ }
+ return { refreshToken: token }
}📝 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.
| export function toRefreshPayload(form) { | |
| return { | |
| refreshToken: form.refreshToken?.trim(), | |
| } | |
| } | |
| export function toRefreshPayload(form) { | |
| const token = typeof form?.refreshToken === 'string' ? form.refreshToken.trim() : '' | |
| if (!token) { | |
| // wrapApi 에서 적절히 잡혀 사용자 친화적 메시지로 변환됨 | |
| throw new Error('MISSING_REFRESH_TOKEN') | |
| } | |
| return { refreshToken: token } | |
| } |
🤖 Prompt for AI Agents
In front/src/features/auth/dto.js around lines 36 to 40, toRefreshPayload
currently returns a possibly empty refreshToken; add a validation step that
trims form.refreshToken, checks that it's a non-empty string (length > 0) and if
valid returns { refreshToken }, otherwise throw or return a synchronous
validation error (e.g., throw new Error("refreshToken is required") or return
null/validation result) so callers immediately fail client-side and avoid
sending empty payloads to the server; ensure the calling code handles the
thrown/returned error consistently and update any tests/usages accordingly.
| export function decodeJwt(token) { | ||
| try { | ||
| if (!token) return null | ||
|
|
||
| const payload = token.split('.')[1] | ||
| if (!payload) return null | ||
|
|
||
| const decoded = atob(payload.replace(/-/g, '+').replace(/_/g, '/')) | ||
| return JSON.parse(decoded) | ||
| } catch (e) { | ||
| console.error('JWT 디코딩 실패:', e) | ||
| return null | ||
| } | ||
| } |
There was a problem hiding this comment.
base64url 패딩 누락으로 디코딩 실패 가능 (exp 계산 오동작 유발)
JWT payload는 '=' 패딩이 생략될 수 있습니다. 현재 구현은 패딩을 보완하지 않아 atob에서 실패할 수 있습니다. SSR 환경에서 atob가 없는 경우도 대비하세요.
안전한 base64url 디코딩으로 교체를 권장합니다:
+function base64UrlToBase64(input) {
+ const b64 = input.replace(/-/g, '+').replace(/_/g, '/')
+ const pad = b64.length % 4
+ return pad ? b64 + '='.repeat(4 - pad) : b64
+}
+
export function decodeJwt(token) {
try {
if (!token) return null
const payload = token.split('.')[1]
if (!payload) return null
- const decoded = atob(payload.replace(/-/g, '+').replace(/_/g, '/'))
+ const b64 = base64UrlToBase64(payload)
+ const decoded =
+ typeof atob === 'function'
+ ? atob(b64)
+ : Buffer.from(b64, 'base64').toString('binary')
return JSON.parse(decoded)
} catch (e) {
console.error('JWT 디코딩 실패:', e)
return null
}
}🤖 Prompt for AI Agents
In front/src/shared/utils/jwtUtils.js around lines 1 to 14, the decodeJwt
implementation can fail because it doesn't restore '=' base64url padding and
assumes atob exists (breaks in SSR); replace the current logic with a safe
base64url decoder that (1) converts '-' and '_' back to '+' and '/', (2) adds
the required '=' padding based on payload length, (3) uses a robust decoding
fallback when atob is unavailable (e.g., Buffer.from for Node/SSR), and (4)
parses the JSON payload inside try/catch and logs errors as before.
| export function isAccessTokenExpired(token) { | ||
| const exp = getTokenExpiration(token) | ||
| if (!exp) return true | ||
| return Date.now() >= exp | ||
| } |
There was a problem hiding this comment.
🧹 Nitpick | 🔵 Trivial
만료 정보가 없으면 항상 만료로 간주하는 정책 재확인
isAccessTokenExpired가 exp 없을 때 true를 반환합니다. 현재 인터셉터는 exp 타임스탬프를 따로 저장/사용하므로 이 함수 사용 시 동작이 달라질 수 있습니다. 사용 지점을 명확히 정하세요.
🤖 Prompt for AI Agents
In front/src/shared/utils/jwtUtils.js around lines 21 to 25, the current
isAccessTokenExpired returns true when no exp is present which can conflict with
interceptors that store/use exp separately; change the function signature to
accept an options parameter (e.g., { assumeExpiredIfNoExp: true } defaulting to
current behavior) and implement logic to return the configured value instead of
always true, then update callers: keep default behavior for general use, but
modify interceptor call sites to pass assumeExpiredIfNoExp: false (or explicitly
handle missing exp) so behavior is consistent and documented.
| const getAccessToken = () => { | ||
| return accessToken.value | ||
| } | ||
|
|
||
| const getRefreshToken = () => { | ||
| return refreshToken.value | ||
| } | ||
|
|
||
| const getAccessTokenExpirationTime = () => { | ||
| return accessTokenExpirationTime.value | ||
| } |
There was a problem hiding this comment.
🧹 Nitpick | 🔵 Trivial
getter 함수들이 불필요할 수 있습니다.
Vue 3와 Pinia에서는 ref가 이미 반응형이고 return 객체에서 직접 노출되므로, getAccessToken(), getRefreshToken(), getAccessTokenExpirationTime() 같은 단순 getter 함수들은 중복될 수 있습니다. 소비자 코드에서 authStore.accessToken 형태로 직접 접근 가능합니다.
다만, 기존 코드베이스에서 이미 getter 패턴을 사용 중이거나 일관성을 위해 유지하는 경우라면 현재 구현도 충분히 타당합니다.
🤖 Prompt for AI Agents
In front/src/stores/useAuthStore.js around lines 30 to 40, the simple getter
functions getAccessToken(), getRefreshToken(), and
getAccessTokenExpirationTime() are redundant because Pinia exposes refs
directly; remove these three functions and expose the ref properties
(accessToken, refreshToken, accessTokenExpirationTime) from the store so
consumers use authStore.accessToken etc., or if you must keep the getters for
consistency, mark them as legacy and add a TODO comment to remove later; update
any call sites to read the properties directly.
| const reset = () => { | ||
| staySignedIn.value = false | ||
| token.value = '' | ||
| accessToken.value = '' | ||
| } |
There was a problem hiding this comment.
reset() 함수가 모든 토큰 관련 상태를 초기화하지 않습니다.
reset() 함수가 accessToken만 초기화하고 refreshToken과 accessTokenExpirationTime은 초기화하지 않습니다. 이는 로그아웃이나 세션 종료 시 민감한 토큰 정보가 영구 저장소에 남아있을 수 있는 보안 문제를 야기합니다.
다음 diff를 적용하여 모든 토큰 관련 상태를 초기화하세요:
const reset = () => {
staySignedIn.value = false
accessToken.value = ''
+ refreshToken.value = ''
+ accessTokenExpirationTime.value = 0
}📝 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.
| const reset = () => { | |
| staySignedIn.value = false | |
| token.value = '' | |
| accessToken.value = '' | |
| } | |
| const reset = () => { | |
| staySignedIn.value = false | |
| accessToken.value = '' | |
| refreshToken.value = '' | |
| accessTokenExpirationTime.value = 0 | |
| } |
🤖 Prompt for AI Agents
In front/src/stores/useAuthStore.js around lines 42 to 45, the reset() function
only clears accessToken but leaves refreshToken and accessTokenExpirationTime
intact; update reset() to also clear refreshToken and accessTokenExpirationTime
(e.g., set refreshToken to an empty string and accessTokenExpirationTime to
null/0) and ensure any persisted token entries (localStorage/sessionStorage) are
removed as part of the reset.
#️⃣연관된 이슈
변경 타입
변경 내용
to-be(변경 후 설명을 여기에 작성)
체크리스트
코멘트
Summary by CodeRabbit
릴리스 노트
새로운 기능
버그 수정