feat(auth): require signed NIP-42 event for Nostr login

Closes a security flaw where the server trusted any pubkey the client
sent. The frontend now signs a per-session, time-bound challenge
(kind-22242 event) that the backend verifies with swentel/nostr-php
before establishing the session.

- NostrAuth: issueChallenge() + loginWithSignedEvent() with full
  schnorr/id verification, TTL window, and idempotent re-entry for
  concurrent Livewire listeners.
- auth-button: mounts a fresh challenge, exposes it via data-attribute
  + requestNostrChallenge() fallback, renders a full-viewport AAA-style
  loading overlay while the wallet signs.
- NostrSessionGuard: override logout() to drop the cookie-jar dep so
  programmatic logout works in any context.
This commit is contained in:
HolgerHatGarKeineNode
2026-05-20 01:09:20 +02:00
parent 532199fe15
commit 6bb7d93d1d
9 changed files with 501 additions and 77 deletions
@@ -55,20 +55,23 @@ it('can delete project', function () {
expect(ProjectProposal::find($project->id))->toBeNull();
});
it('handles nostr login', function () {
it('reflects an authenticated nostr session on mount', function () {
$pleb = EinundzwanzigPleb::factory()->create();
NostrAuth::login($pleb->pubkey);
Livewire::test('association.project-support.index')
->call('handleNostrLogin', $pleb->pubkey)
->assertSet('currentPubkey', $pleb->pubkey)
->assertSet('isAllowed', true);
});
it('handles nostr logout', function () {
it('clears state on nostr logout', function () {
$pleb = EinundzwanzigPleb::factory()->create();
NostrAuth::login($pleb->pubkey);
Livewire::test('association.project-support.index')
->call('handleNostrLogin', $pleb->pubkey)
->assertSet('currentPubkey', $pleb->pubkey)
->call('handleNostrLogout')
->assertSet('currentPubkey', null)
->assertSet('isAllowed', false);