From 4d1116530b216a79e20d7b86ef51e46d7b07a07e Mon Sep 17 00:00:00 2001 From: thePR0M3TH3AN <53631862+PR0M3TH3AN@users.noreply.github.com> Date: Wed, 22 Jul 2026 00:19:43 -0400 Subject: [PATCH] Surface BitLogin sign-in rejections instead of silently swallowing them devLogger.warn is a no-op outside dev mode (IS_DEV_MODE = false on this deployment), so when AuthService.requestLogin() rejected a BitLogin sign-in -- e.g. the invite-only access-control check ("Access restricted to admins and moderators users only.") -- nothing ever told the user why. The widget itself doesn't know bitvid rejected it, so it kept showing its own "Signed in" screen, and the login modal never closed, looking exactly like a hung/broken close button. On rejection: route the error through app.handleLoginModalError (the same visible-banner path every other sign-in method already uses) and reset the widget via widget.logout() so it doesn't keep claiming a session bitvid never actually granted. Also added a WeakSet guard so wireBitloginLogin() can never attach two "bitlogin-login" listeners to the same element, regardless of how attachWidget() ends up getting invoked -- cheap insurance, no observed bug currently triggers it. Co-Authored-By: Claude Sonnet 5 Claude-Session: https://claude.ai/code/session_01DY9UMTuF43Ekc2U4E1eDwX --- js/ui/bitloginModalIntegration.js | 45 +++++++++++++++++++++++++++++-- 1 file changed, 43 insertions(+), 2 deletions(-) diff --git a/js/ui/bitloginModalIntegration.js b/js/ui/bitloginModalIntegration.js index c71ba074..99e3391e 100644 --- a/js/ui/bitloginModalIntegration.js +++ b/js/ui/bitloginModalIntegration.js @@ -9,11 +9,12 @@ // gets its own widget in the modal instead of a grid button (see // providersForModal in js/services/authProviders/index.js) and its own small // integration module here rather than special-casing LoginModalController. -import { devLogger } from "../utils/logger.js"; +import { devLogger, userLogger } from "../utils/logger.js"; import { closeStaticModal } from "./components/staticModalAccessibility.js"; import { setModalState as setGlobalModalState } from "../state/appState.js"; import { createBitloginAdapter } from "../nostr/adapters/bitloginAdapter.js"; import { setPendingBitloginResult } from "../services/authProviders/bitlogin.js"; +import bitloginProvider from "../services/authProviders/bitlogin.js"; import { FEATURE_BITLOGIN } from "../constants.js"; // Vendored, pinned BitLogin widget bundle (scripts/build-bitlogin-widget.mjs). @@ -44,6 +45,13 @@ function closeLoginModal() { } } +// Cheap insurance against wireBitloginLogin() ever running twice for the same +// element (e.g. a future bootstrap change that calls it more than once): the +// same instance should only ever get one "bitlogin-login" +// listener, since two would each race the shared, single-read `pendingResult` +// and each independently call requestLogin() for one real sign-in. +const wiredWidgets = new WeakSet(); + function attachWidget(app, widget) { const mount = document.getElementById("bitloginMount"); if (!FEATURE_BITLOGIN) { @@ -59,6 +67,11 @@ function attachWidget(app, widget) { mount.hidden = false; } + if (wiredWidgets.has(widget)) { + return; + } + wiredWidgets.add(widget); + loadBitloginWidgetOnce().catch((error) => { devLogger.warn("[BitLogin] Failed to load the widget bundle:", error); }); @@ -81,7 +94,35 @@ function attachWidget(app, widget) { await app.authService.requestLogin({ providerId: "bitlogin" }); closeLoginModal(); } catch (error) { - devLogger.warn("[BitLogin] Sign-in failed to apply:", error); + // bitvid rejecting the sign-in (e.g. the invite-only access-control + // check) leaves the widget itself still showing its own "Signed in" + // screen -- BitLogin succeeded on its own terms, bitvid just didn't + // accept the identity. Reset the widget and surface the real reason + // through the same error-banner path every other sign-in method uses + // (devLogger.warn is a silent no-op outside dev mode -- see + // IS_DEV_MODE in config/instance-config.js -- so relying on it here + // meant a rejected user saw no feedback at all). + userLogger.warn("[BitLogin] Sign-in failed to apply:", error); + const maybePromise = app?.handleLoginModalError?.({ + error, + provider: bitloginProvider, + }); + if (maybePromise && typeof maybePromise.then === "function") { + maybePromise.catch((handlerError) => { + devLogger.warn( + "[BitLogin] handleLoginModalError threw:", + handlerError, + ); + }); + } + try { + await widget.logout(); + } catch (logoutError) { + devLogger.warn( + "[BitLogin] Failed to reset widget after a rejected sign-in:", + logoutError, + ); + } } }); }