feat: NIP-98 + JWT authentication with signer support
Replace insecure raw-pubkey auth with cryptographic NIP-98 signed requests and server-issued JWT sessions. Logout now fully clears all state including nsec. Add yellow "Use Nostr Signer" button for Amber/NIP-07 remote signers. - Server: JWT middleware (HMAC-SHA256, 24h expiry), NIP-98 verification - Server: POST /api/auth/nostr/session endpoint - Frontend: NIP-98 token builder + authFetch wrapper with JWT Bearer - Frontend: All authenticated API calls use authFetch - Security: logout clears JWT, pubkey, bot, nsec, and profile pic Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
co-authored by
Claude Opus 4.6
parent
ad96d1158f
commit
3ba05a66b4
@@ -4,6 +4,7 @@ import { finalizeEvent } from 'nostr-tools'
|
||||
import * as nip04 from 'nostr-tools/nip04'
|
||||
import * as nip44 from 'nostr-tools/nip44'
|
||||
import { hexToBytes, bytesToHex } from 'nostr-tools/utils'
|
||||
import { authFetch } from '../lib/nostr-auth'
|
||||
|
||||
type WalletMethod = 'nwc' | 'lnaddress' | null
|
||||
type PaymentStatus = 'idle' | 'invoiced' | 'paying' | 'confirmed' | 'failed'
|
||||
@@ -59,7 +60,7 @@ export function useWallet() {
|
||||
// Validate the NWC URL format
|
||||
parseNwcUrl(connectionString)
|
||||
|
||||
const res = await fetch('/api/payments/connect-wallet', {
|
||||
const res = await authFetch('/api/payments/connect-wallet', {
|
||||
method: 'POST',
|
||||
headers: { 'Content-Type': 'application/json' },
|
||||
body: JSON.stringify({
|
||||
@@ -88,7 +89,7 @@ export function useWallet() {
|
||||
throw new Error('Invalid Lightning Address format')
|
||||
}
|
||||
|
||||
const res = await fetch('/api/payments/connect-wallet', {
|
||||
const res = await authFetch('/api/payments/connect-wallet', {
|
||||
method: 'POST',
|
||||
headers: { 'Content-Type': 'application/json' },
|
||||
body: JSON.stringify({
|
||||
@@ -111,7 +112,7 @@ export function useWallet() {
|
||||
async function disconnectWallet(): Promise<void> {
|
||||
if (!pubkey.value) return
|
||||
|
||||
await fetch('/api/payments/disconnect-wallet', {
|
||||
await authFetch('/api/payments/disconnect-wallet', {
|
||||
method: 'DELETE',
|
||||
headers: { 'Content-Type': 'application/json' },
|
||||
body: JSON.stringify({ pubkey: pubkey.value }),
|
||||
@@ -128,7 +129,7 @@ export function useWallet() {
|
||||
async function checkWalletStatus(): Promise<void> {
|
||||
if (!pubkey.value) return
|
||||
|
||||
const res = await fetch(`/api/payments/wallet-status?pubkey=${pubkey.value}`)
|
||||
const res = await authFetch(`/api/payments/wallet-status?pubkey=${pubkey.value}`)
|
||||
if (res.ok) {
|
||||
const data = await res.json()
|
||||
isWalletConnected.value = data.connected
|
||||
@@ -144,7 +145,7 @@ export function useWallet() {
|
||||
|
||||
try {
|
||||
// Create invoice
|
||||
const invoiceRes = await fetch('/api/payments/create-invoice', {
|
||||
const invoiceRes = await authFetch('/api/payments/create-invoice', {
|
||||
method: 'POST',
|
||||
headers: { 'Content-Type': 'application/json' },
|
||||
body: JSON.stringify({ botId, pubkey: pubkey.value }),
|
||||
@@ -175,7 +176,7 @@ export function useWallet() {
|
||||
const preimage = await payViaNWC(nwcUrl, bolt11)
|
||||
|
||||
// Tell server payment is confirmed (skip lookup_invoice polling)
|
||||
await fetch(`/api/payments/confirm/${paymentId}`, {
|
||||
await authFetch(`/api/payments/confirm/${paymentId}`, {
|
||||
method: 'POST',
|
||||
headers: { 'Content-Type': 'application/json' },
|
||||
body: JSON.stringify({ preimage, pubkey: pubkey.value }),
|
||||
@@ -187,7 +188,7 @@ export function useWallet() {
|
||||
// No NWC — poll for confirmation (manual payment / QR code flow)
|
||||
for (let i = 0; i < 30; i++) {
|
||||
await new Promise(r => setTimeout(r, 2000))
|
||||
const checkRes = await fetch(`/api/payments/check/${paymentId}`)
|
||||
const checkRes = await authFetch(`/api/payments/check/${paymentId}`)
|
||||
if (checkRes.ok) {
|
||||
const { status } = await checkRes.json()
|
||||
if (status === 'confirmed') {
|
||||
@@ -212,7 +213,7 @@ export function useWallet() {
|
||||
}
|
||||
|
||||
async function submitCashuToken(botId: string, token: string): Promise<string> {
|
||||
const res = await fetch('/api/payments/submit-cashu', {
|
||||
const res = await authFetch('/api/payments/submit-cashu', {
|
||||
method: 'POST',
|
||||
headers: { 'Content-Type': 'application/json' },
|
||||
body: JSON.stringify({ botId, token }),
|
||||
|
||||
Reference in New Issue
Block a user