From 1d8dacbaa3d9b41965b9b7a489dfa7f104b2f82b Mon Sep 17 00:00:00 2001 From: Padreug Date: Sun, 24 May 2026 16:32:29 +0200 Subject: [PATCH] fix: require admin_key + owner check on PUT /tickets/register MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The legacy register endpoint had no auth decorator and no event-ownership check — any caller who knew a ticket id could mark it registered. Add require_admin_key (matches the rest of the wallet-bound endpoints in this file) and verify the caller's user owns the event the ticket belongs to. Breaking change for any external integration that hit this endpoint unauthed; the in-tree Quasar register page (static/js/register.js) already sends the session admin_key via LNbits.api.request so it keeps working. The Nostr-transport flow at events_ticket_register (previous commit) is the preferred call site for new callers; this HTTP path stays for the legacy LNbits admin UI. Co-Authored-By: Claude Opus 4.7 (1M context) --- views_api.py | 33 ++++++++++++++++++++++++++++++++- 1 file changed, 32 insertions(+), 1 deletion(-) diff --git a/views_api.py b/views_api.py index e7c118f..d2bac8f 100644 --- a/views_api.py +++ b/views_api.py @@ -766,7 +766,24 @@ async def api_ticket_resend_email( @tickets_api_router.put("/register/{ticket_id}") -async def api_event_register_ticket(ticket_id) -> Ticket: +async def api_event_register_ticket( + ticket_id: str, + key_info: WalletTypeInfo = Depends(require_admin_key), +) -> Ticket: + """Mark a ticket as registered at the door. + + Auth: wallet admin_key. Caller must own the event the ticket + belongs to — we check `event.wallet` against the user's full + wallet set so an organizer with multiple wallets can scan + regardless of which wallet's key they're using. + + Until v1.6.1-aio.3 this endpoint had no auth, which meant any + caller who knew a ticket id could register it. The + Nostr-transport flow at `events_ticket_register` is now the + preferred call site for the webapp; this HTTP path stays for + the legacy LNbits Quasar register page which already sends + the wallet admin_key through `LNbits.api.request`. + """ ticket = await get_ticket(ticket_id) if not ticket: @@ -774,6 +791,20 @@ async def api_event_register_ticket(ticket_id) -> Ticket: status_code=HTTPStatus.NOT_FOUND, detail="Ticket does not exist." ) + event = await get_event(ticket.event) + if not event: + raise HTTPException( + status_code=HTTPStatus.NOT_FOUND, detail="Event does not exist." + ) + + user = await get_user(key_info.wallet.user) + owned_wallet_ids = user.wallet_ids if user else [key_info.wallet.id] + if event.wallet not in owned_wallet_ids: + raise HTTPException( + status_code=HTTPStatus.FORBIDDEN, + detail="You do not own this event.", + ) + if not ticket.paid: raise HTTPException( status_code=HTTPStatus.FORBIDDEN, detail="Ticket not paid for."