diff --git a/bun.lockb b/bun.lockb
index a3d38b2..02c1670 100755
Binary files a/bun.lockb and b/bun.lockb differ
diff --git a/components/Modals/Login.tsx b/components/Modals/Login.tsx
index 5dc402c..9032f40 100644
--- a/components/Modals/Login.tsx
+++ b/components/Modals/Login.tsx
@@ -12,6 +12,7 @@ import { Label } from "@/components/ui/label";
import AddPassphrase from "./AddPassphrase";
import { decryptMessage } from "@/lib/nostr";
import { toast } from "sonner";
+import { generateSchnorrKeyPair } from "@/lib/keys";
export default function LoginModal() {
const { loginWithNip07, loginWithSecret } = useNDK();
@@ -19,8 +20,10 @@ export default function LoginModal() {
const [isLoading, setIsLoading] = useState(false);
const [showExtensionLogin, setShowExtensionLogin] = useState(true);
const [showPassphraseLogin, setShowPassphraseLogin] = useState(false);
+ const [showEmailLogin, setShowEmailLogin] = useState(false);
const [nsec, setNsec] = useState("");
const [passphrase, setPassphrase] = useState("");
+ const [email, setEmail] = useState("");
const [encryptedNsec, setEncryptedNsec] = useState("");
const modal = useModal();
@@ -92,6 +95,25 @@ export default function LoginModal() {
setIsLoading(false);
modal?.hide();
}
+ async function handleLoginEmail() {
+ if (!email || !passphrase) return;
+ setIsLoading(true);
+ const seedString = email + passphrase;
+ const { privateKey, publicKey } = generateSchnorrKeyPair(seedString);
+ console.log("privateKey", privateKey, "publicKey", publicKey);
+ const emailNsec = nip19.nsecEncode(privateKey);
+ const user = await loginWithSecret(emailNsec);
+ console.log("User", user);
+ if (!user) {
+ throw new Error("NO auth");
+ }
+ await loginWithPubkey(nip19.decode(user.npub).data.toString());
+ if (typeof window.webln !== "undefined") {
+ await window.webln.enable();
+ }
+ setIsLoading(false);
+ modal?.hide();
+ }
async function handleLoginPassphrase() {
if (!encryptedNsec || !passphrase) return;
setIsLoading(true);
@@ -138,7 +160,51 @@ export default function LoginModal() {
Connect with extension
)}
- {showPassphraseLogin ? (
+ {showEmailLogin ? (
+
+
+
+ setEmail(e.target.value)}
+ placeholder="email"
+ type="email"
+ className="text-[16px]"
+ />
+
+
+
+ setPassphrase(e.target.value)}
+ placeholder="password..."
+ type="password"
+ className="text-[16px]"
+ />
+
+
+
+
+
+
+ ) : showPassphraseLogin ? (
Connect with Nsec
- {!!encryptedNsec && (
-
-
-
- )}
+
+
+ Or, use
+ {!!encryptedNsec && (
+ <>
+
+ or
+ >
+ )}
+
+
)}
diff --git a/lib/hooks/useKeyboardShortcut.ts b/lib/hooks/useKeyboardShortcut.ts
index d8819d6..72ea108 100644
--- a/lib/hooks/useKeyboardShortcut.ts
+++ b/lib/hooks/useKeyboardShortcut.ts
@@ -12,7 +12,7 @@ export const useKeyboardShortcut = (keys: Key[], callback: () => void) => {
(key === "ctrl" && (event.metaKey || event.ctrlKey)) ||
(key === "shift" && event.shiftKey) ||
(key === "alt" && event.altKey) ||
- (typeof key === "string" && event.key.toLowerCase() === key),
+ (typeof key === "string" && event.key?.toLowerCase() === key),
)
) {
callback();
diff --git a/lib/keys/index.ts b/lib/keys/index.ts
new file mode 100644
index 0000000..555192e
--- /dev/null
+++ b/lib/keys/index.ts
@@ -0,0 +1,24 @@
+import { createHmac } from "crypto";
+import { ec as EC } from "elliptic";
+
+export function generateSchnorrKeyPair(seed: string): {
+ privateKey: string;
+ publicKey: string;
+} {
+ // Use HMAC-SHA256 with your seed as the message and a secret key (salt)
+ const hmac = createHmac("sha256", "secret-salt");
+ hmac.update(seed);
+ const derivedKey = hmac.digest();
+
+ // Create a key pair using the derived private key
+ const ec = new EC("secp256k1");
+ const keyPair = ec.keyFromPrivate(derivedKey);
+
+ // Get the public key in hexadecimal format
+ const publicKey = keyPair.getPublic("hex");
+
+ return {
+ privateKey: derivedKey.toString("hex"),
+ publicKey,
+ };
+}
diff --git a/package.json b/package.json
index 3b3371e..f83c2f5 100644
--- a/package.json
+++ b/package.json
@@ -44,6 +44,7 @@
"dayjs": "^1.11.10",
"dexie": "^3.2.4",
"dexie-react-hooks": "^1.1.6",
+ "elliptic": "^6.5.4",
"focus-trap-react": "^10.2.3",
"framer-motion": "^10.16.4",
"jotai": "^2.4.3",
@@ -75,6 +76,7 @@
"@tailwindcss/typography": "^0.5.10",
"@total-typescript/ts-reset": "^0.5.1",
"@types/crypto-js": "^4.1.2",
+ "@types/elliptic": "^6.4.16",
"@types/latlon-geohash": "^2.0.2",
"@types/node": "^20",
"@types/ramda": "^0.29.6",