FIXED token issues but added logs
This commit is contained in:
parent
88161f7933
commit
d46bc1f461
|
|
@ -45,25 +45,40 @@ class AuthApiClient(private val context: Context) {
|
|||
loadTokens {
|
||||
val accessToken = tokenManager.getAccessToken()
|
||||
val refreshToken = tokenManager.getRefreshToken()
|
||||
android.util.Log.d("AuthApiClient", "loadTokens: accessToken=${accessToken != null}, refreshToken=${refreshToken != null}")
|
||||
if (accessToken != null && refreshToken != null) {
|
||||
android.util.Log.d("AuthApiClient", "loadTokens: Returning BearerTokens")
|
||||
BearerTokens(accessToken, refreshToken)
|
||||
} else {
|
||||
android.util.Log.d("AuthApiClient", "loadTokens: No tokens available, returning null")
|
||||
null
|
||||
}
|
||||
}
|
||||
|
||||
refreshTokens {
|
||||
val refreshToken = tokenManager.getRefreshToken() ?: return@refreshTokens null
|
||||
android.util.Log.d("AuthApiClient", "refreshTokens: Starting token refresh")
|
||||
val refreshToken = tokenManager.getRefreshToken() ?: run {
|
||||
android.util.Log.e("AuthApiClient", "refreshTokens: No refresh token found!")
|
||||
return@refreshTokens null
|
||||
}
|
||||
|
||||
android.util.Log.d("AuthApiClient", "refreshTokens: Calling /auth/refresh endpoint")
|
||||
try {
|
||||
val response: RefreshResponse = client.post("http://10.0.2.2:3000/auth/refresh") {
|
||||
markAsRefreshTokenRequest()
|
||||
contentType(ContentType.Application.Json)
|
||||
setBody(RefreshRequest(refreshToken))
|
||||
}.body()
|
||||
|
||||
val response: RefreshResponse = client.post("http://10.0.2.2:3000/auth/refresh") {
|
||||
markAsRefreshTokenRequest()
|
||||
contentType(ContentType.Application.Json)
|
||||
setBody(RefreshRequest(refreshToken))
|
||||
}.body()
|
||||
android.util.Log.d("AuthApiClient", "refreshTokens: Refresh successful, saving new tokens")
|
||||
tokenManager.saveTokens(response.accessToken, response.refreshToken)
|
||||
android.util.Log.d("AuthApiClient", "refreshTokens: New tokens saved successfully")
|
||||
|
||||
tokenManager.saveTokens(response.accessToken, response.refreshToken)
|
||||
|
||||
BearerTokens(response.accessToken, response.refreshToken)
|
||||
BearerTokens(response.accessToken, response.refreshToken)
|
||||
} catch (e: Exception) {
|
||||
android.util.Log.e("AuthApiClient", "refreshTokens: Refresh failed: ${e.message}", e)
|
||||
throw e
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -155,21 +170,40 @@ class AuthApiClient(private val context: Context) {
|
|||
}
|
||||
|
||||
suspend fun getUserDetails(): Result<UserDetails> = runCatching {
|
||||
client.get("users/me").body()
|
||||
android.util.Log.d("AuthApiClient", "getUserDetails: Calling /users/me endpoint")
|
||||
try {
|
||||
val response = client.get("users/me")
|
||||
android.util.Log.d("AuthApiClient", "getUserDetails: Response status=${response.status}")
|
||||
val userDetails = response.body<UserDetails>()
|
||||
android.util.Log.d("AuthApiClient", "getUserDetails: Success - user id=${userDetails.id}")
|
||||
userDetails
|
||||
} catch (e: Exception) {
|
||||
android.util.Log.e("AuthApiClient", "getUserDetails: Error - ${e.message}", e)
|
||||
throw e
|
||||
}
|
||||
}
|
||||
|
||||
suspend fun refreshToken(): Result<RefreshResponse> = runCatching {
|
||||
android.util.Log.d("AuthApiClient", "refreshToken: Starting manual token refresh")
|
||||
val refreshToken = tokenManager.getRefreshToken()
|
||||
?: throw IllegalStateException("No refresh token found")
|
||||
|
||||
val response: RefreshResponse = client.post("auth/refresh") {
|
||||
contentType(ContentType.Application.Json)
|
||||
setBody(RefreshRequest(refreshToken))
|
||||
}.body()
|
||||
android.util.Log.d("AuthApiClient", "refreshToken: Calling /auth/refresh endpoint")
|
||||
try {
|
||||
val response: RefreshResponse = client.post("auth/refresh") {
|
||||
contentType(ContentType.Application.Json)
|
||||
setBody(RefreshRequest(refreshToken))
|
||||
}.body()
|
||||
|
||||
// Save the new tokens (refresh token rotates)
|
||||
tokenManager.saveTokens(response.accessToken, response.refreshToken)
|
||||
response
|
||||
android.util.Log.d("AuthApiClient", "refreshToken: Refresh successful, saving new tokens")
|
||||
// Save the new tokens (refresh token rotates)
|
||||
tokenManager.saveTokens(response.accessToken, response.refreshToken)
|
||||
android.util.Log.d("AuthApiClient", "refreshToken: New tokens saved successfully")
|
||||
response
|
||||
} catch (e: Exception) {
|
||||
android.util.Log.e("AuthApiClient", "refreshToken: Refresh failed: ${e.message}", e)
|
||||
throw e
|
||||
}
|
||||
}
|
||||
|
||||
suspend fun logout(): Result<LogoutResponse> = runCatching {
|
||||
|
|
|
|||
|
|
@ -2,6 +2,7 @@ package com.example.livingai_lg.api
|
|||
|
||||
import android.content.Context
|
||||
import android.content.SharedPreferences
|
||||
import android.util.Log
|
||||
import androidx.security.crypto.EncryptedSharedPreferences
|
||||
import androidx.security.crypto.MasterKey
|
||||
|
||||
|
|
@ -20,25 +21,47 @@ class TokenManager(context: Context) {
|
|||
)
|
||||
|
||||
companion object {
|
||||
private const val TAG = "TokenManager"
|
||||
private const val KEY_ACCESS_TOKEN = "access_token"
|
||||
private const val KEY_REFRESH_TOKEN = "refresh_token"
|
||||
}
|
||||
|
||||
fun saveTokens(accessToken: String, refreshToken: String) {
|
||||
prefs.edit()
|
||||
Log.d(TAG, "saveTokens: Saving tokens (accessToken length=${accessToken.length}, refreshToken length=${refreshToken.length})")
|
||||
val success = prefs.edit()
|
||||
.putString(KEY_ACCESS_TOKEN, accessToken)
|
||||
.putString(KEY_REFRESH_TOKEN, refreshToken)
|
||||
.commit() // Use commit() instead of apply() to ensure tokens are saved synchronously
|
||||
|
||||
if (success) {
|
||||
Log.d(TAG, "saveTokens: Tokens saved successfully")
|
||||
// Verify tokens were saved
|
||||
val savedAccess = prefs.getString(KEY_ACCESS_TOKEN, null)
|
||||
val savedRefresh = prefs.getString(KEY_REFRESH_TOKEN, null)
|
||||
Log.d(TAG, "saveTokens: Verification - accessToken saved=${savedAccess != null}, refreshToken saved=${savedRefresh != null}")
|
||||
} else {
|
||||
Log.e(TAG, "saveTokens: FAILED to save tokens!")
|
||||
}
|
||||
}
|
||||
|
||||
fun getAccessToken(): String? = prefs.getString(KEY_ACCESS_TOKEN, null)
|
||||
fun getAccessToken(): String? {
|
||||
val token = prefs.getString(KEY_ACCESS_TOKEN, null)
|
||||
Log.d(TAG, "getAccessToken: token=${token != null}, length=${token?.length ?: 0}")
|
||||
return token
|
||||
}
|
||||
|
||||
fun getRefreshToken(): String? = prefs.getString(KEY_REFRESH_TOKEN, null)
|
||||
fun getRefreshToken(): String? {
|
||||
val token = prefs.getString(KEY_REFRESH_TOKEN, null)
|
||||
Log.d(TAG, "getRefreshToken: token=${token != null}, length=${token?.length ?: 0}")
|
||||
return token
|
||||
}
|
||||
|
||||
fun clearTokens() {
|
||||
Log.d(TAG, "clearTokens: Clearing all tokens")
|
||||
prefs.edit()
|
||||
.remove(KEY_ACCESS_TOKEN)
|
||||
.remove(KEY_REFRESH_TOKEN)
|
||||
.apply()
|
||||
Log.d(TAG, "clearTokens: Tokens cleared")
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -38,12 +38,22 @@ class MainViewModel(context: Context) : ViewModel() {
|
|||
|
||||
init {
|
||||
// Immediately check if tokens exist (synchronous check)
|
||||
val hasTokens = tokenManager.getAccessToken() != null && tokenManager.getRefreshToken() != null
|
||||
val accessToken = tokenManager.getAccessToken()
|
||||
val refreshToken = tokenManager.getRefreshToken()
|
||||
val hasTokens = accessToken != null && refreshToken != null
|
||||
|
||||
Log.d(TAG, "MainViewModel.init: accessToken=${accessToken != null}, refreshToken=${refreshToken != null}, hasTokens=$hasTokens")
|
||||
|
||||
if (hasTokens) {
|
||||
// Tokens exist, validate them asynchronously
|
||||
// Tokens exist - optimistically set to Authenticated for immediate navigation
|
||||
// Then validate in background (this prevents redirect to landing page on app restart)
|
||||
Log.d(TAG, "MainViewModel.init: Tokens found, setting authState to Authenticated (optimistic)")
|
||||
_authState.value = AuthState.Authenticated
|
||||
// Validate tokens in background (this will only revert if there's a clear auth failure)
|
||||
checkAuthStatus()
|
||||
} else {
|
||||
// No tokens, immediately set to unauthenticated
|
||||
Log.d(TAG, "MainViewModel.init: No tokens found, setting authState to Unauthenticated")
|
||||
_authState.value = AuthState.Unauthenticated
|
||||
}
|
||||
}
|
||||
|
|
@ -86,15 +96,17 @@ class MainViewModel(context: Context) : ViewModel() {
|
|||
*/
|
||||
private fun validateTokensOptimistic() {
|
||||
viewModelScope.launch {
|
||||
Log.d(TAG, "validateTokensOptimistic: Starting token validation")
|
||||
// Try to fetch user details first - Ktor's Auth plugin will auto-refresh if access token is expired
|
||||
authApiClient.getUserDetails()
|
||||
.onSuccess { userDetails ->
|
||||
// Tokens are valid, user is authenticated
|
||||
Log.d(TAG, "Token validation successful - user authenticated")
|
||||
Log.d(TAG, "validateTokensOptimistic: Token validation successful - user authenticated, userId=${userDetails.id}")
|
||||
_authState.value = AuthState.Authenticated
|
||||
_userState.value = UserState.Success(userDetails)
|
||||
}
|
||||
.onFailure { error ->
|
||||
Log.w(TAG, "validateTokensOptimistic: getUserDetails failed - ${error.message}")
|
||||
// Check if this is a network error or authentication error
|
||||
val isNetworkError = error.message?.contains("Unable to resolve host", ignoreCase = true) == true
|
||||
|| error.message?.contains("timeout", ignoreCase = true) == true
|
||||
|
|
@ -104,12 +116,15 @@ class MainViewModel(context: Context) : ViewModel() {
|
|||
|| error.message?.contains("ConnectException", ignoreCase = true) == true
|
||||
|| error.message?.contains("UnknownHostException", ignoreCase = true) == true
|
||||
|
||||
Log.d(TAG, "validateTokensOptimistic: isNetworkError=$isNetworkError")
|
||||
|
||||
if (isNetworkError) {
|
||||
// Network error - keep optimistic authentication state
|
||||
// User might be offline, tokens are still valid
|
||||
Log.w(TAG, "Network error during token validation (keeping optimistic auth): ${error.message}")
|
||||
Log.w(TAG, "validateTokensOptimistic: Network error detected (keeping optimistic auth): ${error.message}")
|
||||
_userState.value = UserState.Error("Network error. Please check your connection.")
|
||||
// Keep authState as Authenticated - don't revert on network errors
|
||||
Log.d(TAG, "validateTokensOptimistic: Keeping authState as Authenticated despite network error")
|
||||
return@launch
|
||||
}
|
||||
|
||||
|
|
@ -190,12 +205,16 @@ class MainViewModel(context: Context) : ViewModel() {
|
|||
val accessToken = tokenManager.getAccessToken()
|
||||
val refreshToken = tokenManager.getRefreshToken()
|
||||
|
||||
Log.d(TAG, "checkAuthStatus: accessToken=${accessToken != null}, refreshToken=${refreshToken != null}")
|
||||
|
||||
if (accessToken != null && refreshToken != null) {
|
||||
// Tokens exist, validate them by fetching user details
|
||||
// The Ktor Auth plugin will automatically refresh if access token is expired
|
||||
validateTokens()
|
||||
// Tokens exist, validate them using optimistic validation
|
||||
// This keeps authState as Authenticated unless there's a clear auth failure
|
||||
Log.d(TAG, "checkAuthStatus: Validating tokens optimistically")
|
||||
validateTokensOptimistic()
|
||||
} else {
|
||||
// No tokens, user is not authenticated
|
||||
Log.d(TAG, "checkAuthStatus: No tokens found, setting authState to Unauthenticated")
|
||||
_authState.value = AuthState.Unauthenticated
|
||||
}
|
||||
}
|
||||
|
|
|
|||
Loading…
Reference in New Issue