From 1a2f0e7951ac6e696a9caccae4e7656e489d4715 Mon Sep 17 00:00:00 2001 From: Jason Tudisco Date: Mon, 16 Mar 2026 18:43:01 -0600 Subject: [PATCH] feat: add Nostr NIP-07 browser extension login and invite by pubkey - Server: nostr crate, migration 008 (nostr_pubkey column), challenge/verify endpoints for Schnorr-signed NIP-07 auth, invite-by-nostr endpoint - Client: NIP-07 extension detection, relay profile fetch, Nostr login button on login/register pages, Nostr tab in invite modal, profile page handles no-email Nostr users - Sentinel emails (nostr:) hidden at API boundary via public_email() Co-Authored-By: Claude Opus 4.6 (1M context) --- client/src/components/invite-modal.riot | 115 ++++++++++- client/src/components/login-page.riot | 103 ++++++++++ client/src/components/profile-page.riot | 4 +- client/src/components/register-page.riot | 93 +++++++++ client/src/services/api.js | 7 +- client/src/services/nostr.js | 75 +++++++ server/Cargo.lock | 239 +++++++++++++++++++++++ server/Cargo.toml | 1 + server/migrations/008_nostr.sql | 1 + server/src/handlers/auth.rs | 8 +- server/src/handlers/invites.rs | 72 ++++++- server/src/handlers/mod.rs | 1 + server/src/handlers/nostr_auth.rs | 167 ++++++++++++++++ server/src/handlers/profile.rs | 8 +- server/src/handlers/rooms.rs | 6 +- server/src/main.rs | 14 ++ server/src/models/mod.rs | 30 +++ 17 files changed, 927 insertions(+), 17 deletions(-) create mode 100644 client/src/services/nostr.js create mode 100644 server/migrations/008_nostr.sql create mode 100644 server/src/handlers/nostr_auth.rs diff --git a/client/src/components/invite-modal.riot b/client/src/components/invite-modal.riot index 89f5427..9f72e9d 100644 --- a/client/src/components/invite-modal.riot +++ b/client/src/components/invite-modal.riot @@ -10,7 +10,18 @@ -
+
+ + +
+ +
-
+

Invite link generated!

+ +
+
+ + update({ nostrPubkey: e.target.value })} + required + /> +
+ +

{state.error}

+ + +
+ +
+

Added {state.nostrDisplayName} to room

+

+ This Nostr user hasn't joined GroupChat yet. They'll need to log in with their Nostr extension first. +

+ +
@@ -150,6 +194,49 @@ font-size: var(--text-xs); color: var(--text-muted); } + + .invite-tabs { + display: flex; + gap: var(--space-xs); + margin-bottom: var(--space-lg); + border-bottom: 1px solid var(--border); + padding-bottom: var(--space-xs); + } + + .invite-tab { + background: none; + border: none; + padding: var(--space-xs) var(--space-md); + font-size: var(--text-sm); + color: var(--text-secondary); + cursor: pointer; + border-radius: var(--radius-md) var(--radius-md) 0 0; + transition: color var(--transition-fast), background var(--transition-fast); + } + + .invite-tab:hover { + color: var(--text-primary); + background: var(--bg-tertiary); + } + + .invite-tab.active { + color: var(--accent); + border-bottom: 2px solid var(--accent); + font-weight: 500; + } + + .success-msg { + color: var(--success); + font-weight: 500; + margin-bottom: var(--space-md); + } + + .info-msg { + color: var(--text-secondary); + font-size: var(--text-sm); + line-height: 1.5; + margin-bottom: var(--space-md); + } diff --git a/client/src/components/profile-page.riot b/client/src/components/profile-page.riot index a3e780c..278efaf 100644 --- a/client/src/components/profile-page.riot +++ b/client/src/components/profile-page.riot @@ -41,8 +41,8 @@
- - Email cannot be changed + + {props.user?.email ? 'Email cannot be changed' : 'Logged in via Nostr'}

{state.error}

diff --git a/client/src/components/register-page.riot b/client/src/components/register-page.riot index 0058dff..b915293 100644 --- a/client/src/components/register-page.riot +++ b/client/src/components/register-page.riot @@ -51,6 +51,17 @@ +
+ or +
+ + +