diff --git a/.cla-accepted b/.cla-accepted
index 722b0d023debe2a17be57a7b4e81d012356b0b41..1669034493f181e528e1f3c688f4f3cfc307ad0d 100644
--- a/.cla-accepted
+++ b/.cla-accepted
@@ -4,3 +4,4 @@
 @akwizgran (Michael Rogers)
 @paul-lorenc (Paul Lorenc)
 @ialokim (Mikolai Gütschow)
+@altynbek.nurtaza (Altynbek Nurtaza)
diff --git a/src/main/kotlin/androidx/compose/material/OutlinedTextFieldExt.kt b/src/main/kotlin/androidx/compose/material/OutlinedTextFieldExt.kt
index e5ad6c096ac7f0536da60f4f4068a38363d219f5..d2defb118f2a555ff0f74e30b06fb92be6f03e3f 100644
--- a/src/main/kotlin/androidx/compose/material/OutlinedTextFieldExt.kt
+++ b/src/main/kotlin/androidx/compose/material/OutlinedTextFieldExt.kt
@@ -27,6 +27,9 @@ import androidx.compose.foundation.text.KeyboardOptions
 import androidx.compose.material.InitialFocusState.AFTER_FIRST_FOCUSSED
 import androidx.compose.material.InitialFocusState.AFTER_FOCUS_LOST_ONCE
 import androidx.compose.material.InitialFocusState.FROM_START
+import androidx.compose.material.icons.Icons
+import androidx.compose.material.icons.filled.Visibility
+import androidx.compose.material.icons.filled.VisibilityOff
 import androidx.compose.runtime.Composable
 import androidx.compose.runtime.derivedStateOf
 import androidx.compose.runtime.getValue
@@ -43,9 +46,11 @@ import androidx.compose.ui.input.key.key
 import androidx.compose.ui.input.key.onPreviewKeyEvent
 import androidx.compose.ui.input.key.type
 import androidx.compose.ui.text.TextStyle
+import androidx.compose.ui.text.input.PasswordVisualTransformation
 import androidx.compose.ui.text.input.VisualTransformation
 import androidx.compose.ui.unit.dp
 import androidx.compose.ui.unit.sp
+import org.briarproject.briar.desktop.utils.InternationalizationUtils.i18n
 
 /**
  * Material Design outlined text field with extended support for error and helper messages,
@@ -142,3 +147,101 @@ fun OutlinedTextField(
 }
 
 enum class InitialFocusState { FROM_START, AFTER_FIRST_FOCUSSED, AFTER_FOCUS_LOST_ONCE }
+
+/**
+ * Material Design outlined text field with support for error and helper messages,
+ * as well as for handling the Enter key. The difference with [OutlinedTextField] is
+ * that it shows the visibility icons instead of error icons in the error state.
+ * All parameters not specified here are the same as on the original [OutlinedTextField].
+ *
+ * @param visualTransformation Visual transformation is only applied when password is hidden
+ */
+@OptIn(ExperimentalComposeUiApi::class)
+@Composable
+fun OutlinedPasswordTextField(
+    value: String,
+    onValueChange: (String) -> Unit,
+    modifier: Modifier = Modifier,
+    onEnter: () -> Unit = {},
+    enabled: Boolean = true,
+    readOnly: Boolean = false,
+    textStyle: TextStyle = LocalTextStyle.current,
+    label: @Composable (() -> Unit)? = null,
+    placeholder: @Composable (() -> Unit)? = null,
+    leadingIcon: @Composable (() -> Unit)? = null,
+    helperMessage: String? = null,
+    errorMessage: String? = null,
+    isError: Boolean = false,
+    showErrorWhen: InitialFocusState = FROM_START,
+    visualTransformation: VisualTransformation = PasswordVisualTransformation(),
+    keyboardOptions: KeyboardOptions = KeyboardOptions.Default,
+    singleLine: Boolean = false,
+    maxLines: Int = Int.MAX_VALUE,
+    interactionSource: MutableInteractionSource = remember { MutableInteractionSource() },
+    shape: Shape = MaterialTheme.shapes.small,
+    colors: TextFieldColors = TextFieldDefaults.outlinedTextFieldColors()
+) {
+    var isPasswordVisible by remember { mutableStateOf(false) }
+
+    OutlinedTextField(
+        value = value,
+        onValueChange = onValueChange,
+        modifier = modifier,
+        onEnter = onEnter,
+        enabled = enabled,
+        readOnly = readOnly,
+        textStyle = textStyle,
+        label = label,
+        placeholder = placeholder,
+        leadingIcon = leadingIcon,
+        trailingIcon = {
+            ShowHidePasswordIcon(
+                isVisible = isPasswordVisible,
+                toggleIsVisible = {
+                    isPasswordVisible = !isPasswordVisible
+                },
+            )
+        },
+        errorIcon = {
+            ShowHidePasswordIcon(
+                isVisible = isPasswordVisible,
+                toggleIsVisible = {
+                    isPasswordVisible = !isPasswordVisible
+                },
+            )
+        },
+        helperMessage = helperMessage,
+        errorMessage = errorMessage,
+        isError = isError,
+        showErrorWhen = showErrorWhen,
+        visualTransformation = if (!isPasswordVisible) visualTransformation else VisualTransformation.None,
+        keyboardOptions = keyboardOptions,
+        singleLine = singleLine,
+        maxLines = maxLines,
+        interactionSource = interactionSource,
+        shape = shape,
+        colors = colors,
+    )
+}
+
+@Composable
+private fun ShowHidePasswordIcon(
+    isVisible: Boolean,
+    toggleIsVisible: () -> Unit,
+) {
+    IconButton(
+        onClick = toggleIsVisible
+    ) {
+        if (isVisible) {
+            Icon(
+                imageVector = Icons.Filled.VisibilityOff,
+                contentDescription = i18n("access.password.show"),
+            )
+        } else {
+            Icon(
+                imageVector = Icons.Filled.Visibility,
+                contentDescription = i18n("access.password.hide"),
+            )
+        }
+    }
+}
diff --git a/src/main/kotlin/org/briarproject/briar/desktop/login/LoginScreen.kt b/src/main/kotlin/org/briarproject/briar/desktop/login/LoginScreen.kt
index f1413a50ad4354c9688b4dff5fadecda576ff912..abdb0fd68a26b29a8da868df8622c48d646ee194 100644
--- a/src/main/kotlin/org/briarproject/briar/desktop/login/LoginScreen.kt
+++ b/src/main/kotlin/org/briarproject/briar/desktop/login/LoginScreen.kt
@@ -26,7 +26,7 @@ import androidx.compose.material.ButtonType.DESTRUCTIVE
 import androidx.compose.material.ButtonType.NEUTRAL
 import androidx.compose.material.DialogButton
 import androidx.compose.material.ExperimentalMaterialApi
-import androidx.compose.material.OutlinedTextField
+import androidx.compose.material.OutlinedPasswordTextField
 import androidx.compose.material.Text
 import androidx.compose.material.TextButton
 import androidx.compose.runtime.Composable
@@ -38,7 +38,6 @@ import androidx.compose.ui.focus.FocusRequester
 import androidx.compose.ui.focus.focusRequester
 import androidx.compose.ui.text.input.ImeAction
 import androidx.compose.ui.text.input.KeyboardType
-import androidx.compose.ui.text.input.PasswordVisualTransformation
 import androidx.compose.ui.unit.dp
 import org.briarproject.briar.desktop.login.LoginSubViewModel.State.COMPACTING
 import org.briarproject.briar.desktop.login.LoginSubViewModel.State.MIGRATING
@@ -110,14 +109,13 @@ fun LoginForm(
     val initialFocusRequester = remember { FocusRequester() }
     val passwordForgotten = remember { mutableStateOf(false) }
 
-    OutlinedTextField(
+    OutlinedPasswordTextField(
         value = password,
         onValueChange = setPassword,
         label = { Text(i18n("startup.field.password")) },
         singleLine = true,
         isError = passwordInvalidError,
         errorMessage = i18n("startup.error.password_wrong"),
-        visualTransformation = PasswordVisualTransformation(),
         keyboardOptions = KeyboardOptions(keyboardType = KeyboardType.Password, imeAction = ImeAction.Next),
         modifier = Modifier.fillMaxWidth().focusRequester(initialFocusRequester),
         onEnter = onEnter
diff --git a/src/main/kotlin/org/briarproject/briar/desktop/login/RegistrationScreen.kt b/src/main/kotlin/org/briarproject/briar/desktop/login/RegistrationScreen.kt
index d223cb9b17996e26bb25c14dc292d13f2a517c9b..0397637debd567e2987a3f37a0dbf2790fffcfba 100644
--- a/src/main/kotlin/org/briarproject/briar/desktop/login/RegistrationScreen.kt
+++ b/src/main/kotlin/org/briarproject/briar/desktop/login/RegistrationScreen.kt
@@ -24,6 +24,7 @@ import androidx.compose.foundation.layout.requiredHeight
 import androidx.compose.foundation.text.KeyboardOptions
 import androidx.compose.material.InitialFocusState.AFTER_FIRST_FOCUSSED
 import androidx.compose.material.InitialFocusState.AFTER_FOCUS_LOST_ONCE
+import androidx.compose.material.OutlinedPasswordTextField
 import androidx.compose.material.OutlinedTextField
 import androidx.compose.material.Text
 import androidx.compose.runtime.Composable
@@ -37,7 +38,6 @@ import androidx.compose.ui.focus.focusRequester
 import androidx.compose.ui.platform.LocalFocusManager
 import androidx.compose.ui.text.input.ImeAction
 import androidx.compose.ui.text.input.KeyboardType
-import androidx.compose.ui.text.input.PasswordVisualTransformation
 import androidx.compose.ui.unit.dp
 import org.briarproject.briar.desktop.login.RegistrationSubViewModel.State.CREATED
 import org.briarproject.briar.desktop.login.RegistrationSubViewModel.State.CREATING
@@ -140,7 +140,7 @@ fun PasswordForm(
         if (password.isNotEmpty())
             StrengthMeter(passwordStrength, Modifier.fillMaxWidth())
     }
-    OutlinedTextField(
+    OutlinedPasswordTextField(
         value = password,
         onValueChange = setPassword,
         label = { Text(i18n("startup.field.password")) },
@@ -148,12 +148,11 @@ fun PasswordForm(
         isError = passwordTooWeakError,
         showErrorWhen = AFTER_FOCUS_LOST_ONCE,
         errorMessage = i18n("startup.error.password_too_weak"),
-        visualTransformation = PasswordVisualTransformation(),
         keyboardOptions = KeyboardOptions(keyboardType = KeyboardType.Password, imeAction = ImeAction.Next),
         modifier = Modifier.fillMaxWidth().focusRequester(initialFocusRequester),
-        onEnter = { focusManager.moveFocus(FocusDirection.Next) }
+        onEnter = { focusManager.moveFocus(FocusDirection.Next) },
     )
-    OutlinedTextField(
+    OutlinedPasswordTextField(
         value = passwordConfirmation,
         onValueChange = setPasswordConfirmation,
         label = { Text(i18n("startup.field.password_confirmation")) },
@@ -161,7 +160,6 @@ fun PasswordForm(
         isError = passwordsDontMatchError,
         showErrorWhen = AFTER_FIRST_FOCUSSED,
         errorMessage = i18n("startup.error.passwords_not_match"),
-        visualTransformation = PasswordVisualTransformation(),
         keyboardOptions = KeyboardOptions(keyboardType = KeyboardType.Password, imeAction = ImeAction.Done),
         modifier = Modifier.fillMaxWidth(),
         onEnter = onEnter,