Avoid password prompts for credential fields

Render API keys, access tokens, client secrets, and similar non-password credentials as masked text inputs so browsers do not offer to save them as passwords. Preserve readable placeholders and reveal controls while leaving genuine password fields unchanged.

Alessandro committed Aug 26, 2026 at 22:09 UTC 64427163b5a5ab3c16386914521602a075f5a61b
8 files changed +20 -12
plugins/_model_config/webui/api-keys.html
+2 -1
@@ -70,7 +70,8 @@
70 <div class="field-title" x-text="provider.label"></div>
71 </div>
72 <div class="field-control" style="position:relative;" x-data="{ showKey: false }">
73 - <input :type="showKey ? 'text' : 'password'"
73 + <input type="text"
74 + :class="{ 'masked-text-input': !showKey }"
75 :value="$store.modelConfig.apiKeyValues[provider.value]"
76 :placeholder="provider.has_key ? '••••••••••••' : ''"
77 autocomplete="off"
plugins/_model_config/webui/model-field.html
+3 -2
@@ -86,7 +86,8 @@
86 <div class="field-description">Authentication key for this provider. Shared across all model slots using the same provider.</div>
87 </div>
88 <div class="field-control" style="position:relative;" x-data="{ showKey: false }">
89 - <input :type="showKey ? 'text' : 'password'"
89 + <input type="text"
90 + :class="{ 'masked-text-input': !showKey }"
91 :value="$store.modelConfig.apiKeyValues[_prov]"
92 :placeholder="$store.modelConfig.apiKeyStatus[_prov] ? '&#x2022;&#x2022;&#x2022;&#x2022;&#x2022;&#x2022;&#x2022;&#x2022;&#x2022;&#x2022;&#x2022;&#x2022;' : ''"
93 autocomplete="off"
@@ -113,7 +114,7 @@
114 <div class="field-description">Authentication key for this provider. Shared across all model slots using the same provider.</div>
115 </div>
116 <div class="field-control" style="position:relative;" x-data="{ showKey: false, _revealed: '' }">
116 - <input :type="showKey ? 'text' : 'password'" x-model="model.api_key" autocomplete="off"
117 + <input type="text" :class="{ 'masked-text-input': !showKey }" x-model="model.api_key" autocomplete="off"
118 :placeholder="$store.modelConfig.apiKeyStatus[_prov] ? '&#x2022;&#x2022;&#x2022;&#x2022;&#x2022;&#x2022;&#x2022;&#x2022;&#x2022;&#x2022;&#x2022;&#x2022;' : ''"
119 style="padding-right:32px;" />
120 <x-icon class="eye-toggle"
plugins/_oauth/webui/config.html
+3 -2
@@ -106,7 +106,8 @@
106 <label class="oauth-provider-input">
107 <span>OAuth client secret</span>
108 <input
109 - type="password"
109 + type="text"
110 + class="masked-text-input"
111 x-model="$store.oauthConfig.providerUiFor(card.provider_id).client_secret"
112 name="oauth_client_secret"
113 autocomplete="off"
@@ -369,7 +370,7 @@
370 </label>
371 <label>
372 <span>Codex proxy token</span>
372 - <input type="password" x-model="$store.oauthConfig.codex().proxy_token" autocomplete="off" />
373 + <input type="text" class="masked-text-input" x-model="$store.oauthConfig.codex().proxy_token" autocomplete="off" />
374 </label>
375 </div>
376 </details>
plugins/_onboarding/webui/onboarding.html
+3 -2
@@ -773,7 +773,7 @@
773 </label>
774 <label class="field">
775 <span>OAuth client secret</span>
776 - <input type="password" autocomplete="off" x-model="$store.onboarding.oauthProviderUiFor($store.onboarding.selectedProviderId).client_secret" placeholder="Google Cloud OAuth client secret">
776 + <input type="text" class="masked-text-input" autocomplete="off" x-model="$store.onboarding.oauthProviderUiFor($store.onboarding.selectedProviderId).client_secret" placeholder="Google Cloud OAuth client secret">
777 </label>
778 <label class="field" x-show="$store.onboarding.oauthProviderStatus($store.onboarding.selectedProviderId)?.supports_quota_project">
779 <span>Quota project</span>
@@ -800,7 +800,8 @@
800 API key <span x-show="$store.onboarding.providerKeyOptional($store.onboarding.selectedProviderId)">(optional)</span>
801 </label>
802 <input id="onboarding-api-key"
803 - type="password"
803 + type="text"
804 + class="masked-text-input"
805 autocomplete="off"
806 x-model="$store.modelConfig.apiKeyValues[$store.onboarding.selectedProviderId]"
807 :placeholder="$store.modelConfig.apiKeyStatus[$store.onboarding.selectedProviderId] ? 'Key already saved' : 'Paste your API key'"
plugins/_plugin_installer/webui/install-git.html
+1 -1
@@ -20,7 +20,7 @@
20
21 <div class="pi-form-group">
22 <label class="pi-label">Access Token <span class="pi-optional">(optional, for private repos)</span></label>
23 - <input type="password" class="pi-input"
23 + <input type="text" class="pi-input masked-text-input" autocomplete="off"
24 x-model="$store.pluginInstallStore.gitToken"
25 :disabled="$store.pluginInstallStore.loading"
26 placeholder="ghp_xxxx or glpat-xxxx">
plugins/_telegram_integration/webui/config.html
+3 -3
@@ -85,7 +85,7 @@
85 <div class="field-description">Paste the token you got from @BotFather.</div>
86 </div>
87 <div class="field-control">
88 - <input type="password" x-model="bot.token"
88 + <input type="text" class="masked-text-input" autocomplete="off" x-model="bot.token"
89 placeholder="123456:ABC-DEF1234ghIkl-zyx57W2v1u123ew11" />
90 </div>
91 </div>
@@ -274,7 +274,7 @@
274 <div class="field-description">Optional shared secret for webhook verification.</div>
275 </div>
276 <div class="field-control">
277 - <input type="password" x-model="bot.webhook_secret" />
277 + <input type="text" class="masked-text-input" autocomplete="off" x-model="bot.webhook_secret" />
278 </div>
279 </div>
280
@@ -677,4 +677,4 @@
677 </style>
678 </body>
679
680 -</html>
\ No newline at end of file
680 +</html>
webui/components/projects/project-create.html
+1 -1
@@ -29,7 +29,7 @@
29 <div class="projects-form-group">
30 <label class="projects-form-label">Access Token (optional)</label>
31 <span class="projects-form-description">For private repositories. Token is used only for cloning and will not be stored.</span>
32 - <input class="projects-form-input" type="password"
32 + <input class="projects-form-input masked-text-input" type="text" autocomplete="off"
33 x-model="$store.projects.selectedProject.git_token"
34 :disabled="$store.projects.selectedProject._cloning"
35 placeholder="ghp_xxxx or glpat-xxxx">
webui/css/settings.css
+4
@@ -76,6 +76,10 @@ textarea:focus {
76 -webkit-text-security: disc;
77 }
78
79 +.masked-text-input::placeholder {
80 + -webkit-text-security: none;
81 +}
82 +
83 .light-mode input[type="text"]:focus,
84 .light-mode input[type="number"]:focus,
85 .light-mode input[type="password"]:focus,