Follow-up from the adversarial review of #271, which fixed cross-intent receipt dispatch but left two adjacent pre-existing gaps open.
First, ReceiptHeader in both MPP authenticators emits a Payment-Receipt with status: success from nothing but a parseable credential. l402.FromHeader accepts macaroons via Grpc-Metadata-Macaroon and scans all Authorization values, so a request can authenticate via L402 while its first Authorization header carries a fabricated Payment credential with intent=charge; the charge authenticator's ReceiptHeader then stamps a success receipt with the attacker-chosen payment hash as its reference, with no VerifyChallengeID and no settlement check. Receipts are informational, so nothing is lost but audit integrity, but BearerSessionID already re-verifies for exactly this class of reason (a request can carry credentials for several schemes and only one of them authenticated it), and its cost argument applies unchanged here: one HMAC per receipt.
Second, MultiAuthenticator.ReceiptHeader consults every provider, while AcceptForScheme filters sub-authenticators through schemeMatches. An l402-only service can therefore still emit MPP receipts for stray Payment credentials. Same root cause, same fix shape: apply the scheme filter to receipt dispatch too.
A related nit from the same review: close receipts (SessionReceipt) omit challengeId, while every other receipt carries it for traceability. challengeId is optional per draft-httpauth-payment-00, but the asymmetry loses the audit link precisely on the action that moves money back.
Follow-up from the adversarial review of #271, which fixed cross-intent receipt dispatch but left two adjacent pre-existing gaps open.
First,
ReceiptHeaderin both MPP authenticators emits aPayment-Receiptwithstatus: successfrom nothing but a parseable credential.l402.FromHeaderaccepts macaroons viaGrpc-Metadata-Macaroonand scans allAuthorizationvalues, so a request can authenticate via L402 while its firstAuthorizationheader carries a fabricatedPaymentcredential with intent=charge; the charge authenticator'sReceiptHeaderthen stamps a success receipt with the attacker-chosen payment hash as its reference, with noVerifyChallengeIDand no settlement check. Receipts are informational, so nothing is lost but audit integrity, butBearerSessionIDalready re-verifies for exactly this class of reason (a request can carry credentials for several schemes and only one of them authenticated it), and its cost argument applies unchanged here: one HMAC per receipt.Second,
MultiAuthenticator.ReceiptHeaderconsults every provider, whileAcceptForSchemefilters sub-authenticators throughschemeMatches. An l402-only service can therefore still emit MPP receipts for stray Payment credentials. Same root cause, same fix shape: apply the scheme filter to receipt dispatch too.A related nit from the same review: close receipts (
SessionReceipt) omitchallengeId, while every other receipt carries it for traceability.challengeIdis optional per draft-httpauth-payment-00, but the asymmetry loses the audit link precisely on the action that moves money back.