diff --git a/VIBE/CHAT_LOG.md b/VIBE/CHAT_LOG.md index 0836147..f355271 100644 --- a/VIBE/CHAT_LOG.md +++ b/VIBE/CHAT_LOG.md @@ -849,6 +849,12 @@ Change the title of the field “One-time password” to “One-time password (w ### Assistant outcome Updated the OTP field label in the English, Spanish, German, French, and Dutch extension locale catalogs to clarify that the code is needed only when OTP is configured. +### User +The plugin settings still show `emailLabel`; make that `email` as the title for the email field. + +### Assistant outcome +Added the missing `emailLabel` and `emailPlaceholder` messages to every extension locale. The English field now resolves to `Email` instead of displaying the localization key. + ### User For the new password in the user setting add a validation field to make sure they are the same before submitting. @@ -914,3 +920,39 @@ Remove the “New primary email address” functionality and keep only selecting ### Assistant outcome Removed the replacement-email workflow entirely. Added cleanup migration 14 for its unused table; verified alternatives remain the only addresses eligible for primary selection. + +### User +When the user is logged in, the web plugin should not display “Please sign in to use LinkLog.” + +### Assistant outcome +Removed the authenticated-state warning flash by hiding the popup sign-in warning in the initial markup. The popup reveals it only after the session check confirms that no valid bearer session is available. + +### User +When the user is signed in, the plugin should not display “Please sign in to use LinkLog.” or the settings link. + +### Assistant outcome +Updated popup session detection to validate the stored bearer token through `/api/auth/me` before hiding the sign-in warning and revealing capture fields. Expired or invalid sessions now keep the warning and settings action visible. + +### User +The plugin still does not behave as expected and still shows that the user should sign in. + +### Assistant outcome +Centralized popup session validation, ensured valid bearer sessions hide the sign-in warning, and cleared stale credentials when the backend rejects a session. The capture form is shown only after successful validation. + +### User +In the popup show that the user has logged in. + +### Assistant outcome +Added a localized authenticated-session indicator to the Firefox popup, showing the returned username and backend URL after successful session validation. It remains hidden for signed-out or invalid sessions. + +### User +The popup still shows the sign-in block even though the authenticated session text is displayed; show the block only when signed out. + +### Assistant outcome +Made popup authentication states mutually exclusive using the DOM `hidden` property. Authenticated sessions now always hide the sign-in block, while signed-out sessions hide the session text and show only the warning block. + +### User +The authenticated session text and sign-in block are still shown together. + +### Assistant outcome +Made popup state transitions defensive by synchronizing both `hidden` attributes and CSS classes. Non-authentication tag-loading errors no longer switch the popup to signed-out state; only a rejected session does. Added `display: none !important` guards for both authentication blocks. diff --git a/VIBE/PROMPTS.md b/VIBE/PROMPTS.md index 6b14b71..791bc6f 100644 --- a/VIBE/PROMPTS.md +++ b/VIBE/PROMPTS.md @@ -147,6 +147,7 @@ 138. When the user is not logged in then the plugin should just display no form fields but warn the user that they have to log in with a link to settings. 139. Remove DEFAULT_BACKEND setting in the plugin. 140. Change the title of the field "One-time password" to "One-time password (when configured)". +157. The plugin settings still show 'emailLabel'; make that 'email' as title for the email field. 141. Remove any leading and trailing spaces when entering fields in the settings page of the plugin. 142. Do a full security audit document in what you have done in detail in Security-audit.md 143. Address issue 1. and improve password storage @@ -164,6 +165,12 @@ 152. Make sure an email can only be selected when it has been validated. 153. Remove the entire "New primary email address" block; keep only selecting an existing alternative as primary. 155. Remove the “New primary email address” functionality and keep only selecting an existing alternative as primary. +158. When the user is logged in the webplugin should not display "Please sign in to use LinkLog." +156. When the user is signed in the plugin should not display "Please sign in to use LinkLog." and the link to the settings +159. The plugin still does not behave as expected. It still shows that the user should sign in. +160. In the popup show that the user has logged in. +161. The popup still shows the sign-in block even though the authenticated session text is displayed; show the block only when signed out. +162. The authenticated session text and sign-in block are still shown together. ## Future entries diff --git a/XPI/unsigned/LinkLog-0.1.0.xpi b/XPI/unsigned/LinkLog-0.1.0.xpi index fbf3920..ca769e2 100644 Binary files a/XPI/unsigned/LinkLog-0.1.0.xpi and b/XPI/unsigned/LinkLog-0.1.0.xpi differ diff --git a/webextension/_locales/de/messages.json b/webextension/_locales/de/messages.json index 639bfb0..3cbf0b4 100644 --- a/webextension/_locales/de/messages.json +++ b/webextension/_locales/de/messages.json @@ -15,6 +15,8 @@ "openSettings": {"message": "Einstellungen öffnen"}, "signOut": {"message": "Abmelden"}, "backendUrlLabel": {"message": "Backend-URL"}, + "emailLabel": {"message": "E-Mail"}, + "emailPlaceholder": {"message": "alice@example.com"}, "usernameLabel": {"message": "Benutzername"}, "usernamePlaceholder": {"message": "alice"}, "passwordLabel": {"message": "Passwort"}, diff --git a/webextension/_locales/en-US/messages.json b/webextension/_locales/en-US/messages.json index d02af9f..106be54 100644 --- a/webextension/_locales/en-US/messages.json +++ b/webextension/_locales/en-US/messages.json @@ -47,6 +47,12 @@ "backendUrlLabel": { "message": "Backend URL" }, + "emailLabel": { + "message": "Email" + }, + "emailPlaceholder": { + "message": "alice@example.com" + }, "usernameLabel": { "message": "Username" }, diff --git a/webextension/_locales/es/messages.json b/webextension/_locales/es/messages.json index 8962bcb..9e435ec 100644 --- a/webextension/_locales/es/messages.json +++ b/webextension/_locales/es/messages.json @@ -15,6 +15,8 @@ "openSettings": {"message": "Abrir configuración"}, "signOut": {"message": "Cerrar sesión"}, "backendUrlLabel": {"message": "URL del servidor"}, + "emailLabel": {"message": "Correo electrónico"}, + "emailPlaceholder": {"message": "alice@example.com"}, "usernameLabel": {"message": "Nombre de usuario"}, "usernamePlaceholder": {"message": "alice"}, "passwordLabel": {"message": "Contraseña"}, diff --git a/webextension/_locales/fr/messages.json b/webextension/_locales/fr/messages.json index ea90114..86931b3 100644 --- a/webextension/_locales/fr/messages.json +++ b/webextension/_locales/fr/messages.json @@ -15,6 +15,8 @@ "openSettings": {"message": "Ouvrir les paramètres"}, "signOut": {"message": "Se déconnecter"}, "backendUrlLabel": {"message": "URL du serveur"}, + "emailLabel": {"message": "E-mail"}, + "emailPlaceholder": {"message": "alice@example.com"}, "usernameLabel": {"message": "Nom d’utilisateur"}, "usernamePlaceholder": {"message": "alice"}, "passwordLabel": {"message": "Mot de passe"}, diff --git a/webextension/_locales/nl/messages.json b/webextension/_locales/nl/messages.json index b6c7965..4dd0a68 100644 --- a/webextension/_locales/nl/messages.json +++ b/webextension/_locales/nl/messages.json @@ -15,6 +15,8 @@ "openSettings": {"message": "Instellingen openen"}, "signOut": {"message": "Uitloggen"}, "backendUrlLabel": {"message": "Backend-URL"}, + "emailLabel": {"message": "E-mail"}, + "emailPlaceholder": {"message": "alice@example.com"}, "usernameLabel": {"message": "Gebruikersnaam"}, "usernamePlaceholder": {"message": "alice"}, "passwordLabel": {"message": "Wachtwoord"}, diff --git a/webextension/popup.css b/webextension/popup.css index 96bf787..bce211b 100644 --- a/webextension/popup.css +++ b/webextension/popup.css @@ -149,6 +149,16 @@ button { color: #f38ba8; } +.auth-session { + margin-bottom: 10px; + border: 1px solid #585b70; + border-radius: 8px; + padding: 8px 10px; + color: #94e2d5; + font-size: 0.8rem; + overflow-wrap: anywhere; +} + #auth-warning { display: grid; gap: 8px; @@ -162,6 +172,11 @@ button { display: none; } +#auth-session[hidden], +#auth-warning[hidden] { + display: none !important; +} + .extension-footer { margin-top: 16px; color: #7f849c; diff --git a/webextension/popup.html b/webextension/popup.html index aca73f9..7d3b82d 100644 --- a/webextension/popup.html +++ b/webextension/popup.html @@ -15,7 +15,8 @@ -
+ + diff --git a/webextension/popup.js b/webextension/popup.js index 1221e9f..e2ade98 100644 --- a/webextension/popup.js +++ b/webextension/popup.js @@ -12,6 +12,7 @@ const newTagsInput = document.getElementById('new-tags'); const feedLink = document.getElementById('feed-link'); const authWarning = document.getElementById('auth-warning'); const warningSettingsButton = document.getElementById('warning-settings'); +const authSession = document.getElementById('auth-session'); const t = window.linklogI18n; @@ -31,13 +32,36 @@ async function getSettings() { return result; } +async function validateSession(settings) { + if (!settings.backendUrl || !settings.accessToken) return null; + try { + const response = await fetch(`${settings.backendUrl}/api/auth/me`, { + headers: {'Authorization': `Bearer ${settings.accessToken}`}, + }); + if (!response.ok) { + await browser.storage.local.remove(['accessToken', 'tokenType', 'tokenExpiresAt', 'refreshToken']); + return null; + } + return await response.json(); + } catch (error) { + return null; + } +} + function showSignedOutState() { form.classList.add('hidden'); + authSession.hidden = true; + authSession.classList.add('hidden'); + authWarning.hidden = false; authWarning.classList.remove('hidden'); } -function showSignedInState() { +function showSignedInState(user, backendUrl) { + authWarning.hidden = true; authWarning.classList.add('hidden'); + authSession.textContent = t('loggedInAt', [user.username || user.email, backendUrl]); + authSession.hidden = false; + authSession.classList.remove('hidden'); form.classList.remove('hidden'); } @@ -59,12 +83,20 @@ async function loadExistingTags() { showSignedOutState(); return; } - showSignedInState(); + const user = await validateSession(settings); + if (!user) { + showSignedOutState(); + return; + } + showSignedInState(user, settings.backendUrl); const response = await fetch(`${settings.backendUrl}/api/tags`, { headers: {'Authorization': `Bearer ${settings.accessToken}`}, }); if (!response.ok) { - if (response.status === 401) showSignedOutState(); + if (response.status === 401) { + showSignedOutState(); + return; + } existingTags.textContent = t('loadTagsFailed'); return; } @@ -124,6 +156,13 @@ async function handleSubmit(event) { return; } + if (!await validateSession(settings)) { + showSignedOutState(); + setStatus(t('sessionExpired'), true); + browser.runtime.openOptionsPage(); + return; + } + try { const response = await fetch(`${backendUrl}/api/links`, { method: 'POST',