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 ? '' : `
+
+
+ `}
+
@@ -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) => {