fix: enhance authorization handling with improved logging for missing and invalid headers
This commit is contained in:
@@ -4,6 +4,7 @@ import android.content.Context
|
||||
import com.collabtable.app.data.preferences.PreferencesManager
|
||||
import android.os.Build
|
||||
import android.net.Uri
|
||||
import com.collabtable.app.utils.Logger
|
||||
import okhttp3.Interceptor
|
||||
import okhttp3.OkHttpClient
|
||||
import okhttp3.logging.HttpLoggingInterceptor
|
||||
@@ -32,7 +33,7 @@ object ApiClient {
|
||||
val newRequest =
|
||||
if (!password.isNullOrBlank()) {
|
||||
request.newBuilder()
|
||||
.header("Authorization", "Bearer $password")
|
||||
.header("Authorization", "Bearer ${'$'}password")
|
||||
.build()
|
||||
} else {
|
||||
request
|
||||
@@ -101,11 +102,17 @@ object ApiClient {
|
||||
this.context = context.applicationContext
|
||||
val prefs = PreferencesManager.getInstance(context)
|
||||
baseUrl = normalizeForAndroidEmulator(prefs.getServerUrl())
|
||||
try {
|
||||
Logger.i("HTTP", "API base URL initialized to $baseUrl")
|
||||
} catch (_: Exception) { }
|
||||
retrofit = buildRetrofit()
|
||||
}
|
||||
|
||||
fun setBaseUrl(url: String) {
|
||||
baseUrl = normalizeForAndroidEmulator(url)
|
||||
try {
|
||||
Logger.i("HTTP", "API base URL set to $baseUrl")
|
||||
} catch (_: Exception) { }
|
||||
retrofit = buildRetrofit()
|
||||
}
|
||||
|
||||
|
||||
+6
-5
@@ -41,7 +41,7 @@ class ServerSetupViewModel(
|
||||
|
||||
fun validateAndSaveServerUrl(
|
||||
url: String,
|
||||
password: String,
|
||||
password: String,
|
||||
) {
|
||||
viewModelScope.launch {
|
||||
_isValidating.value = true
|
||||
@@ -85,8 +85,9 @@ class ServerSetupViewModel(
|
||||
// Ensure it ends with /
|
||||
normalizedUrl = if (normalizedUrl.endsWith("/")) normalizedUrl else "$normalizedUrl/"
|
||||
|
||||
// Validate password is not empty
|
||||
if (password.isBlank()) {
|
||||
// Validate password is not empty (trim leading/trailing spaces)
|
||||
val trimmedPassword = password.trim()
|
||||
if (trimmedPassword.isBlank()) {
|
||||
_validationError.value = "Password cannot be empty"
|
||||
_isValidating.value = false
|
||||
return@launch
|
||||
@@ -178,7 +179,7 @@ class ServerSetupViewModel(
|
||||
val authRequest =
|
||||
Request.Builder()
|
||||
.url(testUrl)
|
||||
.header("Authorization", "Bearer $password")
|
||||
.header("Authorization", "Bearer ${'$'}trimmedPassword")
|
||||
.get()
|
||||
.build()
|
||||
|
||||
@@ -191,7 +192,7 @@ class ServerSetupViewModel(
|
||||
if (authResponse.isSuccessful) {
|
||||
// Password is valid, save both URL and password
|
||||
preferencesManager.setServerUrl(finalUrl)
|
||||
preferencesManager.setServerPassword(password)
|
||||
preferencesManager.setServerPassword(trimmedPassword)
|
||||
// Reset sync baseline for a fresh initial sync on new server
|
||||
preferencesManager.clearSyncState()
|
||||
preferencesManager.setIsFirstRun(false)
|
||||
|
||||
@@ -18,6 +18,12 @@ export const authenticatePassword = (req: Request, res: Response, next: NextFunc
|
||||
const authHeader = req.headers.authorization;
|
||||
|
||||
if (!authHeader) {
|
||||
// Log diagnostic info to help trace intermittent 401s
|
||||
console.warn('[AUTH] Missing Authorization header', {
|
||||
path: req.path,
|
||||
method: req.method,
|
||||
ip: req.ip,
|
||||
});
|
||||
return res.status(401).json({
|
||||
error: 'Unauthorized',
|
||||
message: 'No authorization header provided'
|
||||
@@ -27,6 +33,13 @@ export const authenticatePassword = (req: Request, res: Response, next: NextFunc
|
||||
// Parse the authorization header
|
||||
const parts = authHeader.split(' ');
|
||||
if (parts.length !== 2 || parts[0] !== 'Bearer') {
|
||||
console.warn('[AUTH] Invalid authorization header format', {
|
||||
path: req.path,
|
||||
method: req.method,
|
||||
ip: req.ip,
|
||||
// Do not log full header to avoid leaking secrets; include prefix only
|
||||
headerPreview: authHeader.substring(0, Math.min(authHeader.length, 20)) + '...'
|
||||
});
|
||||
return res.status(401).json({
|
||||
error: 'Unauthorized',
|
||||
message: 'Invalid authorization header format. Expected: Bearer <password>'
|
||||
@@ -37,6 +50,14 @@ export const authenticatePassword = (req: Request, res: Response, next: NextFunc
|
||||
|
||||
// Validate password
|
||||
if (providedPassword !== serverPassword) {
|
||||
// Avoid logging the full password; log only length and a small preview
|
||||
const safePreview = providedPassword ? `${providedPassword.substring(0, 2)}…(${providedPassword.length})` : 'null';
|
||||
console.warn('[AUTH] Invalid password', {
|
||||
path: req.path,
|
||||
method: req.method,
|
||||
ip: req.ip,
|
||||
providedPreview: safePreview,
|
||||
});
|
||||
return res.status(401).json({
|
||||
error: 'Unauthorized',
|
||||
message: 'Invalid password'
|
||||
|
||||
Reference in New Issue
Block a user