Skip to content

Adds List-Unsubscribe-Post header to notification emails

What does this MR do and why?

Contributes to Fix immediate unsubscribe of participants (#466167)

This MR adds the List-Unsubscribe-Post header to all unsubscribable list email to prevent email clients and SMTP firewalls from automatically unsubscribing participants (and external participants).

RFC 8058 suggests to use a POST request for the List-Unsubscribe header, right now we use a basic GET request, which should be fine. We use the force=true query parameter to allow immediate unsubscribe without a user confirmation. The RFC suggest adding an additional header to the email to signal that this is a one-click action. The suggested header is

List-Unsubscribe-Post: List-Unsubscribe=One-Click

See the RFCs examples section for further details

This behavior was reported for the external participants feature in Service Desk because we make unsubscribe events for external participants visible on the ticket using system notes. Customers reported that they are seeing an immediate unsubscribe event after inviting an external participant to a ticket using the /add_email quick action. This sends a new_participant Service Desk email to the external participant, which contains the List-Unsubscribe header and optionally a unsubscribe link in the email body.

MR acceptance checklist

Please evaluate this MR against the MR acceptance checklist. It helps you analyze changes to reduce risks in quality, performance, reliability, security, and maintainability.

Screenshots or screen recordings

Screenshots are required for UI changes, and strongly recommended for all other merge requests.

🚫 backend only changes

How to set up and validate locally

Numbered steps to set up and validate the change are strongly suggested.

Let's verify this locally by generating emails for new note notification. All steps to be run using the console.

  1. Choose a user-generated note
    note = Note.where(system: false).last
  2. Generate a new note email for this note and verify that the header is present
    method_name = "note_#{note.noteable_ability_name}_email".to_sym
    email = Notify.send(method_name, note.author.id, note.id, NotificationReason::MENTIONED)
    # This should contain one header field entry   
    email.header['List-Unsubscribe-Post']
  3. Also generate a Service Desk new note email for this note and look for the header
    issue = note.noteable
    participant = IssueEmailParticipant.new(email: 'user@example.com', issue: issue)
    email = Notify.service_desk_new_note_email(issue.id, note.id, participant)
    # This should contain one header field entry   
    email.header['List-Unsubscribe-Post']
Edited by Marc Saleiko

Merge request reports