11using System ;
2+ using System . Collections ;
23using System . Security . Cryptography ;
34using System . Text ;
45using UnityEngine ;
@@ -14,9 +15,8 @@ namespace ProjectVG.Infrastructure.Auth
1415 /// </summary>
1516 public class TokenManager : MonoBehaviour
1617 {
17- private const string ACCESS_TOKEN_KEY = "access_token" ;
18+ // AccessToken은 메모리에만 저장하므로 ACCESS_TOKEN_KEY, TOKEN_EXPIRY_KEY 는 제거
1819 private const string REFRESH_TOKEN_KEY = "refresh_token" ;
19- private const string TOKEN_EXPIRY_KEY = "token_expiry" ;
2020 private const string USER_ID_KEY = "user_id" ;
2121 private const string ENCRYPTION_KEY = "ProjectVG_OAuth2_Secure_Key_2024" ;
2222
@@ -54,6 +54,13 @@ private void Awake()
5454 _instance = this ;
5555 DontDestroyOnLoad ( gameObject ) ;
5656 LoadTokensFromStorage ( ) ;
57+
58+ // 앱 시작 시 RefreshToken이 있으면 자동으로 AccessToken 복구 시도
59+ if ( HasRefreshToken && ! IsRefreshTokenExpired ( ) )
60+ {
61+ Debug . Log ( "[TokenManager] RefreshToken 발견 - 자동 AccessToken 복구 시작" ) ;
62+ StartCoroutine ( AutoRecoverAccessTokenCoroutine ( ) ) ;
63+ }
5764 }
5865 else if ( _instance != this )
5966 {
@@ -74,21 +81,11 @@ public void SaveTokens(TokenSet tokenSet)
7481
7582 try
7683 {
84+ // AccessToken은 메모리에만 저장 (영속적 저장 안함)
7785 _currentAccessToken = tokenSet . AccessToken ;
7886 _currentRefreshToken = tokenSet . RefreshToken ;
7987 _currentUserId = tokenSet . RefreshToken ? . DeviceId ;
8088
81- // Access Token 저장 (암호화)
82- var accessTokenData = new TokenStorageData
83- {
84- Token = _currentAccessToken . Token ,
85- ExpiresAt = _currentAccessToken . ExpiresAt ,
86- TokenType = _currentAccessToken . TokenType ,
87- Scope = _currentAccessToken . Scope
88- } ;
89- var encryptedAccessToken = EncryptData ( JsonConvert . SerializeObject ( accessTokenData ) ) ;
90- PlayerPrefs . SetString ( ACCESS_TOKEN_KEY , encryptedAccessToken ) ;
91-
9289 // Refresh Token 저장 (강력한 암호화)
9390 string encryptedRefreshToken = null ;
9491 if ( _currentRefreshToken != null )
@@ -109,8 +106,7 @@ public void SaveTokens(TokenSet tokenSet)
109106 PlayerPrefs . DeleteKey ( REFRESH_TOKEN_KEY ) ;
110107 }
111108
112- // 만료 시간 저장
113- PlayerPrefs . SetString ( TOKEN_EXPIRY_KEY , _currentAccessToken . ExpiresAt . ToString ( "O" ) ) ;
109+ // AccessToken 만료 시간은 메모리에만 보관 (PlayerPrefs 저장 안함)
114110
115111 // User ID 저장
116112 if ( ! string . IsNullOrEmpty ( _currentUserId ) )
@@ -121,9 +117,8 @@ public void SaveTokens(TokenSet tokenSet)
121117 PlayerPrefs . Save ( ) ;
122118
123119 Debug . Log ( "=== 🔐 TokenManager 토큰 저장 완료 ===" ) ;
124- Debug . Log ( $ "[TokenManager] Access Token 저장 위치: PlayerPrefs[' { ACCESS_TOKEN_KEY } '] ") ;
120+ Debug . Log ( $ "[TokenManager] Access Token 저장: 메모리 전용 (영속적 저장 안함) ") ;
125121 Debug . Log ( $ "[TokenManager] Access Token 만료: { _currentAccessToken . ExpiresAt } ") ;
126- Debug . Log ( $ "[TokenManager] Access Token 암호화: { ! string . IsNullOrEmpty ( encryptedAccessToken ) } ") ;
127122
128123 if ( _currentRefreshToken != null )
129124 {
@@ -137,7 +132,6 @@ public void SaveTokens(TokenSet tokenSet)
137132 }
138133
139134 Debug . Log ( $ "[TokenManager] User ID 저장 위치: PlayerPrefs['{ USER_ID_KEY } '] = { _currentUserId } ") ;
140- Debug . Log ( $ "[TokenManager] 만료 시간 저장 위치: PlayerPrefs['{ TOKEN_EXPIRY_KEY } '] = { _currentAccessToken . ExpiresAt : O} ") ;
141135 Debug . Log ( "=== 토큰 저장 완료 ===" ) ;
142136
143137 OnTokensUpdated ? . Invoke ( tokenSet ) ;
@@ -226,22 +220,10 @@ public void UpdateAccessToken(string newAccessToken, int expiresInSeconds)
226220
227221 try
228222 {
223+ // AccessToken은 메모리에만 업데이트 (영속적 저장 안함)
229224 _currentAccessToken = new AccessToken ( newAccessToken , expiresInSeconds , "Bearer" , "oauth2" ) ;
230225
231- // Access Token만 업데이트
232- var accessTokenData = new TokenStorageData
233- {
234- Token = _currentAccessToken . Token ,
235- ExpiresAt = _currentAccessToken . ExpiresAt ,
236- TokenType = _currentAccessToken . TokenType ,
237- Scope = _currentAccessToken . Scope
238- } ;
239- var encryptedAccessToken = EncryptData ( JsonConvert . SerializeObject ( accessTokenData ) ) ;
240- PlayerPrefs . SetString ( ACCESS_TOKEN_KEY , encryptedAccessToken ) ;
241- PlayerPrefs . SetString ( TOKEN_EXPIRY_KEY , _currentAccessToken . ExpiresAt . ToString ( "O" ) ) ;
242- PlayerPrefs . Save ( ) ;
243-
244- Debug . Log ( "[TokenManager] Access Token 갱신 완료" ) ;
226+ Debug . Log ( "[TokenManager] Access Token 갱신 완료 (메모리 전용)" ) ;
245227 Debug . Log ( $ "[TokenManager] 새로운 만료 시간: { _currentAccessToken . ExpiresAt } ") ;
246228
247229 var tokenSet = new TokenSet ( _currentAccessToken , _currentRefreshToken ) ;
@@ -261,13 +243,13 @@ public void ClearTokens()
261243 {
262244 try
263245 {
246+ // 메모리에서 AccessToken 제거
264247 _currentAccessToken = null ;
265248 _currentRefreshToken = null ;
266249 _currentUserId = null ;
267250
268- PlayerPrefs . DeleteKey ( ACCESS_TOKEN_KEY ) ;
251+ // RefreshToken만 PlayerPrefs에서 삭제 (기존 ACCESS_TOKEN_KEY, TOKEN_EXPIRY_KEY 삭제 로직 제거)
269252 PlayerPrefs . DeleteKey ( REFRESH_TOKEN_KEY ) ;
270- PlayerPrefs . DeleteKey ( TOKEN_EXPIRY_KEY ) ;
271253 PlayerPrefs . DeleteKey ( USER_ID_KEY ) ;
272254 PlayerPrefs . Save ( ) ;
273255
@@ -288,20 +270,9 @@ private void LoadTokensFromStorage()
288270 {
289271 try
290272 {
291- // Access Token 로드
292- if ( PlayerPrefs . HasKey ( ACCESS_TOKEN_KEY ) )
293- {
294- var encryptedAccessToken = PlayerPrefs . GetString ( ACCESS_TOKEN_KEY ) ;
295- var decryptedAccessToken = DecryptData ( encryptedAccessToken ) ;
296- var accessTokenData = JsonConvert . DeserializeObject < TokenStorageData > ( decryptedAccessToken ) ;
297-
298- _currentAccessToken = new AccessToken (
299- accessTokenData . Token ,
300- accessTokenData . ExpiresAt ,
301- accessTokenData . TokenType ,
302- accessTokenData . Scope
303- ) ;
304- }
273+ // AccessToken은 저장소에서 로드하지 않음 (메모리 전용)
274+ // 앱 시작 시 AccessToken은 null 상태로 시작
275+ _currentAccessToken = null ;
305276
306277 // Refresh Token 로드
307278 if ( PlayerPrefs . HasKey ( REFRESH_TOKEN_KEY ) )
@@ -324,13 +295,8 @@ private void LoadTokensFromStorage()
324295 }
325296
326297 Debug . Log ( "=== 🔍 TokenManager 저장소에서 토큰 로드 완료 ===" ) ;
327- Debug . Log ( $ "[TokenManager] Access Token 로드 위치: PlayerPrefs['{ ACCESS_TOKEN_KEY } ']") ;
328- Debug . Log ( $ "[TokenManager] Access Token 존재: { _currentAccessToken != null } ") ;
329- if ( _currentAccessToken != null )
330- {
331- Debug . Log ( $ "[TokenManager] Access Token 만료: { _currentAccessToken . ExpiresAt } ") ;
332- Debug . Log ( $ "[TokenManager] Access Token 유효: { ! _currentAccessToken . IsExpired ( ) } ") ;
333- }
298+ Debug . Log ( $ "[TokenManager] Access Token: 메모리 전용 (영속적 로드 안함)") ;
299+ Debug . Log ( $ "[TokenManager] Access Token 상태: null (앱 시작 시 기본값)") ;
334300
335301 Debug . Log ( $ "[TokenManager] Refresh Token 로드 위치: PlayerPrefs['{ REFRESH_TOKEN_KEY } ']") ;
336302 Debug . Log ( $ "[TokenManager] Refresh Token 존재: { _currentRefreshToken != null } ") ;
@@ -341,7 +307,6 @@ private void LoadTokensFromStorage()
341307 }
342308
343309 Debug . Log ( $ "[TokenManager] User ID 로드 위치: PlayerPrefs['{ USER_ID_KEY } '] = { _currentUserId } ") ;
344- Debug . Log ( $ "[TokenManager] 만료 시간 로드 위치: PlayerPrefs['{ TOKEN_EXPIRY_KEY } ']") ;
345310 Debug . Log ( "=== 토큰 로드 완료 ===" ) ;
346311 }
347312 catch ( Exception ex )
@@ -418,10 +383,9 @@ public string GetDebugInfo()
418383 {
419384 var info = "TokenManager Debug Info:\n " ;
420385 info += $ "=== 저장 위치 정보 ===\n ";
421- info += $ "Access Token 저장: PlayerPrefs[' { ACCESS_TOKEN_KEY } '] \n ";
386+ info += $ "Access Token 저장: 메모리 전용 (영속적 저장 안함) \n ";
422387 info += $ "Refresh Token 저장: PlayerPrefs['{ REFRESH_TOKEN_KEY } ']\n ";
423388 info += $ "User ID 저장: PlayerPrefs['{ USER_ID_KEY } ']\n ";
424- info += $ "만료 시간 저장: PlayerPrefs['{ TOKEN_EXPIRY_KEY } ']\n ";
425389 info += $ "=== 토큰 상태 ===\n ";
426390 info += $ "Has Valid Tokens: { HasValidTokens } \n ";
427391 info += $ "Has Refresh Token: { HasRefreshToken } \n ";
@@ -445,6 +409,55 @@ public string GetDebugInfo()
445409
446410 return info ;
447411 }
412+
413+ /// <summary>
414+ /// 앱 시작 시 RefreshToken으로 AccessToken 자동 복구
415+ /// </summary>
416+ private IEnumerator AutoRecoverAccessTokenCoroutine ( )
417+ {
418+ // TokenRefreshService가 초기화될 때까지 대기
419+ yield return new WaitForSeconds ( 0.5f ) ;
420+
421+ if ( TokenRefreshService . Instance != null )
422+ {
423+ Debug . Log ( "[TokenManager] TokenRefreshService를 통해 AccessToken 복구 시도" ) ;
424+
425+ // 비동기 호출을 코루틴에서 처리
426+ StartCoroutine ( TryRefreshTokenCoroutine ( ) ) ;
427+ }
428+ else
429+ {
430+ Debug . LogWarning ( "[TokenManager] TokenRefreshService가 아직 초기화되지 않았습니다." ) ;
431+ }
432+ }
433+
434+ /// <summary>
435+ /// RefreshToken으로 AccessToken 복구 코루틴
436+ /// </summary>
437+ private IEnumerator TryRefreshTokenCoroutine ( )
438+ {
439+ var refreshTask = TokenRefreshService . Instance . RefreshAccessTokenAsync ( ) ;
440+
441+ // UniTask를 코루틴에서 기다림
442+ yield return new WaitUntil ( ( ) => refreshTask . Status != Cysharp . Threading . Tasks . UniTaskStatus . Pending ) ;
443+
444+ if ( refreshTask . Status == Cysharp . Threading . Tasks . UniTaskStatus . Succeeded )
445+ {
446+ bool success = refreshTask . GetAwaiter ( ) . GetResult ( ) ;
447+ if ( success )
448+ {
449+ Debug . Log ( "[TokenManager] 앱 시작 시 AccessToken 자동 복구 성공" ) ;
450+ }
451+ else
452+ {
453+ Debug . LogWarning ( "[TokenManager] 앱 시작 시 AccessToken 자동 복구 실패" ) ;
454+ }
455+ }
456+ else
457+ {
458+ Debug . LogError ( "[TokenManager] AccessToken 복구 중 오류 발생" ) ;
459+ }
460+ }
448461 }
449462
450463 /// <summary>
0 commit comments