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
@@ -7,6 +7,7 @@ import SpritePreview from '../components/SpritePreview.vue'
|
||||
import { ensureAudioContext } from '../game/audio'
|
||||
import HumanPreview from '../components/HumanPreview.vue'
|
||||
import WalletConnect from '../components/WalletConnect.vue'
|
||||
import { authFetch } from '../lib/nostr-auth'
|
||||
|
||||
const router = useRouter()
|
||||
const { pubkey, bot, profilePicUrl, isLoggedIn, hasExtension, hasStoredKey, isLoading, login, generateLogin, loginWithNsec, registerBot, registerHuman, getStoredNsec, logout } = useNostr()
|
||||
@@ -184,6 +185,14 @@ async function handleLogin() {
|
||||
}
|
||||
}
|
||||
|
||||
function handleSignerLogin() {
|
||||
if (!window.nostr) {
|
||||
error.value = 'No Nostr signer detected. Install a NIP-07 extension (Nos2x, Alby) or use Amber on Android.'
|
||||
return
|
||||
}
|
||||
handleLogin()
|
||||
}
|
||||
|
||||
function handleGenerateLogin() {
|
||||
error.value = ''
|
||||
const { nsec } = generateLogin()
|
||||
@@ -432,7 +441,7 @@ async function fightRanked() {
|
||||
error.value = ''
|
||||
try {
|
||||
const paymentId = await payEntryFee(bot.value.id)
|
||||
const res = await fetch(`/api/queue/join-ranked/${bot.value.id}`, {
|
||||
const res = await authFetch(`/api/queue/join-ranked/${bot.value.id}`, {
|
||||
method: 'POST',
|
||||
headers: { 'Content-Type': 'application/json' },
|
||||
body: JSON.stringify({ paymentId, pubkey: pubkey.value }),
|
||||
@@ -540,7 +549,7 @@ function handleSignOut() {
|
||||
</div>
|
||||
|
||||
<div v-if="!showNsecBackup" class="space-y-3">
|
||||
<!-- Sign in with extension -->
|
||||
<!-- Sign in with extension (NIP-07) -->
|
||||
<button
|
||||
v-if="hasExtension"
|
||||
class="w-full py-4 bg-neon-purple/10 border-2 border-neon-purple/50 text-neon-purple
|
||||
@@ -552,7 +561,21 @@ function handleSignOut() {
|
||||
@click="handleLogin"
|
||||
>
|
||||
<span v-if="isLoading" class="w-5 h-5 border-2 border-neon-purple/30 border-t-neon-purple rounded-full animate-spin" />
|
||||
{{ isLoading ? 'CONNECTING...' : 'SIGN IN WITH NOSTR' }}
|
||||
{{ isLoading ? 'CONNECTING...' : 'SIGN IN WITH EXTENSION' }}
|
||||
</button>
|
||||
|
||||
<!-- Sign in with Nostr signer -->
|
||||
<button
|
||||
class="w-full py-4 bg-neon-yellow/10 border-2 border-neon-yellow/50 text-neon-yellow
|
||||
font-display font-black text-base tracking-widest
|
||||
hover:bg-neon-yellow/20 hover:border-neon-yellow transition-all
|
||||
disabled:opacity-50 disabled:cursor-wait
|
||||
flex items-center justify-center gap-3"
|
||||
:disabled="isLoading"
|
||||
@click="handleSignerLogin"
|
||||
>
|
||||
<span v-if="isLoading" class="w-5 h-5 border-2 border-neon-yellow/30 border-t-neon-yellow rounded-full animate-spin" />
|
||||
{{ isLoading ? 'CONNECTING...' : 'USE NOSTR SIGNER' }}
|
||||
</button>
|
||||
|
||||
<!-- Sign in with stored key -->
|
||||
|
||||
Reference in New Issue
Block a user