mirror of
https://github.com/HolgerHatGarKeineNode/einundzwanzig-app.git
synced 2026-05-05 04:54:53 +00:00
90835f8b1f
- Remove unauthenticated /test route that dispatched FetchNostrProfileJob for a hardcoded user (routes/web.php). - Enforce created_by ownership check in meetup and lecturer Livewire edit components; mirror the existing services/edit pattern. - Replace blind-trust nostrLoggedIn handler with NIP-42-style signed event verification: server-issued challenge stored in session, client signs a kind:22242 event, server verifies signature via swentel/nostr-php and derives npub. Challenge is single-use with 5-minute TTL. - Validate the ?my[] parameter on the calendar download endpoint as an array of integers and intersect with the authenticated user's meetups.
106 lines
3.1 KiB
JavaScript
106 lines
3.1 KiB
JavaScript
export default () => ({
|
|
pollingInterval: null,
|
|
errorCheckInterval: null,
|
|
authErrorShown: false,
|
|
startTime: null,
|
|
pollCount: 0,
|
|
MAX_POLL_COUNT: 30,
|
|
|
|
async init() {
|
|
this.startTime = Date.now();
|
|
},
|
|
|
|
async openNostrLogin() {
|
|
const livewireComponent = this.$el.closest('[wire\\:id]')?.__livewire;
|
|
const challenge = livewireComponent?.$wire?.nostrChallenge;
|
|
if (!challenge) {
|
|
this.showAuthError('Login challenge missing. Please reload and try again.');
|
|
return;
|
|
}
|
|
|
|
const signedEvent = await window.nostr.signEvent({
|
|
kind: 22242,
|
|
created_at: Math.floor(Date.now() / 1000),
|
|
tags: [['challenge', challenge]],
|
|
content: '',
|
|
});
|
|
|
|
this.$dispatch('nostrLoggedIn', {signedEvent});
|
|
},
|
|
|
|
initErrorPolling() {
|
|
this.errorCheckInterval = setInterval(() => {
|
|
this.checkForErrors();
|
|
}, 4000);
|
|
},
|
|
|
|
async checkForErrors() {
|
|
if (this.authErrorShown) {
|
|
return;
|
|
}
|
|
|
|
try {
|
|
const livewireComponent = this.$el.closest('[wire\\:id]')?.__livewire;
|
|
if (!livewireComponent) {
|
|
return;
|
|
}
|
|
|
|
this.pollCount++;
|
|
const elapsedSeconds = Math.floor((Date.now() - this.startTime) / 1000);
|
|
|
|
const response = await fetch('/api/check-auth-error', {
|
|
method: 'POST',
|
|
headers: {
|
|
'Content-Type': 'application/json',
|
|
'X-CSRF-TOKEN': document.querySelector('meta[name="csrf-token"]')?.content,
|
|
},
|
|
body: JSON.stringify({
|
|
k1: livewireComponent.$wire.k1,
|
|
elapsed_seconds: elapsedSeconds,
|
|
}),
|
|
});
|
|
|
|
if (response.ok) {
|
|
const data = await response.json();
|
|
if (data.error) {
|
|
this.showAuthError(data.error);
|
|
this.authErrorShown = true;
|
|
}
|
|
}
|
|
} catch (error) {
|
|
console.error('Error checking for auth errors:', error);
|
|
}
|
|
},
|
|
|
|
showAuthError(error) {
|
|
let message = error || 'Authentication failed. Please try again.';
|
|
let variant = 'danger';
|
|
|
|
if (message.includes('incompatible') || message.includes('format')) {
|
|
message = 'Wallet signature format incompatible. Please try a different wallet.';
|
|
variant = 'warning';
|
|
} else if (message.includes('expired') || message.includes('Session')) {
|
|
message = 'Session expired. Please try again.';
|
|
variant = 'warning';
|
|
}
|
|
|
|
if (window.Flux && window.Flux.toast) {
|
|
window.Flux.toast({
|
|
heading: 'Authentication Error',
|
|
text: message,
|
|
variant: variant,
|
|
duration: 8000,
|
|
});
|
|
}
|
|
|
|
this.$dispatch('auth-error', {message, variant});
|
|
},
|
|
|
|
destroy() {
|
|
if (this.errorCheckInterval) {
|
|
clearInterval(this.errorCheckInterval);
|
|
}
|
|
},
|
|
|
|
});
|