diff --git a/app/src/main/java/net/novagamestudios/kaffeekasse/ui/login/Login.kt b/app/src/main/java/net/novagamestudios/kaffeekasse/ui/login/Login.kt
index c257b54d3fb93068e5242d924d3525e3e3cd8184..3413a22fb68f59eb79883dd148f81efed09e58be 100644
--- a/app/src/main/java/net/novagamestudios/kaffeekasse/ui/login/Login.kt
+++ b/app/src/main/java/net/novagamestudios/kaffeekasse/ui/login/Login.kt
@@ -1,37 +1,13 @@
 package net.novagamestudios.kaffeekasse.ui.login
 
-import androidx.compose.foundation.layout.Arrangement
-import androidx.compose.foundation.layout.Column
-import androidx.compose.foundation.layout.IntrinsicSize
-import androidx.compose.foundation.layout.width
-import androidx.compose.foundation.text.KeyboardActions
-import androidx.compose.foundation.text.KeyboardOptions
 import androidx.compose.material.icons.Icons
 import androidx.compose.material.icons.automirrored.filled.Logout
-import androidx.compose.material.icons.filled.Key
-import androidx.compose.material.icons.filled.ScreenLockLandscape
-import androidx.compose.material.icons.filled.SmartScreen
-import androidx.compose.material3.AlertDialog
-import androidx.compose.material3.Button
 import androidx.compose.material3.Icon
 import androidx.compose.material3.IconButton
 import androidx.compose.material3.MaterialTheme
-import androidx.compose.material3.OutlinedTextField
-import androidx.compose.material3.Text
-import androidx.compose.material3.TextButton
 import androidx.compose.runtime.Composable
-import androidx.compose.runtime.MutableState
 import androidx.compose.runtime.collectAsState
 import androidx.compose.runtime.getValue
-import androidx.compose.runtime.mutableStateOf
-import androidx.compose.runtime.setValue
-import androidx.compose.ui.Alignment
-import androidx.compose.ui.Modifier
-import androidx.compose.ui.focus.FocusDirection
-import androidx.compose.ui.platform.LocalFocusManager
-import androidx.compose.ui.text.input.ImeAction
-import androidx.compose.ui.text.input.KeyboardType
-import androidx.compose.ui.unit.dp
 import cafe.adriel.voyager.core.model.ScreenModel
 import cafe.adriel.voyager.core.model.screenModelScope
 import cafe.adriel.voyager.navigator.Navigator
@@ -39,18 +15,13 @@ import kotlinx.coroutines.launch
 import net.novagamestudios.common_utils.Logger
 import net.novagamestudios.common_utils.compose.components.CircularLoadingBox
 import net.novagamestudios.kaffeekasse.app
-import net.novagamestudios.kaffeekasse.model.credentials.DeviceCredentials
-import net.novagamestudios.kaffeekasse.model.credentials.isValid
 import net.novagamestudios.kaffeekasse.model.session.Session
 import net.novagamestudios.kaffeekasse.repositories.LoginRepository
 import net.novagamestudios.kaffeekasse.repositories.RepositoryProvider
 import net.novagamestudios.kaffeekasse.repositories.i11.PortalRepository
-import net.novagamestudios.kaffeekasse.ui.AppInfoTopBarAction
-import net.novagamestudios.kaffeekasse.ui.FullscreenIconButton
 import net.novagamestudios.kaffeekasse.ui.login.UserSelectionScreenModel.Companion.backNavigationHandler
 import net.novagamestudios.kaffeekasse.ui.navigation.LoginNavigation
 import net.novagamestudios.kaffeekasse.ui.util.navigation.BackNavigationHandler
-import net.novagamestudios.kaffeekasse.ui.util.rememberSerializableState
 import net.novagamestudios.kaffeekasse.ui.util.screenmodel.ScreenModelFactory
 import net.novagamestudios.kaffeekasse.ui.util.screenmodel.collectAsStateHere
 
@@ -64,15 +35,7 @@ class LoginScreenModel private constructor(
 
     val isLoading by loginRepository.isPerformingAction.collectAsStateHere()
 
-    var showLoginDeviceDialog by mutableStateOf(false)
-
-
-    fun loginDevice(deviceCredentials: DeviceCredentials) {
-        screenModelScope.launch {
-            loginRepository.loginDevice(deviceCredentials)
-            showLoginDeviceDialog = false
-        }
-    }
+    val loginDeviceState = LoginDeviceState(screenModelScope, loginRepository)
 
     companion object : ScreenModelFactory<LoginNavigation.Companion, LoginScreenModel> {
         context (RepositoryProvider)
@@ -99,100 +62,16 @@ fun LoginAdditional(
             if (session !is Session.WithDevice) navigator.replace(LoginNavigation.FormScreen)
         }
     }
-    if (model.showLoginDeviceDialog) LoginDeviceDialog(model)
-}
-
-
-@Composable
-private fun LoginDeviceDialog(
-    model: LoginScreenModel,
-    modifier: Modifier = Modifier
-) {
-    val inputState = rememberSerializableState {
-        mutableStateOf(DeviceCredentials.Empty)
-    }
-    val input by inputState
-    AlertDialog(
-        onDismissRequest = { model.showLoginDeviceDialog = false },
-        confirmButton = {
-            Button(
-                onClick = { model.loginDevice(input) },
-                enabled = !model.isLoading && input.isValid
-            ) {
-                Text("Einloggen")
-            }
-        },
-        modifier,
-        dismissButton = {
-            TextButton(
-                onClick = { model.showLoginDeviceDialog = false },
-                enabled = !model.isLoading
-            ) {
-                Text("Abbrechen")
-            }
-        },
-        text = {
-            CircularLoadingBox(loading = model.isLoading) {
-                LoginDeviceForm(
-                    inputState = inputState,
-                    onDone = { model.loginDevice(input) }
-                )
-            }
-        }
-    )
+    LoginDeviceAdditional(model.loginDeviceState)
 }
 
-@Composable
-private fun LoginDeviceForm(
-    inputState: MutableState<DeviceCredentials>,
-    onDone: () -> Unit,
-    modifier: Modifier = Modifier
-) {
-    var input by inputState
-    Column(
-        modifier.width(IntrinsicSize.Min),
-        verticalArrangement = Arrangement.spacedBy(8.dp),
-        horizontalAlignment = Alignment.CenterHorizontally
-    ) {
-        val focusManager = LocalFocusManager.current
-        OutlinedTextField(
-            value = input.deviceId,
-            onValueChange = { input = input.copy(deviceId = it.uppercase()) },
-            label = { Text("Geräte-Id") },
-            placeholder = { Text("XX:XX:XX:XX:XX:XX") },
-            leadingIcon = { Icon(Icons.Default.SmartScreen, "Geräte-Id") },
-            keyboardOptions = KeyboardOptions(
-                keyboardType = KeyboardType.Text,
-                imeAction = ImeAction.Next
-            ),
-            keyboardActions = KeyboardActions(onNext = { focusManager.moveFocus(FocusDirection.Next) }),
-            singleLine = true
-        )
-        OutlinedTextField(
-            value = input.apiKey,
-            onValueChange = { input = input.copy(apiKey = it) },
-            label = { Text("API-Schlüssel") },
-            leadingIcon = { Icon(Icons.Default.Key, "API-Schlüssel") },
-            keyboardOptions = KeyboardOptions(
-                keyboardType = KeyboardType.Password,
-                imeAction = ImeAction.Done
-            ),
-            keyboardActions = KeyboardActions(onDone = { onDone() }),
-            singleLine = true
-        )
-    }
-}
-
-
 @Composable
 fun LoginTopBarTitle(
     model: LoginScreenModel,
     navigator: Navigator
 ) {
     when (val screen = navigator.lastItem) {
-        is LoginNavigation.UserSelectionScreen -> {
-            UserSelectionTopBarTitle(screen.model, navigator)
-        }
+        is LoginNavigation.UserSelectionScreen -> UserSelectionTopBarTitle(screen.model)
     }
 }
 
@@ -202,20 +81,9 @@ fun LoginTopBarActions(
     navigator: Navigator
 ) {
     when (val screen = navigator.lastItem) {
-        is LoginNavigation.FormScreen -> {
-            AppInfoTopBarAction()
-            IconButton(
-                onClick = { model.showLoginDeviceDialog = true },
-                enabled = !model.isLoading
-            ) {
-                Icon(Icons.Default.ScreenLockLandscape, "Gerät einloggen")
-            }
-        }
-        is LoginNavigation.UserSelectionScreen -> {
-            UserSelectionTopBarActions(screen.model, navigator)
-        }
+        is LoginNavigation.FormScreen -> LoginFormTopBarActions(model.loginDeviceState)
+        is LoginNavigation.UserSelectionScreen -> UserSelectionTopBarActions(screen.model, model.loginDeviceState)
     }
-    FullscreenIconButton()
 }
 
 @Composable
@@ -226,15 +94,12 @@ fun LoginScreenModel.backNavigationHandler(navigator: Navigator): BackNavigation
     }
 }
 
-
 @Composable
-fun LogoutTopBarAction(session: Session) {
-    val app = app()
-    val loginRepository = app.loginRepository
+fun LogoutTopBarAction(session: Session) = with(app()) {
     val isLoading by loginRepository.isPerformingAction.collectAsState()
     CircularLoadingBox(loading = isLoading) {
         IconButton(
-            onClick = { app.launch { loginRepository.logoutUser() } },
+            onClick = { launch { loginRepository.logoutUser() } },
             enabled = session !is Session.Empty
         ) {
             Icon(
diff --git a/app/src/main/java/net/novagamestudios/kaffeekasse/ui/login/LoginDevice.kt b/app/src/main/java/net/novagamestudios/kaffeekasse/ui/login/LoginDevice.kt
new file mode 100644
index 0000000000000000000000000000000000000000..51dc7ec6517719a6dda5e48d64fdb35f17d0c21d
--- /dev/null
+++ b/app/src/main/java/net/novagamestudios/kaffeekasse/ui/login/LoginDevice.kt
@@ -0,0 +1,221 @@
+package net.novagamestudios.kaffeekasse.ui.login
+
+import androidx.compose.foundation.layout.Arrangement
+import androidx.compose.foundation.layout.Column
+import androidx.compose.foundation.layout.IntrinsicSize
+import androidx.compose.foundation.layout.width
+import androidx.compose.foundation.text.KeyboardActions
+import androidx.compose.foundation.text.KeyboardOptions
+import androidx.compose.material.icons.Icons
+import androidx.compose.material.icons.filled.Key
+import androidx.compose.material.icons.filled.PhonelinkErase
+import androidx.compose.material.icons.filled.ScreenLockLandscape
+import androidx.compose.material.icons.filled.SmartScreen
+import androidx.compose.material3.AlertDialog
+import androidx.compose.material3.Button
+import androidx.compose.material3.Icon
+import androidx.compose.material3.IconButton
+import androidx.compose.material3.OutlinedTextField
+import androidx.compose.material3.Text
+import androidx.compose.material3.TextButton
+import androidx.compose.runtime.Composable
+import androidx.compose.runtime.MutableState
+import androidx.compose.runtime.getValue
+import androidx.compose.runtime.mutableStateOf
+import androidx.compose.runtime.remember
+import androidx.compose.runtime.setValue
+import androidx.compose.ui.Alignment
+import androidx.compose.ui.Modifier
+import androidx.compose.ui.focus.FocusDirection
+import androidx.compose.ui.platform.LocalFocusManager
+import androidx.compose.ui.text.input.ImeAction
+import androidx.compose.ui.text.input.KeyboardType
+import androidx.compose.ui.unit.dp
+import kotlinx.coroutines.CoroutineScope
+import kotlinx.coroutines.launch
+import net.novagamestudios.common_utils.compose.components.CircularLoadingBox
+import net.novagamestudios.common_utils.compose.state.collectAsStateIn
+import net.novagamestudios.kaffeekasse.model.credentials.DeviceCredentials
+import net.novagamestudios.kaffeekasse.model.credentials.isValid
+import net.novagamestudios.kaffeekasse.repositories.LoginRepository
+import net.novagamestudios.kaffeekasse.ui.util.rememberSerializableState
+
+
+class LoginDeviceState(
+    private val coroutineScope: CoroutineScope,
+    private val loginRepository: LoginRepository
+) {
+    val isLoading by loginRepository.isPerformingAction.collectAsStateIn(coroutineScope)
+
+    var showDialog by mutableStateOf(false)
+
+    fun login(deviceCredentials: DeviceCredentials) {
+        coroutineScope.launch {
+            loginRepository.loginDevice(deviceCredentials)
+            showDialog = false
+        }
+    }
+
+    fun logout() {
+        coroutineScope.launch {
+            loginRepository.logoutDevice()
+        }
+    }
+}
+
+@Composable
+fun LoginDeviceAdditional(
+    state: LoginDeviceState,
+    modifier: Modifier = Modifier
+) {
+    if (state.showDialog) LoginDeviceDialog(
+        onDismiss = { state.showDialog = false },
+        onLogin = { state.login(it) },
+        isLoading = state.isLoading,
+        modifier
+    )
+}
+
+@Composable
+private fun LoginDeviceDialog(
+    onDismiss: () -> Unit,
+    onLogin: (DeviceCredentials) -> Unit,
+    isLoading: Boolean,
+    modifier: Modifier = Modifier
+) {
+    val inputState = rememberSerializableState {
+        mutableStateOf(DeviceCredentials.Empty)
+    }
+    val input by inputState
+    AlertDialog(
+        onDismissRequest = onDismiss,
+        confirmButton = {
+            Button(
+                onClick = { onLogin(input) },
+                enabled = !isLoading && input.isValid
+            ) {
+                Text("Einloggen")
+            }
+        },
+        modifier,
+        dismissButton = {
+            TextButton(
+                onClick = onDismiss,
+                enabled = !isLoading
+            ) {
+                Text("Abbrechen")
+            }
+        },
+        text = {
+            CircularLoadingBox(loading = isLoading) {
+                LoginDeviceForm(
+                    inputState = inputState,
+                    onDone = { onLogin(input) }
+                )
+            }
+        }
+    )
+}
+
+@Composable
+private fun LoginDeviceForm(
+    inputState: MutableState<DeviceCredentials>,
+    onDone: () -> Unit,
+    modifier: Modifier = Modifier
+) {
+    var input by inputState
+    Column(
+        modifier.width(IntrinsicSize.Min),
+        verticalArrangement = Arrangement.spacedBy(8.dp),
+        horizontalAlignment = Alignment.CenterHorizontally
+    ) {
+        val focusManager = LocalFocusManager.current
+        OutlinedTextField(
+            value = input.deviceId,
+            onValueChange = { input = input.copy(deviceId = it.uppercase()) },
+            label = { Text("Geräte-Id") },
+            placeholder = { Text("XX:XX:XX:XX:XX:XX") },
+            leadingIcon = { Icon(Icons.Default.SmartScreen, "Geräte-Id") },
+            keyboardOptions = KeyboardOptions(
+                keyboardType = KeyboardType.Text,
+                imeAction = ImeAction.Next
+            ),
+            keyboardActions = KeyboardActions(onNext = { focusManager.moveFocus(FocusDirection.Next) }),
+            singleLine = true
+        )
+        OutlinedTextField(
+            value = input.apiKey,
+            onValueChange = { input = input.copy(apiKey = it) },
+            label = { Text("API-Schlüssel") },
+            leadingIcon = { Icon(Icons.Default.Key, "API-Schlüssel") },
+            keyboardOptions = KeyboardOptions(
+                keyboardType = KeyboardType.Password,
+                imeAction = ImeAction.Done
+            ),
+            keyboardActions = KeyboardActions(onDone = { onDone() }),
+            singleLine = true
+        )
+    }
+}
+
+
+@Composable
+fun LoginDeviceButton(
+    state: LoginDeviceState,
+    modifier: Modifier = Modifier
+) = IconButton(
+    onClick = { state.showDialog = true },
+    modifier,
+    enabled = !state.isLoading
+) {
+    Icon(Icons.Default.ScreenLockLandscape, "Gerät einloggen")
+}
+
+@Composable
+fun LogoutDeviceButton(
+    state: LoginDeviceState,
+    modifier: Modifier = Modifier
+) {
+    var confirmLogout by remember { mutableStateOf(false) }
+    IconButton(
+        onClick = { confirmLogout = true },
+        modifier,
+        enabled = !state.isLoading
+    ) {
+        Icon(Icons.Default.PhonelinkErase, "Gerät ausloggen")
+    }
+    if (confirmLogout) ConfirmLogoutDeviceDialog(
+        onDismiss = { confirmLogout = false },
+        onConfirm = { state.logout() }
+    )
+}
+
+@Composable
+private fun ConfirmLogoutDeviceDialog(
+    onDismiss: () -> Unit,
+    onConfirm: () -> Unit,
+    modifier: Modifier = Modifier
+) = AlertDialog(
+    onDismissRequest = onDismiss,
+    confirmButton = {
+        Button(
+            onClick = onConfirm
+        ) {
+            Text("Ausloggen")
+        }
+    },
+    modifier,
+    dismissButton = {
+        TextButton(
+            onClick = onDismiss
+        ) {
+            Text("Abbrechen")
+        }
+    },
+    text = {
+        Text("Möchtest du das Gerät wirklich ausloggen?")
+    }
+)
+
+
+
diff --git a/app/src/main/java/net/novagamestudios/kaffeekasse/ui/login/LoginForm.kt b/app/src/main/java/net/novagamestudios/kaffeekasse/ui/login/LoginForm.kt
index 8b91c3152d0736c82ab230ebb025be3fde560197..10e7d5dc297650bc3d71d33e3c227b0243596fd1 100644
--- a/app/src/main/java/net/novagamestudios/kaffeekasse/ui/login/LoginForm.kt
+++ b/app/src/main/java/net/novagamestudios/kaffeekasse/ui/login/LoginForm.kt
@@ -63,10 +63,12 @@ import net.novagamestudios.kaffeekasse.repositories.LoginRepository
 import net.novagamestudios.kaffeekasse.repositories.RepositoryProvider
 import net.novagamestudios.kaffeekasse.repositories.SettingsRepository
 import net.novagamestudios.kaffeekasse.repositories.i11.PortalRepository
+import net.novagamestudios.kaffeekasse.ui.AppInfoTopBarAction
+import net.novagamestudios.kaffeekasse.ui.FullscreenIconButton
 import net.novagamestudios.kaffeekasse.ui.kaffeekasse.components.FailureRetryScreen
 import net.novagamestudios.kaffeekasse.ui.navigation.LoginNavigation
-import net.novagamestudios.kaffeekasse.ui.util.screenmodel.ScreenModelFactory
 import net.novagamestudios.kaffeekasse.ui.util.rememberSerializableState
+import net.novagamestudios.kaffeekasse.ui.util.screenmodel.ScreenModelFactory
 import net.novagamestudios.kaffeekasse.ui.util.screenmodel.collectAsStateHere
 
 
@@ -238,3 +240,13 @@ fun Modifier.autofill(
         }
 }
 
+
+@Composable
+fun LoginFormTopBarActions(
+    loginDeviceState: LoginDeviceState
+) {
+    AppInfoTopBarAction()
+    LoginDeviceButton(loginDeviceState)
+    FullscreenIconButton()
+}
+
diff --git a/app/src/main/java/net/novagamestudios/kaffeekasse/ui/login/UserSelection.kt b/app/src/main/java/net/novagamestudios/kaffeekasse/ui/login/UserSelection.kt
index ad48a2a9a067dbadfe24e8e9b8f6de374a3243d3..70ace73990cb4d27c8a7ccd23e13a4c6345e98b4 100644
--- a/app/src/main/java/net/novagamestudios/kaffeekasse/ui/login/UserSelection.kt
+++ b/app/src/main/java/net/novagamestudios/kaffeekasse/ui/login/UserSelection.kt
@@ -26,13 +26,11 @@ import androidx.compose.material.icons.Icons
 import androidx.compose.material.icons.filled.ChevronRight
 import androidx.compose.material.icons.filled.Lock
 import androidx.compose.material.icons.filled.Password
-import androidx.compose.material.icons.filled.PhonelinkErase
 import androidx.compose.material3.AlertDialog
 import androidx.compose.material3.Button
 import androidx.compose.material3.DividerDefaults
 import androidx.compose.material3.HorizontalDivider
 import androidx.compose.material3.Icon
-import androidx.compose.material3.IconButton
 import androidx.compose.material3.MaterialTheme
 import androidx.compose.material3.OutlinedCard
 import androidx.compose.material3.OutlinedTextField
@@ -86,6 +84,7 @@ import net.novagamestudios.kaffeekasse.repositories.LoginRepository
 import net.novagamestudios.kaffeekasse.repositories.RepositoryProvider
 import net.novagamestudios.kaffeekasse.repositories.i11.KaffeekasseRepository
 import net.novagamestudios.kaffeekasse.ui.AppInfoTopBarAction
+import net.novagamestudios.kaffeekasse.ui.FullscreenIconButton
 import net.novagamestudios.kaffeekasse.ui.handleSession
 import net.novagamestudios.kaffeekasse.ui.navigation.LoginNavigation
 import net.novagamestudios.kaffeekasse.ui.util.AlphabetSelectionChar
@@ -171,12 +170,6 @@ class UserSelectionScreenModel private constructor(
         loginUser(dialog.user, dialog.auth)
     }
 
-    fun logoutDevice() {
-        screenModelScope.launch {
-            loginRepository.logoutDevice()
-        }
-    }
-
     companion object : ScreenModelFactory<LoginNavigation.UserSelectionScreen, UserSelectionScreenModel> {
         context (RepositoryProvider)
         override fun create(screen: LoginNavigation.UserSelectionScreen) = UserSelectionScreenModel(
@@ -538,8 +531,7 @@ private fun UserAuthDialog(
 
 @Composable
 fun UserSelectionTopBarTitle(
-    model: UserSelectionScreenModel,
-    navigator: Navigator
+    model: UserSelectionScreenModel
 ) {
     Row(verticalAlignment = Alignment.CenterVertically) {
         if (model.searchState.show) {
@@ -559,21 +551,12 @@ fun UserSelectionTopBarTitle(
 @Composable
 fun UserSelectionTopBarActions(
     model: UserSelectionScreenModel,
-    navigator: Navigator
+    loginDeviceState: LoginDeviceState
 ) {
     TopBarSearchAction(model.searchState, alwaysShow = true)
     AppInfoTopBarAction()
-    var confirmLogout by remember { mutableStateOf(false) }
-    IconButton(
-        onClick = { confirmLogout = true },
-        enabled = !model.isPerformingLoginAction
-    ) {
-        Icon(Icons.Default.PhonelinkErase, "Gerät ausloggen")
-    }
-    if (confirmLogout) ConfirmLogoutDeviceDialog(
-        onDismiss = { confirmLogout = false },
-        onConfirm = { model.logoutDevice() }
-    )
+    LogoutDeviceButton(loginDeviceState)
+    FullscreenIconButton()
 }
 
 
@@ -598,30 +581,3 @@ private fun DeviceInfo(
     }
 }
 
-@Composable
-private fun ConfirmLogoutDeviceDialog(
-    onDismiss: () -> Unit,
-    onConfirm: () -> Unit,
-    modifier: Modifier = Modifier
-) = AlertDialog(
-    onDismissRequest = onDismiss,
-    confirmButton = {
-        Button(
-            onClick = onConfirm
-        ) {
-            Text("Ausloggen")
-        }
-    },
-    modifier,
-    dismissButton = {
-        TextButton(
-            onClick = onDismiss
-        ) {
-            Text("Abbrechen")
-        }
-    },
-    text = {
-        Text("Möchtest du das Gerät wirklich ausloggen?")
-    }
-)
-