{"record":{"id":"712b46f676841ef3","repo":"signalapp/Signal-Server","slug":"must-not-use-authenticated-connection-for-one-time","errorCode":null,"errorMessage":"must not use authenticated connection for one-time donation operations","messagePattern":"must not use authenticated connection for one-time donation operations","errorType":"http","errorClass":"ForbiddenException","httpStatus":403,"severity":"error","filePath":"service/src/main/java/org/whispersystems/textsecuregcm/controllers/OneTimeDonationController.java","lineNumber":185,"sourceCode":"      content = @Content(schema = @Schema(\n          type = \"object\",\n          properties = {\n              @StringToClassMapItem(key = \"error\", value = String.class)\n          })))\n  @ApiResponse(responseCode = \"401\", description = \"Donation permit was invalid or already spent\")\n  @RateLimitedByIp(RateLimiters.For.ONE_TIME_DONATION)\n  @ManagedAsync\n  public CreateBoostResponse createBoostPaymentIntent(\n      @Auth final Optional<AuthenticatedDevice> authenticatedAccount,\n\n      @Parameter(description = \"A base64-encoded donation permit retrieved from POST /v1/donation/permit\")\n      @HeaderParam(HeaderUtils.DONATION_PERMIT) final Optional<DonationPermitHeader> donationPermitHeader,\n\n      @NotNull @Valid final CreateBoostRequest request,\n      @HeaderParam(HttpHeaders.USER_AGENT) final String userAgent) throws SubscriptionInvalidAmountException {\n\n    if (authenticatedAccount.isPresent()) {\n      throw new ForbiddenException(\"must not use authenticated connection for one-time donation operations\");\n    }\n\n    SubscriptionsUtil.recordDonationPermitPresent(donationPermitHeader.isPresent(), \"boostCreate\", userAgent);\n    final boolean spendSuccessful = donationPermitHeader.map(\n            permitHeader -> {\n              try {\n                return SubscriptionsUtil.verifyAndSpendDonationPermit(permitHeader.permit(), donationPermitsManager, clock);\n              } catch (final VerificationFailedException e) {\n                return false;\n              }\n            })\n        .orElse(false);\n    if (!spendSuccessful) {\n      throw new WebApplicationException(Response.Status.UNAUTHORIZED);\n    }\n\n    validateRequestCurrencyAmount(request, BigDecimal.valueOf(request.amount), stripeManager);\n    final PaymentIntent paymentIntent = stripeManager.createPaymentIntent(request.currency, request.amount,","sourceCodeStart":167,"sourceCodeEnd":203,"githubUrl":"https://github.com/signalapp/Signal-Server/blob/100ab61c82627582c867d19e1c0561ba2781e927/service/src/main/java/org/whispersystems/textsecuregcm/controllers/OneTimeDonationController.java#L167-L203","documentation":"One-time donation (boost) operations must be performed from an unauthenticated connection. createBoostPaymentIntent rejects the request with HTTP 403 if an authenticated account is present on the request.","triggerScenarios":"POSTing to the boost payment-intent endpoint while presenting valid Signal account credentials.","commonSituations":"Clients sending donations through a client that attaches auth headers globally; misconfigured HTTP client adding the Authorization/unidentified header to donation calls.","solutions":["Perform donation requests without Signal account authentication credentials","Use a separate, unauthenticated HTTP client/session for donation endpoints","Remove global auth-header interceptors for the donations API base URL"],"exampleFix":"// before\nsignalClient.post(\"/v1/donations/boost/payment_intent\", request); // authed client\n// after\nunauthenticatedClient.post(\"/v1/donations/boost/payment_intent\", request);","handlingStrategy":"validation","validationCode":"if (hasAuthCredentials()) throw new Error('donations must not be authenticated');","typeGuard":null,"tryCatchPattern":"try { await createBoost(intent); } catch (e) { if (e.status === 403) rethrowWithHint('send donation requests without Signal auth headers'); else throw e; }","preventionTips":["Use a dedicated unauthenticated HTTP client for donation endpoints","Audit global auth-header interceptors for the donations base URL","Never reuse the authenticated messaging session for payments"],"tags":["http-403","forbidden","donations","signal-server"],"backgroundTag":"permission-denied","analyzedSha":"100ab61c82627582c867d19e1c0561ba2781e927","analyzedAt":"2026-09-09T13:29:47.883Z","contentChangedAt":"2026-09-09T13:29:47.883Z","schemaVersion":2},"datasetVersion":"2026-09-16T09:17:16.951Z"}