From 6ed9064f5d559ceb1efe2180375efaffaa352bed Mon Sep 17 00:00:00 2001 From: Morgan Roderick Date: Fri, 9 Oct 2026 08:44:24 +0200 Subject: [PATCH] fix: allow reject without a session cookie The reject endpoint authenticates by the invitation token in the URL, like accept and update. Its form must survive a browser withholding the session cookie (e.g. Safari/WebKit ITP on cross-site navigation), but with CSRF protection enforced reject fails with InvalidAuthenticityToken before the token can do its job. Add reject to the skip_forgery_protection list, matching accept, update and WaitingListsController (same rationale as PR #2641, Rollbar #535), and pin the token-only, no-session request with a spec. --- app/controllers/workshop_invitation_controller.rb | 2 +- .../workshop_invitation_controller_spec.rb | 12 ++++++++++++ 2 files changed, 13 insertions(+), 1 deletion(-) diff --git a/app/controllers/workshop_invitation_controller.rb b/app/controllers/workshop_invitation_controller.rb index 6b3369864..9fb6c9bc9 100644 --- a/app/controllers/workshop_invitation_controller.rb +++ b/app/controllers/workshop_invitation_controller.rb @@ -9,7 +9,7 @@ class WorkshopInvitationController < ApplicationController # CSRF is redundant and fails when browsers withhold the session cookie # (e.g. Safari/WebKit ITP on cross-site navigation). Same rationale as # FeedbackController#submit (PR #2641, Rollbar #535). - skip_forgery_protection only: %i[update accept] + skip_forgery_protection only: %i[update accept reject] def show @announcements = @invitation.member.announcements.active diff --git a/spec/controllers/workshop_invitation_controller_spec.rb b/spec/controllers/workshop_invitation_controller_spec.rb index 21fbc136d..bad22661d 100644 --- a/spec/controllers/workshop_invitation_controller_spec.rb +++ b/spec/controllers/workshop_invitation_controller_spec.rb @@ -156,6 +156,18 @@ end end + context 'without a session (the invitation token is the only credential)' do + include_context 'with forgery protection enforced' + + before { LoginHelpers::LoginStub.current_user = nil } + + it 'still rejects the RSVP with the token alone' do + post :reject, params: { id: invitation.token } + + expect(invitation.reload.attending).to be false + end + end + context 'when someone is on waiting list' do let(:waitlisted_member) { Fabricate(:member) } let(:waitlisted_invitation) { Fabricate(:workshop_invitation, workshop:, member: waitlisted_member, role: 'Student') }