login with email
This commit is contained in:
parent
06fc618def
commit
9ef6086e76
@ -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
|
||||
</Button>
|
||||
)}
|
||||
{showPassphraseLogin ? (
|
||||
{showEmailLogin ? (
|
||||
<div className="space-y-3">
|
||||
<div className="space-y-2">
|
||||
<Label>Email</Label>
|
||||
<Input
|
||||
value={email}
|
||||
onChange={(e) => setEmail(e.target.value)}
|
||||
placeholder="email"
|
||||
type="email"
|
||||
className="text-[16px]"
|
||||
/>
|
||||
</div>
|
||||
<div className="space-y-2">
|
||||
<Label>Password</Label>
|
||||
<Input
|
||||
value={passphrase}
|
||||
onChange={(e) => setPassphrase(e.target.value)}
|
||||
placeholder="password..."
|
||||
type="password"
|
||||
className="text-[16px]"
|
||||
/>
|
||||
</div>
|
||||
<Button
|
||||
variant={"outline"}
|
||||
onClick={() => void handleLoginEmail()}
|
||||
loading={isLoading}
|
||||
className="w-full"
|
||||
>
|
||||
Login
|
||||
</Button>
|
||||
<div className="center">
|
||||
<Button
|
||||
variant={"link"}
|
||||
size={"sm"}
|
||||
className="h-0 pt-1"
|
||||
onClick={() => {
|
||||
setShowEmailLogin(false);
|
||||
setShowPassphraseLogin(false);
|
||||
}}
|
||||
>
|
||||
Or, use Nsec
|
||||
</Button>
|
||||
</div>
|
||||
</div>
|
||||
) : showPassphraseLogin ? (
|
||||
<div className="space-y-3">
|
||||
<Label>Passphrase</Label>
|
||||
<Input
|
||||
@ -184,18 +250,27 @@ export default function LoginModal() {
|
||||
>
|
||||
Connect with Nsec
|
||||
</Button>
|
||||
|
||||
<div className="center text-xs font-medium text-primary">
|
||||
Or, use
|
||||
{!!encryptedNsec && (
|
||||
<div className="center">
|
||||
<Button
|
||||
variant={"link"}
|
||||
size={"sm"}
|
||||
className="h-0 pt-1"
|
||||
<>
|
||||
<button
|
||||
className="ml-1 hover:underline"
|
||||
onClick={() => setShowPassphraseLogin(true)}
|
||||
>
|
||||
Or, use Passphrase
|
||||
</Button>
|
||||
</div>
|
||||
Passphrase
|
||||
</button>
|
||||
<span className="mx-1">or</span>
|
||||
</>
|
||||
)}
|
||||
<button
|
||||
className="ml-1 hover:underline"
|
||||
onClick={() => setShowEmailLogin(true)}
|
||||
>
|
||||
Email
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
|
@ -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();
|
||||
|
24
lib/keys/index.ts
Normal file
24
lib/keys/index.ts
Normal file
@ -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,
|
||||
};
|
||||
}
|
@ -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",
|
||||
|
Loading…
x
Reference in New Issue
Block a user