Repository: syncthing/syncthing (language: go)

Issue:
Login into UI (Browser) not working anymore
### What happened?

Login using password is no longer working.

Seems I have the same problem as described here: https://forum.syncthing.net/t/login-keeps-failing/23573

If I enter wrong credentials, I get `Incorrect user name or password.`
LOG then shows: `Wrong credentials supplied during API authorization from 127.0.0.1:50860`

If I enter correct credentials, I get `Login failed, see Syncthing logs for details.`
No line gets added to the LOG.

When I delete the password from config, I can access the UI (without login of course).

It doesn't matter how I store the password (syncthing generate --gui-password or using the UI after removing it from config). I replace the password by something simple to avoid problems with special chars, but even `test123` resulted in the same issue.

I'm sure it worked before and I didn't change anything. I definitely had the correct password.
After getting this error, I've reset the password multiple times, updated to the latest version 1.29 (old was 1.27 I think). Nothing helped fix the problem.

My guess: Maybe something changed regarding encryption/decryption of the password (Windows update) causing a bug?

Note: I set `debugging` attribute to `true` in the <gui/> tag in config. Is there is anything else I can do to generate (more) log lines and help debug this issue?

### Syncthing version

v1.29.0 "Gold Grasshopper" (go1.23.4 windows-amd64) builder@github.syncthing.net 2024-12-30 03:46:20 UTC

### Platform & operating system

Windows 11

### Browser version

Chrome 131.0.6778.205 

### Relevant log output

```shell
[KAVAC] 2025/01/09 18:43:09 INFO: Wrong credentials supplied during API authorization from 127.0.0.1:50860
[KAVAC] 2025/01/09 18:43:13 INFO: Wrong credentials supplied during API authorization from 127.0.0.1:50860
[KAVAC] 2025/01/09 18:43:26 INFO: Wrong credentials supplied during API authorization from 127.0.0.1:50860
```



Discussion / hints:
A view from the browser console on what it think about the request would be helpful. You can also try to enable `api` tracing in Syncthing (`STTRACE=api`) to possibly get more information from that side.
Browser console:

```
loadFormIntoScope deviceEditor
syncthingController.js:2249 loadFormIntoScope folderEditor
syncthingController.js:2249 loadFormIntoScope deviceEditor
:8384/rest/noauth/auth/password:1 
        
        
       Failed to load resource: net::ERR_EMPTY_RESPONSE
syncthingController.js:123 Password authentication failed: Object
angular.js:9912 
        
        
       POST http://127.0.0.1:8384/rest/noauth/auth/password net::ERR_EMPTY_RESPONSE
(anonymous) @ angular.js:9912
sendReq @ angular.js:9713
serverRequest @ angular.js:9425
processQueue @ angular.js:13337
(anonymous) @ angular.js:13353
$eval @ angular.js:14589
$digest @ angular.js:14405
$apply @ angular.js:14694
(anonymous) @ angular.js:21724
dispatch @ jquery-2.2.2.js:4755
elemData.handle @ jquery-2.2.2.js:4567Understand this errorAI
syncthingController.js:123 Password authentication failed: {data: null, status: -1, config: {…}, statusText: '', headers: ƒ}
```

So the funny thing is, empty response is what we've always sent and it's expected. There's a cookie header, no content, and a `204 No Content` response code, which is a success code... 

This code changed last 12 months ago...
What I'm looking for is a view of the request in the network console like this, in the hope that it might shed some light on what is different in your setup:

<img width="1624" alt="Screenshot 2025-01-09 at 19 45 57" src="https://github.com/user-attachments/assets/d8c8d5e2-1268-45ef-b3e1-664bbb92b9e6" />


Fix patch:
diff --git a/lib/api/api.go b/lib/api/api.go
index 6205ae6203d..f09df33f3c0 100644
--- a/lib/api/api.go
+++ b/lib/api/api.go
@@ -402,6 +402,9 @@ func (s *service) Serve(ctx context.Context) error {
 		// care about we log ourselves from the handlers.
 		ErrorLog: log.New(io.Discard, "", 0),
 	}
+	if shouldDebugHTTP() {
+		srv.ErrorLog = log.Default()
+	}
 
 	l.Infoln("GUI and API listening on", listener.Addr())
 	l.Infoln("Access the GUI via the following URL:", guiCfg.URL())
diff --git a/lib/api/tokenmanager.go b/lib/api/tokenmanager.go
index 96dfab4a794..38cd2441575 100644
--- a/lib/api/tokenmanager.go
+++ b/lib/api/tokenmanager.go
@@ -36,11 +36,12 @@ type tokenManager struct {
 }
 
 func newTokenManager(key string, miscDB *db.NamespacedKV, lifetime time.Duration, maxItems int) *tokenManager {
-	tokens := &apiproto.TokenSet{
-		Tokens: make(map[string]int64),
-	}
+	var tokens apiproto.TokenSet
 	if bs, ok, _ := miscDB.Bytes(key); ok {
-		_ = proto.Unmarshal(bs, tokens) // best effort
+		_ = proto.Unmarshal(bs, &tokens) // best effort
+	}
+	if tokens.Tokens == nil {
+		tokens.Tokens = make(map[string]int64)
 	}
 	return &tokenManager{
 		key:      key,
@@ -49,7 +50,7 @@ func newTokenManager(key string, miscDB *db.NamespacedKV, lifetime time.Duration
 		maxItems: maxItems,
 		timeNow:  time.Now,
 		mut:      sync.NewMutex(),
-		tokens:   tokens,
+		tokens:   &tokens,
 	}
 }
 


Test patch: