Skip to content

Fix double render in ApplicationController auth callbacks#66

Closed
JuanVqz wants to merge 2 commits intomainfrom
fix/puzzles-controller-double-render
Closed

Fix double render in ApplicationController auth callbacks#66
JuanVqz wants to merge 2 commits intomainfrom
fix/puzzles-controller-double-render

Conversation

@JuanVqz
Copy link
Member

@JuanVqz JuanVqz commented Mar 5, 2026

Problem

Both check_user_token and check_session_expiry in ApplicationController called render without returning, so the before_action callbacks would render the login view but still allow the action to continue executing. This causes Rails to raise AbstractController::DoubleRenderError on every unauthenticated or expired-session request.

# Before — execution continues after render
def check_user_token
  unless session[:user_token]
    render "puzzles/login"  # no return!
  end
end

Fix

Added and return to halt the callback immediately after rendering:

def check_user_token
  render "puzzles/login" and return unless session[:user_token]
end

def check_session_expiry
  if session[:expires_at].present? && Time.current > session[:expires_at]
    reset_session
    render "puzzles/login" and return
  end
  session[:expires_at] = 1.hour.from_now
end

Tests

Added a test to PuzzlesControllerTest asserting that unauthenticated requests render the login page with a 200 response instead of raising a double render error.

@JuanVqz JuanVqz requested review from mateusdeap and torresga March 5, 2026 23:52
autoprefixer-rails (>= 5.2.1)
sassc (>= 2.0.0)
brakeman (8.0.2)
brakeman (8.0.4)
Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

CI was failing because the brakeman version does not match

https://github.com/fastruby/ruby_or_rails/actions/runs/22741750663/job/65956852118

JuanVqz added a commit that referenced this pull request Mar 6, 2026
`valid_slack_request?` returned a boolean but never called `head` or
`render`, so unauthenticated Slack requests were silently allowed through.
Added `head :unauthorized and return` to halt the filter chain, consistent
with the fix applied in #66.
@JuanVqz JuanVqz self-assigned this Mar 6, 2026
JuanVqz added a commit that referenced this pull request Mar 6, 2026
`valid_slack_request?` returned a boolean but never called `head` or
`render`, so unauthenticated Slack requests were silently allowed through.
Added `head :unauthorized and return` to halt the filter chain, consistent
with the fix applied in #66.
Both check_user_token and check_session_expiry rendered the login view
without returning, causing the action to continue executing and Rails to
raise AbstractController::DoubleRenderError on every unauthenticated or
expired-session request.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
@JuanVqz JuanVqz force-pushed the fix/puzzles-controller-double-render branch from 650ad9c to c008034 Compare March 6, 2026 00:26
JuanVqz added a commit that referenced this pull request Mar 6, 2026
Replaces #66, #67, #68, #70 with a single cohesive fix:

- Merge check_session_expiry + check_user_token into one authenticate!
  callback, eliminating the double-render risk by design
- SessionsController and Slack::ApplicationController now each skip a
  single authenticate! instead of managing multiple callbacks
- Rename valid_slack_request? to verify_slack_request! and make it halt
  with head :unauthorized when signature verification fails
- Replace head :unprocessable_entity in send_message with logging and
  Sentry.capture_exception so notification failures never affect the
  HTTP response back to Slack
@JuanVqz
Copy link
Member Author

JuanVqz commented Mar 6, 2026

Superseded by #71 which addresses this and three other related bugs with a more robust design.

@JuanVqz JuanVqz closed this Mar 6, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant