Merge branch 'fix/logout-when-unauthenticated'

This commit is contained in:
Hafiz 2025-07-02 13:30:04 -05:00
commit e5d3ed8c99
4 changed files with 50 additions and 12 deletions

View file

@ -1,6 +1,7 @@
package com.habitrpg.android.habitica.data.implementation
import android.content.Context
import android.util.Log
import com.google.gson.JsonSyntaxException
import com.habitrpg.android.habitica.BuildConfig
import com.habitrpg.android.habitica.HabiticaBaseApplication
@ -55,6 +56,7 @@ import okhttp3.Cache
import okhttp3.OkHttpClient
import okhttp3.Request
import okhttp3.logging.HttpLoggingInterceptor
import org.json.JSONObject
import retrofit2.Converter
import retrofit2.HttpException
import retrofit2.Retrofit
@ -155,6 +157,35 @@ class ApiClientImpl(
// Modify cache control for 4xx or 5xx range - effectively "do not cache", preventing caching of 4xx and 5xx responses
if (response.code in 400..599) {
when (response.code) {
401 -> {
val path = response.request.url.encodedPath
if (!path.contains("/user/auth/update-password")) {
val bodyStr = try {
response.peekBody(1024).string()
} catch (_: Exception) {
""
}
val (errField, msgField) = try {
val obj = JSONObject(bodyStr)
obj.optString("error", "") to obj.optString("message", "")
} catch (_: Exception) {
"" to ""
}
val shouldLogout = errField.equals("missingAuthHeaders", ignoreCase = true)
|| errField.equals("invalidCredentials", ignoreCase = true)
|| msgField.contains("invalidCredentials", ignoreCase = true)
|| msgField.contains("Missing authentication headers", ignoreCase = true)
|| msgField.contains("There is no account that uses those credentials", ignoreCase = true)
if (shouldLogout) {
HabiticaBaseApplication.logout(context)
}
}
return@addNetworkInterceptor response
}
404 -> {
// The server is returning a 404 error, which means the requested resource was not found.
// In this case - we want to actually cache the response, and handle it in the app

View file

@ -271,6 +271,10 @@ class AccountPreferenceFragment :
)
response?.apiToken?.let {
viewModel.saveTokens(it, user?.id ?: "")
(activity as? SnackbarActivity)?.showSnackbar(
content = getString(R.string.password_changed),
displayType = HabiticaSnackbar.SnackbarDisplayType.SUCCESS,
)
sheet.dismiss()
}
}

View file

@ -1,5 +1,7 @@
package com.habitrpg.android.habitica.ui.fragments.preferences
import android.content.ClipData
import android.content.ClipboardManager
import android.content.res.Configuration
import android.os.Bundle
import android.view.LayoutInflater
@ -7,6 +9,7 @@ import android.view.View
import android.view.ViewGroup
import androidx.compose.ui.platform.ComposeView
import androidx.core.content.ContextCompat
import androidx.core.content.ContextCompat.getSystemService
import androidx.core.view.WindowInsetsControllerCompat
import com.google.android.material.bottomsheet.BottomSheetDialogFragment
import com.habitrpg.android.habitica.R
@ -61,6 +64,9 @@ class ApiTokenBottomSheetFragment : BottomSheetDialogFragment() {
content = getString(R.string.copied_to_clipboard, copiedToken),
displayType = HabiticaSnackbar.SnackbarDisplayType.SUCCESS,
)
val clipboard: ClipboardManager? =
context?.let { getSystemService(it, ClipboardManager::class.java) }
clipboard?.setPrimaryClip(ClipData.newPlainText("API Token", copiedToken))
dismiss()
})
}

View file

@ -1,15 +1,12 @@
package com.habitrpg.common.habitica.models.auth
class UserAuthResponse {
// we need apiToken and token, as both are possible returns
var apiToken: String = ""
var token: String
get() {
return apiToken
}
set(value) {
apiToken = value
}
var newUser = false
var id: String = ""
data class UserAuthResponse(
val apiToken: String = "",
val id: String = "",
val newUser: Boolean = false
) {
val token: String
get() = apiToken
}