require "net/http" class SessionsController < ApplicationController skip_before_action :authenticate_user!, only: [ :auth_request, :steam_callback ] def auth_request # Build Steam OpenID URL steam_openid_url = "https://steamcommunity.com/openid/login" # Use the actual request host/protocol for both return_to and realm to avoid signature mismatch base_url = request.base_url return_url = "#{base_url}#{steam_callback_path}" Rails.logger.info("Steam auth_request return_url=#{return_url} realm=#{base_url}") params = { "openid.ns" => "http://specs.openid.net/auth/2.0", "openid.identity" => "http://specs.openid.net/auth/2.0/identifier_select", "openid.claimed_id" => "http://specs.openid.net/auth/2.0/identifier_select", "openid.mode" => "checkid_setup", "openid.return_to" => return_url, "openid.realm" => base_url, "openid.ns.sreg" => "http://openid.net/extensions/sreg/1.1", "openid.sreg.required" => "email" } redirect_to "#{steam_openid_url}?#{params.to_query}", allow_other_host: true end def steam_callback # Get the OpenID response openid_response = request.params # Verify the response with Steam if verify_steam_response(openid_response) # Extract Steam ID from identity URL # Format: http://steamcommunity.com/openid/id/[STEAMID] identity_url = openid_response["openid.identity"] if identity_url && identity_url.include?("/id/") steam_id = identity_url.split("/id/").last # Create mock auth_hash for compatibility with our User model auth_hash = { "uid" => steam_id, "info" => { "nickname" => "Steam User #{steam_id}" } } user = User.find_or_create_from_steam(auth_hash) session[:user_id] = user.id redirect_to dashboard_path, notice: "Logged in successfully!" else redirect_to root_path, alert: "Could not extract Steam ID from response." end else redirect_to root_path, alert: "Steam authentication failed: Invalid response signature." end end def logout session[:user_id] = nil redirect_to root_path, notice: "Logged out successfully!" end private def verify_steam_response(response) # Steam expects only openid.* keys and mode=check_auth for validation openid_params = response.to_h.select { |k, _| k.to_s.start_with?("openid.") } return false unless openid_params["openid.mode"] == "id_res" # Per OpenID 2.0 spec, the verification mode is "check_authentication" verify_params = openid_params.merge("openid.mode" => "check_authentication") Rails.logger.info("Steam verify payload: #{verify_params.inspect}") uri = URI.parse("https://steamcommunity.com/openid/login") http = Net::HTTP.new(uri.host, uri.port) http.use_ssl = true request = Net::HTTP::Post.new(uri.path) request.set_form_data(verify_params) begin response = http.request(request) Rails.logger.info("Steam verify response body: #{response.body.inspect}") response.body.include?("is_valid:true") rescue StandardError => e Rails.logger.error("Steam verification error: #{e.message}") false end end def omniauth_failure redirect_to root_path, alert: "Steam authentication failed: #{params[:message]}" end end