From 20c2bc34cdc7db25370cce37c3a61b955a500bb5 Mon Sep 17 00:00:00 2001 From: Eugene Burmakin Date: Sun, 21 Sep 2025 13:51:26 +0200 Subject: [PATCH] Store client header in session to persist across redirects --- app/controllers/application_controller.rb | 23 ++++++++++++++++------- spec/requests/authentication_spec.rb | 21 ++++++++++++++++++++- 2 files changed, 36 insertions(+), 8 deletions(-) diff --git a/app/controllers/application_controller.rb b/app/controllers/application_controller.rb index 96485374..ba20b793 100644 --- a/app/controllers/application_controller.rb +++ b/app/controllers/application_controller.rb @@ -5,7 +5,7 @@ class ApplicationController < ActionController::Base rescue_from Pundit::NotAuthorizedError, with: :user_not_authorized - before_action :unread_notifications, :set_self_hosted_status + before_action :unread_notifications, :set_self_hosted_status, :store_client_header protected @@ -40,14 +40,17 @@ class ApplicationController < ActionController::Base end def after_sign_in_path_for(resource) - payload = { api_key: resource.api_key, exp: 5.minutes.from_now.to_i } + # Check both current request header and stored session value + client_type = request.headers['X-Dawarich-Client'] || session[:dawarich_client] - token = Subscription::EncodeJwtToken.new( - payload, ENV['AUTH_JWT_SECRET_KEY'] - ).call - - case request.headers['X-Dawarich-Client'] + case client_type when 'ios' + payload = { api_key: resource.api_key, exp: 5.minutes.from_now.to_i } + + token = Subscription::EncodeJwtToken.new( + payload, ENV['AUTH_JWT_SECRET_KEY'] + ).call + ios_success_path(token:) else super @@ -60,6 +63,12 @@ class ApplicationController < ActionController::Base @self_hosted = DawarichSettings.self_hosted? end + def store_client_header + return unless request.headers['X-Dawarich-Client'] + + session[:dawarich_client] = request.headers['X-Dawarich-Client'] + end + def user_not_authorized redirect_back fallback_location: root_path, alert: 'You are not authorized to perform this action.', diff --git a/spec/requests/authentication_spec.rb b/spec/requests/authentication_spec.rb index 99b46959..1494bca7 100644 --- a/spec/requests/authentication_spec.rb +++ b/spec/requests/authentication_spec.rb @@ -72,7 +72,10 @@ RSpec.describe 'Authentication', type: :request do # Make a login request with the iOS client header (user NOT pre-signed in) post user_session_path, params: { user: { email: user.email, password: 'password123' } - }, headers: { 'X-Dawarich-Client' => 'ios' } + }, headers: { + 'X-Dawarich-Client' => 'ios', + 'Accept' => 'text/html' + } # Should redirect to iOS success endpoint after successful login # The redirect will include a token parameter generated by after_sign_in_path_for @@ -80,6 +83,22 @@ RSpec.describe 'Authentication', type: :request do expect(response.location).to include('token=') end + it 'does not redirect to iOS success path when using turbo_stream format' do + # This test demonstrates the issue: when iOS app sends turbo_stream format, + # it doesn't get the iOS-specific redirect behavior + post user_session_path, params: { + user: { email: user.email, password: 'password123' } + }, headers: { + 'X-Dawarich-Client' => 'ios', + 'Accept' => 'text/vnd.turbo-stream.html' + } + + # With turbo_stream format, it doesn't redirect at all (no location header) + # This demonstrates why iOS authentication fails when using turbo_stream + expect(response.location).to be_nil + expect(response.status).to eq(200) # Returns turbo_stream response instead of redirect + end + it 'returns JSON response with JWT token for iOS success endpoint' do # Generate a test JWT token using the same service as the controller payload = { api_key: user.api_key, exp: 5.minutes.from_now.to_i }