[Android] Remote access: extract authentication routing

Nicolas Pomepuy git at videolan.org
Thu Sep 10 04:41:21 UTC 2026


vlc-android | branch: master | Nicolas Pomepuy <nicolas at videolabs.io> | Tue Sep  1 10:32:44 2026 +0200| [8cd6427bf0b79a52218ccc077b356019ca84f134] | committer: Nicolas Pomepuy

Remote access: extract authentication routing

> https://code.videolan.org/videolan/vlc-android/commit/8cd6427bf0b79a52218ccc077b356019ca84f134
---

 .../vlc/remoteaccessserver/RemoteAccessRouting.kt  |  56 +----------
 .../routing/RemoteAccessRoutingAuth.kt             | 104 +++++++++++++++++++++
 2 files changed, 109 insertions(+), 51 deletions(-)

diff --git a/application/remote-access-server/src/main/java/org/videolan/vlc/remoteaccessserver/RemoteAccessRouting.kt b/application/remote-access-server/src/main/java/org/videolan/vlc/remoteaccessserver/RemoteAccessRouting.kt
index 1ffa6436bd..814b8dabf3 100644
--- a/application/remote-access-server/src/main/java/org/videolan/vlc/remoteaccessserver/RemoteAccessRouting.kt
+++ b/application/remote-access-server/src/main/java/org/videolan/vlc/remoteaccessserver/RemoteAccessRouting.kt
@@ -166,52 +166,9 @@ private const val TAG = "VLC/HttpSharingServer"
 fun Route.setupRouting(appContext: Context, scope: CoroutineScope) {
     val settings = Settings.getInstance(appContext)
     staticFiles("", File(getServerFiles(appContext)))
-    //the client is requesting a new code.
-    // if the formparameters "challenge" is sent. Remove the corresponding code
-    post("/code") {
-        val formParameters = try {
-            call.receiveParameters()
-        } catch (e: Exception) {
-            null
-        }
-        val challenge = if (formParameters == null) null else formParameters["challenge"].toString()
-        if (!challenge.isNullOrBlank()) {
-            RemoteAccessOTP.removeCodeWithChallenge(challenge)
-        }
-        val code = RemoteAccessOTP.getFirstValidCode(appContext)
-        scope.launch {
-            RemoteAccessUtils.otpFlow.emit(code.code)
-        }
-        call.respondText(code.challenge)
-    }
-    //Verify the code and inject the cookie if valid
-    post("/verify-code") {
-        val formParameters = try {
-            call.receiveParameters()
-        } catch (e: Exception) {
-            null
-        }
-        val idString = formParameters?.get("code")
-        if (idString == null){
-            call.respond(HttpStatusCode.BadRequest)
-            return at post
-        }
-        if (RemoteAccessOTP.verifyCode(appContext, idString)) {
-            //verification is OK
-            RemoteAccessSession.injectCookie(call, settings)
-            scope.launch {
-                RemoteAccessUtils.otpFlow.emit(null)
-            }
-            call.respondRedirect("/")
-            return at post
-        }
-        if (isFlooding(appContext, call.request.origin.remoteAddress)) {
-            Log.w(TAG, "Too many requests from ${call.request.origin.remoteAddress}")
-            call.respond(HttpStatusCode.TooManyRequests)
-            return at post
-        }
-        call.respondRedirect("/index.html#/login/error")
-    }
+
+    publicAuthRouting(appContext, scope, settings)
+
     // Main end point redirect to index.html
     get("/") {
         call.respondRedirect("index.html", permanent = true)
@@ -443,12 +400,14 @@ fun Route.setupRouting(appContext: Context, scope: CoroutineScope) {
     }
 
     authenticate("user_session", optional = RemoteAccessServer.byPassAuth) {
+        authenticatedAuthRouting()
         post("/logs") {
             val formParameters = try {
                 call.receiveParameters()
             } catch (e: Exception) {
                 null
             }
+
             val logs = buildString {
                 formParameters?.forEach { s, strings ->
                     if (s.contains("[time]"))
@@ -501,11 +460,6 @@ fun Route.setupRouting(appContext: Context, scope: CoroutineScope) {
             else
                 call.respondText("")
         }
-        //Provide a Websocket auth ticket as auth is validated
-        get("/wsticket") {
-            val ticket = RemoteAccessWebSockets.createTicket()
-            call.respondText(ticket)
-        }
 
         // List of all the videos
         get("/video-list") {
diff --git a/application/remote-access-server/src/main/java/org/videolan/vlc/remoteaccessserver/routing/RemoteAccessRoutingAuth.kt b/application/remote-access-server/src/main/java/org/videolan/vlc/remoteaccessserver/routing/RemoteAccessRoutingAuth.kt
new file mode 100644
index 0000000000..ef473cf655
--- /dev/null
+++ b/application/remote-access-server/src/main/java/org/videolan/vlc/remoteaccessserver/routing/RemoteAccessRoutingAuth.kt
@@ -0,0 +1,104 @@
+/*
+ * ************************************************************************
+ *  RemoteAccessRoutingAuth.kt
+ * *************************************************************************
+ * Copyright © 2026 VLC authors and VideoLAN
+ * Author: Nicolas POMEPUY
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston MA 02110-1301, USA.
+ * **************************************************************************
+ *
+ *
+ */
+
+package org.videolan.vlc.remoteaccessserver.routing
+
+import android.content.Context
+import android.content.SharedPreferences
+import android.util.Log
+import io.ktor.http.HttpStatusCode
+import io.ktor.server.application.call
+import io.ktor.server.plugins.origin
+import io.ktor.server.request.receiveParameters
+import io.ktor.server.response.respond
+import io.ktor.server.response.respondRedirect
+import io.ktor.server.response.respondText
+import io.ktor.server.routing.Route
+import io.ktor.server.routing.get
+import io.ktor.server.routing.post
+import kotlinx.coroutines.CoroutineScope
+import kotlinx.coroutines.launch
+import org.videolan.vlc.remoteaccessserver.RemoteAccessOTP
+import org.videolan.vlc.remoteaccessserver.RemoteAccessSession
+import org.videolan.vlc.remoteaccessserver.websockets.RemoteAccessWebSockets
+import org.videolan.vlc.util.RemoteAccessUtils
+
+private const val TAG = "RARoutingAuth"
+
+fun Route.publicAuthRouting(appContext: Context, scope: CoroutineScope, settings: SharedPreferences) {
+    //the client is requesting a new code.
+    // if the formparameters "challenge" is sent. Remove the corresponding code
+    post("/code") {
+        val formParameters = try {
+            call.receiveParameters()
+        } catch (_: Exception) {
+            null
+        }
+        val challenge = formParameters?.get("challenge")
+        if (!challenge.isNullOrBlank()) {
+            RemoteAccessOTP.removeCodeWithChallenge(challenge)
+        }
+        val code = RemoteAccessOTP.getFirstValidCode(appContext)
+        scope.launch {
+            RemoteAccessUtils.otpFlow.emit(code.code)
+        }
+        call.respondText(code.challenge)
+    }
+    //Verify the code and inject the cookie if valid
+    post("/verify-code") {
+        val formParameters = try {
+            call.receiveParameters()
+        } catch (e: Exception) {
+            null
+        }
+        val idString = formParameters?.get("code")
+        if (idString == null){
+            call.respond(HttpStatusCode.BadRequest)
+            return at post
+        }
+        if (RemoteAccessOTP.verifyCode(appContext, idString)) {
+            //verification is OK
+            RemoteAccessSession.injectCookie(call, settings)
+            scope.launch {
+                RemoteAccessUtils.otpFlow.emit(null)
+            }
+            call.respondRedirect("/")
+            return at post
+        }
+        if (isFlooding(appContext, call.request.origin.remoteAddress)) {
+            Log.w(TAG, "Too many requests from ${call.request.origin.remoteAddress}")
+            call.respond(HttpStatusCode.TooManyRequests)
+            return at post
+        }
+        call.respondRedirect("/index.html#/login/error")
+    }
+}
+
+fun Route.authenticatedAuthRouting() {
+    //Provide a Websocket auth ticket as auth is validated
+    get("/wsticket") {
+        val ticket = RemoteAccessWebSockets.createTicket()
+        call.respondText(ticket)
+    }
+}



More information about the Android mailing list