Electron 'login' event fires repeatedly after wrong credentials — how to stop the 401 retry loop
0 reputation · 08 Feb 2022, 05:52 UTC
0 reputation · 08 Feb 2022, 05:52 UTC
I'm handling HTTP authentication in an Electron app via app.on('login'). My handler calls event.preventDefault() and supplies cached credentials with callback(username, password). When those credentials expire or are rejected by the server, the login event fires again for the same origin, and my handler keeps supplying the same stale credentials — an endless challenge/response loop with no built-in signal from Electron that the credentials were refused.
Credentials are stored encrypted via safeStorage.encryptString and decrypted on demand. I want least-privilege behavior: only supply credentials to an allowlisted origin, and fall back to the default Chromium dialog (by not calling preventDefault()) once cached credentials are known to be bad. Assume a recent Electron version (28+) and that the server returns a fresh 401 with WWW-Authenticate on failure.
login event firing again means my supplied credentials were rejected, rather than a new independent challenge?webContents or the session) that exposes the final 401 after credentials were submitted?login handler itself, or is it cleaner to invalidate the cached credential after the first failure and let the next challenge show the default dialog?35775 reputation · 08 Feb 2022, 16:55 UTC
When the app.on('login') handler supplies cached credentials that the server rejects, Electron fires the login event again for the same origin. The event carries no explicit “credentials were rejected” flag, so the only reliable indicator is that the event is being emitted a second time for the same origin while you have already called callback(). A per‑origin guard that skips preventDefault() on the second firing will stop the endless 401 loop and allow Chromium’s default dialog to appear.
Electron keeps retrying the authentication challenge until a successful 2xx response is received. Because your handler always calls preventDefault() and provides the same stale credentials, the server keeps replying 401 and Electron keeps refiring login. There is no built‑in signal that the credentials were rejected, so you must detect the repeat challenge yourself.
login event that indicates a failed credential attempt.login event includes an authInfo object with host, port, realm, scheme, and isProxy fields. These are stable for a given origin and can be used as a key.webRequest.onHeadersReceived listener can see 401 responses, but it cannot reliably link them to a particular login invocation.preventDefault() and callback(), verify that authInfo.host (and optionally authInfo.port and authInfo.realm) matches an allowlist of origins you are willing to auto‑authenticate.Map keyed by a string such as authInfo.host + ':' + authInfo.port + ':' + authInfo.realm that records whether you have already supplied credentials for the current challenge sequence.event.preventDefault(), decrypt the cached credential with safeStorage.decryptString(), and invoke callback(username, password). Then store the key in the Map to mark that the first attempt has been made.preventDefault(); let Chromium show its default dialog. Immediately delete the encrypted credential from safeStorage and remove the key from the Map so that a future legitimate challenge will not be auto‑answered again.session.webRequest.onCompleted filtered for the origin and a status code 200-399. After reset, a new challenge will trigger the guard again.authInfo.isProxy and authInfo.scheme in the key string. This prevents a proxy challenge from suppressing a legitimate server prompt.Because the login event is the only place where you can decide whether to supply credentials or defer to the default dialog, placing the guard there is cleaner and avoids the overhead of global request listeners. The guard also provides an immediate, per‑origin context that is lost if you try to infer the failure from later 401 responses.
Could you log the full authInfo object (host, port, realm, scheme, isProxy) for each login firing? That would confirm that the repeat challenge is indeed for the same origin and help fine‑tune the key used in the Map.
Use comments to ask for clarification. Post a solution as an answer.
No question comments on this page.