From 8142d626c065b289fb900f404f17c225466be16e Mon Sep 17 00:00:00 2001 From: Nicholas Wallace Date: Sun, 19 Jul 2026 10:24:12 -0700 Subject: [PATCH] Ensure token is not part of download URL --- .../app/managers/DownloadItemManager.kt | 14 ++++++++------ .../app/managers/InternalDownloadManager.kt | 6 ++++-- .../app/models/DownloadItemPart.kt | 16 ++-------------- 3 files changed, 14 insertions(+), 22 deletions(-) diff --git a/android/app/src/main/java/com/audiobookshelf/app/managers/DownloadItemManager.kt b/android/app/src/main/java/com/audiobookshelf/app/managers/DownloadItemManager.kt index 58ba8269..258b9d62 100644 --- a/android/app/src/main/java/com/audiobookshelf/app/managers/DownloadItemManager.kt +++ b/android/app/src/main/java/com/audiobookshelf/app/managers/DownloadItemManager.kt @@ -182,6 +182,9 @@ class DownloadItemManager( part.lastUpdateTime = System.currentTimeMillis() currentDownloadItemParts.add(part) persist(item, force = true) + val token = + DeviceManager.deviceData.serverConnectionConfigs + .find { it.id == item.serverConnectionConfigId }?.token ?: DeviceManager.token activeCalls[part.id] = InternalDownloadManager(stagingFile, part.fileSize, object : InternalProgressCallback { override fun onProgress(totalBytesWritten: Long, progress: Long) { @@ -204,7 +207,9 @@ class DownloadItemManager( persist(item, force = true) } } - }, { hasAvailableSpace(part) }).download(serverUrl(item, part)) + }, { hasAvailableSpace(part) }).download( + serverUrl(item, part), + token) } @Synchronized @@ -445,11 +450,8 @@ class DownloadItemManager( } private fun serverUrl(item: DownloadItem, part: DownloadItemPart): String { - val token = DeviceManager.deviceData.serverConnectionConfigs - .find { it.id == item.serverConnectionConfigId }?.token ?: DeviceManager.token - var url = "${item.serverAddress}${part.serverPath}?token=$token" - if (part.serverPath.endsWith("/cover")) url += "&raw=1" - return url + val rawCover = if (part.serverPath.endsWith("/cover")) "?raw=1" else "" + return "${item.serverAddress}${part.serverPath}$rawCover" } private companion object { diff --git a/android/app/src/main/java/com/audiobookshelf/app/managers/InternalDownloadManager.kt b/android/app/src/main/java/com/audiobookshelf/app/managers/InternalDownloadManager.kt index 63e33828..7c23e6cc 100644 --- a/android/app/src/main/java/com/audiobookshelf/app/managers/InternalDownloadManager.kt +++ b/android/app/src/main/java/com/audiobookshelf/app/managers/InternalDownloadManager.kt @@ -22,16 +22,18 @@ class InternalDownloadManager( /** * Starts or resumes a download. * - * @param url authenticated download URL + * @param url download URL + * @param token access token sent in the Authorization header * @return active call, used to cancel a stalled transfer */ - fun download(url: String): Call { + fun download(url: String, token: String): Call { destinationFile.parentFile?.mkdirs() val existingBytes = destinationFile.takeIf { it.exists() }?.length() ?: 0L val request = Request.Builder() .url(url) .addHeader("Accept-Encoding", "identity") + .addHeader("Authorization", "Bearer $token") .apply { if (existingBytes > 0L) header("Range", "bytes=$existingBytes-") } diff --git a/android/app/src/main/java/com/audiobookshelf/app/models/DownloadItemPart.kt b/android/app/src/main/java/com/audiobookshelf/app/models/DownloadItemPart.kt index 31d887c2..7dadbca8 100644 --- a/android/app/src/main/java/com/audiobookshelf/app/models/DownloadItemPart.kt +++ b/android/app/src/main/java/com/audiobookshelf/app/models/DownloadItemPart.kt @@ -44,13 +44,9 @@ data class DownloadItemPart( fun make(downloadItemId:String, filename:String, fileSize: Long, destinationFile: File, finalDestinationFile: File, subfolder:String, serverPath:String, localFolder: LocalFolder, ebookFile: EBookFile?, audioTrack: AudioTrack?, episode: PodcastEpisode?) :DownloadItemPart { val destinationUri = Uri.fromFile(destinationFile) val finalDestinationUri = Uri.fromFile(finalDestinationFile) + val rawCover = if (serverPath.endsWith("/cover")) "?raw=1" else "" + val downloadUri = Uri.parse("${DeviceManager.serverAddress}${serverPath}$rawCover") - var downloadUrl = "${DeviceManager.serverAddress}${serverPath}?token=${DeviceManager.token}" - if (serverPath.endsWith("/cover")) { - downloadUrl += "&raw=1" // Download raw cover image - } - - val downloadUri = Uri.parse(downloadUrl) Log.d("DownloadItemPart", "Audio File Destination Uri: $destinationUri | Final Destination Uri: $finalDestinationUri | Server Path $serverPath") return DownloadItemPart( id = DeviceManager.getBase64Id(finalDestinationFile.absolutePath), @@ -86,12 +82,4 @@ data class DownloadItemPart( @get:JsonIgnore val isInternalStorage get() = localFolderId.startsWith("internal-") - @get:JsonIgnore - val serverUrl: String - get() { - var url = "${DeviceManager.serverAddress}${serverPath}?token=${DeviceManager.token}" - if (serverPath.endsWith("/cover")) url += "&raw=1" - return url - } - }