diff --git a/shared/components/rstack-identity.ts b/shared/components/rstack-identity.ts index cabbcc6d..3dbfbd5f 100644 --- a/shared/components/rstack-identity.ts +++ b/shared/components/rstack-identity.ts @@ -875,9 +875,13 @@ export class RStackIdentity extends HTMLElement { `}
- ${showPicker ? '' : ``} + ${showPicker ? '' : ` + + + `}
+
Powered by EncryptID
@@ -890,6 +894,7 @@ export class RStackIdentity extends HTMLElement {

Create your EncryptID

Set up a secure, passwordless identity.

+
@@ -921,10 +926,13 @@ export class RStackIdentity extends HTMLElement { if (pickerBtn) { pickerBtn.style.opacity = "0.6"; pickerBtn.style.pointerEvents = "none"; const arrow = pickerBtn.querySelector(".account-pick-arrow"); if (arrow) arrow.innerHTML = ''; } try { + // Send as email or username depending on input format + const isEmail = loginUsername.includes("@"); + const authBody = isEmail ? { email: loginUsername } : { username: loginUsername }; const startRes = await fetch(`${ENCRYPTID_URL}/api/auth/start`, { method: "POST", headers: { "Content-Type": "application/json" }, - body: JSON.stringify({ username: loginUsername }), + body: JSON.stringify(authBody), }); if (!startRes.ok) throw new Error("Failed to start authentication"); const { options: serverOptions, userFound } = await startRes.json(); @@ -981,11 +989,40 @@ export class RStackIdentity extends HTMLElement { } }; + const handleMagicLink = async () => { + const input = overlay.querySelector("#auth-signin-username") as HTMLInputElement | null; + const msgEl = overlay.querySelector("#magic-link-msg") as HTMLElement | null; + const errEl = overlay.querySelector("#auth-error") as HTMLElement | null; + const value = input?.value.trim() || ""; + if (!value || !value.includes("@")) { + if (errEl) errEl.textContent = "Enter an email address to receive a magic link."; + input?.focus(); + return; + } + if (errEl) errEl.textContent = ""; + const btn = overlay.querySelector('[data-action="send-magic-link"]') as HTMLButtonElement | null; + if (btn) { btn.disabled = true; btn.innerHTML = ' Sending...'; } + try { + await fetch(`${ENCRYPTID_URL}/api/auth/magic-link`, { + method: "POST", + headers: { "Content-Type": "application/json" }, + body: JSON.stringify({ email: value }), + }); + if (msgEl) { msgEl.style.display = ""; msgEl.style.color = "#22c55e"; msgEl.textContent = "Login link sent! Check your inbox."; } + } catch { + if (msgEl) { msgEl.style.display = ""; msgEl.style.color = "#f87171"; msgEl.textContent = "Failed to send. Try again."; } + } finally { + if (btn) { btn.disabled = false; btn.innerHTML = "📧 Send Magic Link"; } + } + }; + const handleRegister = async () => { const usernameInput = overlay.querySelector("#auth-username") as HTMLInputElement; + const emailInput = overlay.querySelector("#auth-email") as HTMLInputElement | null; const errEl = overlay.querySelector("#auth-error") as HTMLElement; const btn = overlay.querySelector('[data-action="register"]') as HTMLButtonElement; const username = usernameInput.value.trim(); + const email = emailInput?.value.trim() || ""; if (!username) { errEl.textContent = "Please enter a username."; @@ -1037,6 +1074,7 @@ export class RStackIdentity extends HTMLElement { }, userId, username, + ...(email ? { email } : {}), }), }); const data = await completeRes.json().catch(() => null); @@ -1062,6 +1100,7 @@ export class RStackIdentity extends HTMLElement { callbacks?.onCancel?.(); }); overlay.querySelector('[data-action="signin"]')?.addEventListener("click", handleSignIn); + overlay.querySelector('[data-action="send-magic-link"]')?.addEventListener("click", handleMagicLink); overlay.querySelector('[data-action="register"]')?.addEventListener("click", handleRegister); overlay.querySelector('[data-action="switch-register"]')?.addEventListener("click", () => { mode = "register"; @@ -1099,6 +1138,13 @@ export class RStackIdentity extends HTMLElement { }); }); overlay.querySelector("#auth-username")?.addEventListener("keydown", (e) => { + if ((e as KeyboardEvent).key === "Enter") { + // Tab to email field if empty, otherwise register + const emailInput = overlay.querySelector("#auth-email") as HTMLInputElement | null; + if (emailInput && !emailInput.value.trim()) { emailInput.focus(); } else { handleRegister(); } + } + }); + overlay.querySelector("#auth-email")?.addEventListener("keydown", (e) => { if ((e as KeyboardEvent).key === "Enter") handleRegister(); }); overlay.querySelector("#auth-signin-username")?.addEventListener("keydown", (e) => {