import { useState } from 'react'; import { Download, Copy, Eye, EyeOff, Shield, AlertTriangle, CheckCircle, ExternalLink, KeyRound, QrCode } from 'lucide-react'; import { Card, CardContent, CardDescription, CardHeader, CardTitle } from '@/components/ui/card'; import { Button } from '@/components/ui/button'; import { Alert, AlertDescription } from '@/components/ui/alert'; import { QRCodeCanvas } from '@/components/ui/qrcode'; import { useCurrentUser } from '@/hooks/useCurrentUser'; import { useLocalStorage } from '@/hooks/useLocalStorage'; import { useNostrLogin } from '@nostrify/react/login'; import { toast } from '@/hooks/useToast'; const sanitizeFilename = (filename: string) => filename.replace(/[^a-z0-9_.-]/gi, '_'); export function SecuritySettings() { const { user } = useCurrentUser(); const { logins } = useNostrLogin(); const [confirmed, setConfirmed] = useLocalStorage('backup-confirmed', false); const [showWarning, setShowWarning] = useState(!confirmed); // Recovery reveal state const [revealed, setRevealed] = useState(false); const [showQr, setShowQr] = useState(false); // The active login. For nsec logins, the recovery code (nsec) is stored on // the login object itself — this is how we can reveal it later for users who // skipped backup at signup. For bunker/extension/Amber logins, the app never // held the key, so there is nothing to reveal here. const activeLogin = logins[0] as | (typeof logins[number] & { nsec?: string; data?: { nsec?: string } }) | undefined; const loginType = activeLogin?.type; // Nostrify stores the bech32 nsec directly on the login (older builds nested // it under `data`), so check both to be safe. const recoveryCode = loginType === 'nsec' ? (activeLogin?.nsec ?? activeLogin?.data?.nsec) : undefined; const handleReveal = () => setRevealed(true); const handleCopyRecovery = () => { if (!recoveryCode) return; navigator.clipboard.writeText(recoveryCode); // Copying the recovery code counts as a real backup — mark confirmed so the // gentle reminder stops nagging a user who has genuinely saved their login. setConfirmed(true); setShowWarning(false); toast({ title: 'Copied!', description: 'Recovery code copied. Store it somewhere safe.' }); }; const handleDownloadRecovery = () => { if (!recoveryCode) return; try { const blob = new Blob([recoveryCode], { type: 'text/plain; charset=utf-8' }); const url = globalThis.URL.createObjectURL(blob); const a = document.createElement('a'); a.href = url; a.download = sanitizeFilename('skatespots-recovery-code.txt'); a.style.display = 'none'; document.body.appendChild(a); a.click(); globalThis.URL.revokeObjectURL(url); document.body.removeChild(a); // A downloaded recovery file is a genuine backup — mark confirmed so the // reminder stops for good. setConfirmed(true); setShowWarning(false); toast({ title: 'Saved!', description: 'Recovery code downloaded. Keep it somewhere safe.' }); } catch { toast({ title: 'Download failed', description: 'Please copy it manually instead.', variant: 'destructive' }); } }; if (!user) { return (

Login Required

Log in to access security settings.

); } const handleConfirmBackup = () => { setConfirmed(true); setShowWarning(false); toast({ title: 'Backup Confirmed! ✅', description: 'Great! Your keys are safe.', }); }; return (
{/* Recover / reveal recovery code — the real backup path for skaters who skipped it at signup. Only possible for nsec logins. */} Your Recovery Code Didn't save it when you signed up? You can grab it here anytime. {recoveryCode ? ( <>

This code is the only way to log back in on a new device or browser. Save it somewhere safe (a password manager, or written on paper). Anyone who has it controls your account — never share it.

{!revealed ? ( ) : (
{recoveryCode}
{showQr && (

Scan with another Nostr app to log in. Keep this screen private.

)}

⚠️ Never share this. Hide it when you're done.

)} ) : ( {loginType === 'x-amber' && ( <>You're logged in with Amber. Your recovery code lives inside Amber — open the Amber app to back it up. SkateSpots never sees your key. )} {loginType === 'bunker' && ( <>You're logged in with a remote signer (bunker). Your recovery code is held by your signer service, not SkateSpots. Back it up there. )} {(loginType as string) === 'extension' && ( <>You're logged in with a browser extension. Your recovery code lives in the extension — use its own backup/export option. SkateSpots never sees your key. )} {!loginType && ( <>Your recovery code is managed by your signer app, not SkateSpots. Back it up there. )} )}
{/* Backup Warning */} {showWarning && (

⚠️ Have you backed up your recovery phrase or secret key?

Your keys are the ONLY way to access your account. If you lose them, you lose everything - there's no password reset!

)} {/* Backup Status */} {confirmed ? '✅ Backup Confirmed' : '⚠️ Backup Not Confirmed'} {confirmed ? 'You\'ve confirmed that your keys are safely backed up.' : 'Make sure to backup your recovery phrase or secret key!'} {!confirmed && (

Haven't saved your recovery code yet? No problem — grab it from the Your Recovery Code section above, store it somewhere safe, then confirm below.

)}
{/* Security Information */} Key Security Best Practices How to keep your Nostr account safe
✍️

Write it down on paper

Physical backup is the safest. Store in a secure location like a safe.

💾

Multiple backups

Keep copies in different secure locations (home safe, bank deposit box, etc.)

🔒

Never share your keys

Real Nostr apps NEVER ask for your nsec or recovery phrase after initial login

🚫

Don't store in cloud unencrypted

Avoid Google Docs, email drafts, or plain text cloud storage

🔐

Consider using a browser extension

Alby, nos2x, or Flamingo keep your keys secure and never expose them to websites

⚠️ Important Reminders

  • • If you lose your keys, you lose your account permanently
  • • No company or person can reset your password - not even us
  • • Anyone with your keys has full control of your account
  • • Your keys control all your Nostr data across ALL apps
{/* Browser Extension & Mobile App Recommendations */} 🔌 Recommended Nostr Signers Use a Nostr signer for better security and convenience
{/* Amber - Android */}
Amber Android

Secure Nostr signer for Android. Keeps your keys safe on your phone and signs events without exposing them.

Alby Recommended

Full-featured extension with Lightning wallet, WebLN, and NWC support.

Flamingo

Simple, clean interface. Focuses on Nostr signing without wallet features.

nos2x

Minimal, lightweight extension for advanced users.

{/* Nostr Connect (Bunkers) */} ☁️ Nostr Connect (Remote Signers) Use a remote signer to access your account from any device

Nostr Connect (also called "bunkers") lets you store your keys on a secure server and sign in from any device. Your keys never leave the server, but you can use your account anywhere!

nsec.app Popular

Free, secure remote signer. Create bunker:// URI and paste when logging in.

Nostrum

Another remote signer option with mobile support.

💡 How to use: Create an account on one of these services, get your bunker:// connection string, then paste it in the "Nostr Connect" tab when logging in.

); }