diff --git a/Gemfile.lock b/Gemfile.lock index 8cd59c2b7..f6a1c3200 100644 --- a/Gemfile.lock +++ b/Gemfile.lock @@ -121,7 +121,6 @@ GEM mime-types-data (~> 3.2015) mime-types-data (3.2022.0105) mini_mime (1.1.2) - mini_portile2 (2.8.0) minitest (5.16.3) mocha (2.0.2) ruby2_keywords (>= 0.0.5) @@ -135,8 +134,11 @@ GEM net-smtp (0.3.3) net-protocol nio4r (2.5.8) - nokogiri (1.13.9) - mini_portile2 (~> 2.8.0) + nokogiri (1.13.9-arm64-darwin) + racc (~> 1.4) + nokogiri (1.13.9-x86_64-darwin) + racc (~> 1.4) + nokogiri (1.13.9-x86_64-linux) racc (~> 1.4) oj (3.13.23) openssl (3.0.1) @@ -234,8 +236,9 @@ GEM actionpack (>= 5.2) activesupport (>= 5.2) sprockets (>= 3.0.0) - sqlite3 (1.5.4) - mini_portile2 (~> 2.8.0) + sqlite3 (1.5.4-arm64-darwin) + sqlite3 (1.5.4-x86_64-darwin) + sqlite3 (1.5.4-x86_64-linux) syntax_tree (4.3.0) prettier_print (>= 1.0.2) thor (1.2.1) @@ -253,7 +256,9 @@ GEM zeitwerk (2.6.4) PLATFORMS - ruby + arm64-darwin-21 + x86_64-darwin-19 + x86_64-linux DEPENDENCIES byebug @@ -273,4 +278,4 @@ DEPENDENCIES webmock BUNDLED WITH - 2.3.22 + 2.3.4 diff --git a/app/controllers/shopify_app/callback_controller.rb b/app/controllers/shopify_app/callback_controller.rb index e43339500..c79b1737f 100644 --- a/app/controllers/shopify_app/callback_controller.rb +++ b/app/controllers/shopify_app/callback_controller.rb @@ -8,50 +8,65 @@ class CallbackController < ActionController::Base def callback begin - filtered_params = request.parameters.symbolize_keys.slice(:code, :shop, :timestamp, :state, :host, :hmac) - - auth_result = ShopifyAPI::Auth::Oauth.validate_auth_callback( - cookies: { - ShopifyAPI::Auth::Oauth::SessionCookie::SESSION_COOKIE_NAME => - cookies.encrypted[ShopifyAPI::Auth::Oauth::SessionCookie::SESSION_COOKIE_NAME], - }, - auth_query: ShopifyAPI::Auth::Oauth::AuthQuery.new(**filtered_params), - ) - rescue => e - if e.class.module_parent != ShopifyAPI::Errors - message = <<~EOS - An error of type #{e.class} was rescued. This is not part of `ShopifyAPI::Errors`, which could indicate a - bug in your app, or a bug in the shopify_app gem. Future versions of the gem may re-raise this error rather - than rescuing it. - EOS - ShopifyApp::Logger.deprecated(message, "22.0.0") - end + api_session, cookie = validated_auth_objects + rescue => error + deprecate_callback_rescue(error) unless error.class.module_parent == ShopifyAPI::Errors return respond_with_error end - cookies.encrypted[auth_result[:cookie].name] = { - expires: auth_result[:cookie].expires, - secure: true, - http_only: true, - value: auth_result[:cookie].value, - } + save_session(api_session) if api_session + update_rails_cookie(api_session, cookie) - if auth_result[:session].online? - session[:shopify_user_id] = auth_result[:session].associated_user.id - ShopifyApp::Logger.debug("Saving Shopify user ID to cookie") - end + return respond_with_user_token_flow if start_user_token_flow?(api_session) - if start_user_token_flow?(auth_result[:session]) - return respond_with_user_token_flow - end + perform_post_authenticate_jobs(api_session) + respond_successfully if check_billing(api_session) + end + + private - perform_post_authenticate_jobs(auth_result[:session]) - has_payment = check_billing(auth_result[:session]) + def deprecate_callback_rescue(error) + message = <<~EOS + An error of type #{error.class} was rescued. This is not part of `ShopifyAPI::Errors`, which could indicate a + bug in your app, or a bug in the shopify_app gem. Future versions of the gem may re-raise this error rather + than rescuing it. + EOS + ShopifyApp::Logger.deprecated(message, "22.0.0") + end - respond_successfully if has_payment + def save_session(api_session) + ShopifyApp::SessionRepository.store_session(api_session) end - private + def validated_auth_objects + filtered_params = request.parameters.symbolize_keys.slice(:code, :shop, :timestamp, :state, :host, :hmac) + + oauth_payload = ShopifyAPI::Auth::Oauth.validate_auth_callback( + cookies: { + ShopifyAPI::Auth::Oauth::SessionCookie::SESSION_COOKIE_NAME => + cookies.encrypted[ShopifyAPI::Auth::Oauth::SessionCookie::SESSION_COOKIE_NAME], + }, + auth_query: ShopifyAPI::Auth::Oauth::AuthQuery.new(**filtered_params), + ) + api_session = oauth_payload.dig(:session) + cookie = oauth_payload.dig(:cookie) + + [api_session, cookie] + end + + def update_rails_cookie(api_session, cookie) + if cookie.value.present? + cookies.encrypted[cookie.name] = { + expires: cookie.expires, + secure: true, + http_only: true, + value: cookie.value, + } + end + + session[:shopify_user_id] = api_session.associated_user.id if api_session.online? + ShopifyApp::Logger.debug("Saving Shopify user ID to cookie") + end def respond_successfully if ShopifyAPI::Context.embedded? diff --git a/lib/generators/shopify_app/install/templates/shopify_app.rb.tt b/lib/generators/shopify_app/install/templates/shopify_app.rb.tt index 1f31b3ce5..10648e648 100644 --- a/lib/generators/shopify_app/install/templates/shopify_app.rb.tt +++ b/lib/generators/shopify_app/install/templates/shopify_app.rb.tt @@ -48,7 +48,6 @@ Rails.application.config.after_initialize do scope: ShopifyApp.configuration.scope, is_private: !ENV.fetch('SHOPIFY_APP_PRIVATE_SHOP', '').empty?, is_embedded: ShopifyApp.configuration.embedded_app, - session_storage: ShopifyApp::SessionRepository, log_level: :info, logger: Rails.logger, private_shop: ENV.fetch('SHOPIFY_APP_PRIVATE_SHOP', nil), diff --git a/lib/shopify_app/controller_concerns/login_protection.rb b/lib/shopify_app/controller_concerns/login_protection.rb index 8b14f408b..f7aa1132b 100644 --- a/lib/shopify_app/controller_concerns/login_protection.rb +++ b/lib/shopify_app/controller_concerns/login_protection.rb @@ -48,7 +48,7 @@ def activate_shopify_session def current_shopify_session @current_shopify_session ||= begin cookie_name = ShopifyAPI::Auth::Oauth::SessionCookie::SESSION_COOKIE_NAME - ShopifyAPI::Utils::SessionUtils.load_current_session( + load_current_session( auth_header: request.headers["HTTP_AUTHORIZATION"], cookies: { cookie_name => cookies.encrypted[cookie_name] }, is_online: online_token_configured?, @@ -265,6 +265,15 @@ def user_session_expected? online_token_configured? end + def load_current_session(auth_header: nil, cookies: nil, is_online: false) + return ShopifyAPI::Context.load_private_session if ShopifyAPI::Context.private? + + session_id = ShopifyAPI::Utils::SessionUtils.current_session_id(auth_header, cookies, is_online) + return nil unless session_id + + ShopifyApp::SessionRepository.load_session(session_id) + end + def requested_by_javascript? request.xhr? || request.content_type == "text/javascript" || diff --git a/test/controllers/callback_controller_test.rb b/test/controllers/callback_controller_test.rb index da00ba3d0..94b807e74 100644 --- a/test/controllers/callback_controller_test.rb +++ b/test/controllers/callback_controller_test.rb @@ -28,23 +28,22 @@ class CallbackControllerTest < ActionController::TestCase ShopifyApp::SessionRepository.user_storage = nil ShopifyAppConfigurer.setup_context I18n.locale = :en - + @stubbed_session = ShopifyAPI::Auth::Session.new(shop: "shop", access_token: "token") + @stubbed_cookie = ShopifyAPI::Auth::Oauth::SessionCookie.new(value: "", expires: Time.now) + host = Base64.strict_encode64("#{ShopifyAPI::Context.host_name}/admin") + @callback_params = { shop: "shop", code: "code", state: "state", timestamp: "timestamp", host: host, + hmac: "hmac", } + @auth_query = ShopifyAPI::Auth::Oauth::AuthQuery.new(**@callback_params) request.env["HTTP_USER_AGENT"] = "Mozilla/5.0 (Macintosh; Intel Mac OS X 10_13_6)"\ "AppleWebKit/537.36 (KHTML, like Gecko)"\ "Chrome/69.0.3497.100 Safari/537.36" - end - - test "#callback flashes error when omniauth is not present" do - get :callback, - params: { shop: "shop", code: "code", state: "state", timestamp: "timestamp", host: "host", hmac: "hmac" } - assert_equal flash[:error], "Could not log in to Shopify store" + ShopifyApp::SessionRepository.stubs(:store_session) end test "#callback flashes error in Spanish" do - I18n.locale = :es + I18n.expects(:t).with("could_not_log_in") get :callback, params: { shop: "shop", code: "code", state: "state", timestamp: "timestamp", host: "host", hmac: "hmac" } - assert_match "sesiĆ³n", flash[:error] end test "#callback rescued errors of ShopifyAPI::Error will not emit a deprecation notice" do @@ -89,6 +88,53 @@ class CallbackControllerTest < ActionController::TestCase get :callback, params: @callback_params end + test "#callback saves the session when validated by API library" do + mock_oauth + ShopifyApp::SessionRepository.expects(:store_session).with(@stubbed_session) + + get :callback, params: @callback_params + end + + test "#callback sets the shopify_user_id in the Rails session when session is online" do + associated_user = ShopifyAPI::Auth::AssociatedUser.new( + id: 42, + first_name: "LeeeEEeeeeee3roy", + last_name: "Jenkins", + email: "dat_email@tho.com", + email_verified: true, + locale: "en", + collaborator: true, + account_owner: true, + ) + mock_session = ShopifyAPI::Auth::Session.new(shop: "shop", access_token: "token", is_online: true, + associated_user: associated_user) + mock_oauth(session: mock_session) + get :callback, params: @callback_params + assert_equal session[:shopify_user_id], associated_user.id + end + + test "#callback DOES NOT set the shopify_user_id in the Rails session when session is offline" do + mock_session = ShopifyAPI::Auth::Session.new(shop: "shop", access_token: "token", is_online: false) + mock_oauth(session: mock_session) + get :callback, params: @callback_params + assert_nil session[:shopify_user_id] + end + + test "#callback sets encrypted cookie if API library returns cookie object" do + cookie = ShopifyAPI::Auth::Oauth::SessionCookie.new(value: "snickerdoodle", expires: Time.now + 1.day) + mock_oauth(cookie: cookie) + + get :callback, params: @callback_params + assert_equal cookies.encrypted[cookie.name], cookie.value + end + + test "#callback does not set encrypted cookie if API library returns empty cookie" do + mock_oauth + + get :callback, params: @callback_params + refute_equal cookies.encrypted[@stubbed_cookie.name], @stubbed_cookie.value + end + test "#callback starts the WebhooksManager if webhooks are configured" do ShopifyApp.configure do |config| config.webhooks = [{ topic: "carts/update", address: "example-app.com/webhooks" }] @@ -238,11 +284,7 @@ class CallbackControllerTest < ActionController::TestCase private - def mock_oauth - host = Base64.strict_encode64("#{ShopifyAPI::Context.host_name}/admin") - @callback_params = { shop: "shop", code: "code", state: "state", timestamp: "timestamp", host: host, - hmac: "hmac", } - @auth_query = ShopifyAPI::Auth::Oauth::AuthQuery.new(**@callback_params) + def mock_oauth(cookie: @stubbed_cookie, session: @stubbed_session) ShopifyAPI::Auth::Oauth::AuthQuery.stubs(:new).with(**@callback_params).returns(@auth_query) cookies.encrypted[ShopifyAPI::Auth::Oauth::SessionCookie::SESSION_COOKIE_NAME] = "nonce" @@ -253,8 +295,8 @@ def mock_oauth cookies.encrypted[ShopifyAPI::Auth::Oauth::SessionCookie::SESSION_COOKIE_NAME], }, auth_query: @auth_query) .returns({ - cookie: ShopifyAPI::Auth::Oauth::SessionCookie.new(value: "", expires: Time.now), - session: ShopifyAPI::Auth::Session.new(shop: "shop", access_token: "token"), + cookie: cookie, + session: session, }) end end diff --git a/test/controllers/concerns/ensure_billing_test.rb b/test/controllers/concerns/ensure_billing_test.rb index 07424feed..5ff375995 100644 --- a/test/controllers/concerns/ensure_billing_test.rb +++ b/test/controllers/concerns/ensure_billing_test.rb @@ -24,16 +24,13 @@ def index get "/billing", to: "ensure_billing_test/billing_test#index" end - ShopifyApp::SessionRepository.shop_storage = ShopifyApp::InMemoryShopSessionStore - ShopifyApp::SessionRepository.user_storage = ShopifyApp::InMemoryUserSessionStore - @session = ShopifyAPI::Auth::Session.new( id: "1234", shop: SHOP, access_token: "access-token", scope: ["read_products"], ) - ShopifyAPI::Utils::SessionUtils.stubs(:load_current_session).returns(@session) + @controller.stubs(:current_shopify_session).returns(@session) @api_version = ShopifyAPI::LATEST_SUPPORTED_ADMIN_VERSION ShopifyApp.configuration.api_version = @api_version diff --git a/test/generators/install_generator_test.rb b/test/generators/install_generator_test.rb index 06f8c0a11..383c74d05 100644 --- a/test/generators/install_generator_test.rb +++ b/test/generators/install_generator_test.rb @@ -36,7 +36,6 @@ class InstallGeneratorTest < Rails::Generators::TestCase scope: ShopifyApp.configuration.scope, is_private: !ENV.fetch('SHOPIFY_APP_PRIVATE_SHOP', '').empty?, is_embedded: ShopifyApp.configuration.embedded_app, - session_storage: ShopifyApp::SessionRepository, log_level: :info, logger: Rails.logger, private_shop: ENV.fetch('SHOPIFY_APP_PRIVATE_SHOP', nil), diff --git a/test/shopify_app/controller_concerns/login_protection_test.rb b/test/shopify_app/controller_concerns/login_protection_test.rb index 0d29463b8..1ec699af6 100644 --- a/test/shopify_app/controller_concerns/login_protection_test.rb +++ b/test/shopify_app/controller_concerns/login_protection_test.rb @@ -51,10 +51,16 @@ class LoginProtectionControllerTest < ActionController::TestCase ShopifyApp::SessionRepository.shop_storage = ShopifyApp::InMemoryShopSessionStore ShopifyApp::SessionRepository.user_storage = ShopifyApp::InMemoryUserSessionStore - @session = ShopifyAPI::Auth::Session.new(shop: @shop) + @token = "Bearer Grylls da token" + @session = ShopifyAPI::Auth::Session.new(id: "1", shop: @shop) ShopifyApp::SessionRepository.store_shop_session(@session) ShopifyApp.configuration.old_secret = "old_secret" + ShopifyAPI::Context.stubs(:activate_session) + ShopifyApp::SessionRepository.stubs(:load_session) + .returns(mock_session(shop: @shop)) + ::ShopifyAPI::Utils::SessionUtils.stubs(:current_session_id).returns(@session.id) + ::ShopifyAPI::Context.stubs(:activate_session) ShopifyAPI::Context.setup( api_key: ShopifyApp.configuration.api_key, @@ -74,24 +80,25 @@ class LoginProtectionControllerTest < ActionController::TestCase test "#current_shopify_session returns nil when session is nil" do with_application_test_routes do + ::ShopifyAPI::Utils::SessionUtils.stubs(:current_session_id).raises(ShopifyAPI::Errors::CookieNotFoundError) + session[ShopifyAPI::Auth::Oauth::SessionCookie::SESSION_COOKIE_NAME] = nil - get :index assert_nil @controller.current_shopify_session end end test "#current_shopify_session loads online session if user session expected" do - request.headers["HTTP_AUTHORIZATION"] = "Bearer token" + with_application_test_routes do + request.headers["HTTP_AUTHORIZATION"] = @token - ShopifyAPI::Utils::SessionUtils.expects(:load_current_session) - .with( - auth_header: "Bearer token", - cookies: { ShopifyAPI::Auth::Oauth::SessionCookie::SESSION_COOKIE_NAME => nil }, - is_online: true, - ) - .returns(nil) + ::ShopifyAPI::Utils::SessionUtils.expects(:current_session_id) + .with( + @token, + { ShopifyAPI::Auth::Oauth::SessionCookie::SESSION_COOKIE_NAME => nil }, + true, + ).returns(@session.id) + ::ShopifyAPI::Context.expects(:activate_session) - with_application_test_routes do get :index, params: { shop: @shop } end end @@ -99,13 +106,13 @@ class LoginProtectionControllerTest < ActionController::TestCase test "#current_shopify_session loads offline session if user session unexpected" do ShopifyApp::SessionRepository.user_storage = nil - request.headers["HTTP_AUTHORIZATION"] = "Bearer token" + request.headers["HTTP_AUTHORIZATION"] = @token - ShopifyAPI::Utils::SessionUtils.expects(:load_current_session) + ShopifyAPI::Utils::SessionUtils.expects(:current_session_id) .with( - auth_header: "Bearer token", - cookies: { ShopifyAPI::Auth::Oauth::SessionCookie::SESSION_COOKIE_NAME => nil }, - is_online: false, + @token, + { ShopifyAPI::Auth::Oauth::SessionCookie::SESSION_COOKIE_NAME => nil }, + false, ) .returns(nil) @@ -114,59 +121,20 @@ class LoginProtectionControllerTest < ActionController::TestCase end end - test "#current_shopify_session loads session if token is signed with new secret" do - token = mock_jwt_token(ShopifyApp.configuration.secret) - request.headers["HTTP_AUTHORIZATION"] = "Bearer #{token}" - - ShopifyAPI::Utils::SessionUtils.expects(:load_current_session) - .with( - auth_header: "Bearer #{token}", - cookies: { ShopifyAPI::Auth::Oauth::SessionCookie::SESSION_COOKIE_NAME => nil }, - is_online: true, - ) - .returns(@session) - - with_application_test_routes do - get :index - assert_equal @session, @controller.current_shopify_session - end - end - - test "#current_shopify_session loads session if token is signed with old secret" do - token = mock_jwt_token(ShopifyApp.configuration.old_secret) - request.headers["HTTP_AUTHORIZATION"] = "Bearer #{token}" - - ShopifyAPI::Utils::SessionUtils.expects(:load_current_session) - .with( - auth_header: "Bearer #{token}", - cookies: { ShopifyAPI::Auth::Oauth::SessionCookie::SESSION_COOKIE_NAME => nil }, - is_online: true, - ) - .returns(@session) - - with_application_test_routes do - get :index - assert_equal @session, @controller.current_shopify_session - end - end - test "#current_shopify_session is nil if token is invalid" do request.headers["HTTP_AUTHORIZATION"] = "Bearer invalid" with_application_test_routes do + ::ShopifyAPI::Utils::SessionUtils.stubs(:current_session_id).returns(nil) get :index assert_nil @controller.current_shopify_session end end test "#current_shopify_session is memoized and does not retrieve session twice" do - shop_session_record = ShopifyAPI::Auth::Session.new( - shop: "my-shop", - access_token: "1234", - ) with_application_test_routes do + ShopifyApp::SessionRepository.expects(:load_session).returns(@session).once get :index - ShopifyAPI::Utils::SessionUtils.expects(:load_current_session).returns(shop_session_record).once assert @controller.current_shopify_session end end @@ -174,7 +142,7 @@ class LoginProtectionControllerTest < ActionController::TestCase test "#login_again_if_different_user_or_shop removes current cookie if the session changes" do cookies.encrypted[ShopifyAPI::Auth::Oauth::SessionCookie::SESSION_COOKIE_NAME] = "cookie" - ShopifyAPI::Utils::SessionUtils.stubs(:load_current_session) + ShopifyAPI::Utils::SessionUtils.stubs(:current_session_id) .returns(ShopifyAPI::Auth::Session.new(shop: "shop", shopify_session_id: "123")) with_application_test_routes do @@ -188,7 +156,7 @@ class LoginProtectionControllerTest < ActionController::TestCase with_application_test_routes do cookies.encrypted[ShopifyAPI::Auth::Oauth::SessionCookie::SESSION_COOKIE_NAME] = "old-cookie" - ShopifyAPI::Utils::SessionUtils.expects(:load_current_session).returns( + ShopifyAPI::Utils::SessionUtils.expects(:current_session_id).returns( ShopifyAPI::Auth::Session.new(shop: "some-shop"), ).once @@ -202,7 +170,7 @@ class LoginProtectionControllerTest < ActionController::TestCase with_application_test_routes do cookies.encrypted[ShopifyAPI::Auth::Oauth::SessionCookie::SESSION_COOKIE_NAME] = "old-cookie" - ShopifyAPI::Utils::SessionUtils.expects(:load_current_session).returns( + ShopifyAPI::Utils::SessionUtils.expects(:current_session_id).returns( ShopifyAPI::Auth::Session.new(shop: "some-shop"), ).once @@ -223,11 +191,11 @@ class LoginProtectionControllerTest < ActionController::TestCase cookies.encrypted[ShopifyAPI::Auth::Oauth::SessionCookie::SESSION_COOKIE_NAME] = "cookie" request.headers["HTTP_AUTHORIZATION"] = "Bearer token" - ShopifyAPI::Utils::SessionUtils.stubs(:load_current_session) + ShopifyAPI::Utils::SessionUtils.stubs(:current_session_id) .with( - auth_header: "Bearer token", - cookies: { ShopifyAPI::Auth::Oauth::SessionCookie::SESSION_COOKIE_NAME => "cookie" }, - is_online: true, + "Bearer token", + { ShopifyAPI::Auth::Oauth::SessionCookie::SESSION_COOKIE_NAME => "cookie" }, + true, ) .returns( ShopifyAPI::Auth::Session.new(shop: @shop, scope: ["scope1"]), @@ -242,25 +210,17 @@ class LoginProtectionControllerTest < ActionController::TestCase end test "#current_shopify_session does not redirect when sufficient scope" do - ShopifyApp::SessionRepository.shop_storage.stubs(:retrieve_by_shopify_domain) - .with(@shop) - .returns(mock_session(shop: @shop)) - ShopifyAPI::Context.stubs(:scope).returns(ShopifyAPI::Auth::AuthScopes.new(["scope1"])) + with_application_test_routes do + ShopifyApp::SessionRepository.stubs(:load_session).returns(mock_session(shop: @shop)) + ShopifyAPI::Context.stubs(:scope).returns(ShopifyAPI::Auth::AuthScopes.new(["scope1"])) - cookies.encrypted[ShopifyAPI::Auth::Oauth::SessionCookie::SESSION_COOKIE_NAME] = "cookie" - request.headers["HTTP_AUTHORIZATION"] = "Bearer token" + cookies.encrypted[ShopifyAPI::Auth::Oauth::SessionCookie::SESSION_COOKIE_NAME] = "cookie" + request.headers["HTTP_AUTHORIZATION"] = @token - ShopifyAPI::Utils::SessionUtils.stubs(:load_current_session) - .with( - auth_header: "Bearer token", - cookies: { ShopifyAPI::Auth::Oauth::SessionCookie::SESSION_COOKIE_NAME => "cookie" }, - is_online: true, - ) - .returns( + ShopifyApp::SessionRepository.expects(:load_session).returns( ShopifyAPI::Auth::Session.new(shop: "some-shop", scope: ["scope1", "scope2"]), ) - with_application_test_routes do get :index, params: { shop: @shop } assert_response :ok assert_equal "cookie", cookies.encrypted[ShopifyAPI::Auth::Oauth::SessionCookie::SESSION_COOKIE_NAME] @@ -271,7 +231,7 @@ class LoginProtectionControllerTest < ActionController::TestCase with_application_test_routes do cookies.encrypted[ShopifyAPI::Auth::Oauth::SessionCookie::SESSION_COOKIE_NAME] = "old-cookie" - ShopifyAPI::Utils::SessionUtils.expects(:load_current_session).returns( + ShopifyAPI::Utils::SessionUtils.expects(:current_session_id).returns( ShopifyAPI::Auth::Session.new(shop: "some-shop"), ).once @@ -282,6 +242,7 @@ class LoginProtectionControllerTest < ActionController::TestCase test "#activate_shopify_session with no Shopify session, redirects to the login url" do with_application_test_routes do + ::ShopifyAPI::Utils::SessionUtils.stubs(:current_session_id).returns(nil) get :index, params: { shop: "foobar" } assert_redirected_to "/login?shop=foobar.myshopify.com" end @@ -290,6 +251,7 @@ class LoginProtectionControllerTest < ActionController::TestCase test "#activate_shopify_session with no Shopify session, redirects to a custom config login url" do with_custom_login_url "https://domain.com/custom/route/login" do with_application_test_routes do + ::ShopifyAPI::Utils::SessionUtils.stubs(:current_session_id).returns(nil) get :index, params: { shop: "foobar" } assert_redirected_to "https://domain.com/custom/route/login?shop=foobar.myshopify.com" end @@ -300,6 +262,7 @@ class LoginProtectionControllerTest < ActionController::TestCase shop param of referer" do with_application_test_routes do ShopifyApp.configuration.user_session_repository = nil + ::ShopifyAPI::Utils::SessionUtils.stubs(:current_session_id).returns(nil) @controller.expects(:current_shopify_session).returns(nil) request.headers["Referer"] = "https://example.com/?shop=my-shop.myshopify.com" @@ -313,6 +276,7 @@ class LoginProtectionControllerTest < ActionController::TestCase with_custom_login_url "https://domain.com/custom/route/login" do with_application_test_routes do ShopifyApp.configuration.user_session_repository = nil + ::ShopifyAPI::Utils::SessionUtils.stubs(:current_session_id).returns(nil) @controller.expects(:current_shopify_session).returns(nil) request.headers["Referer"] = "https://example.com/?shop=my-shop.myshopify.com" @@ -325,6 +289,7 @@ class LoginProtectionControllerTest < ActionController::TestCase test '#activate_shopify_session with no Shopify session, redirects to the login url \ with non-String shop param' do with_application_test_routes do + ::ShopifyAPI::Utils::SessionUtils.stubs(:current_session_id).returns(nil) params = { shop: { id: 123 } } get :index, params: params assert_redirected_to "/login?#{params.to_query}" @@ -334,6 +299,7 @@ class LoginProtectionControllerTest < ActionController::TestCase test '#activate_shopify_session with no Shopify session, redirects to a custom config login url \ with non-String shop param' do with_custom_login_url "https://domain.com/custom/route/login" do + ::ShopifyAPI::Utils::SessionUtils.stubs(:current_session_id).returns(nil) with_application_test_routes do params = { shop: { id: 123 } } get :index, params: params @@ -344,6 +310,7 @@ class LoginProtectionControllerTest < ActionController::TestCase test "#activate_shopify_session with no Shopify session, sets session[:return_to]" do with_application_test_routes do + ::ShopifyAPI::Utils::SessionUtils.stubs(:current_session_id).returns(nil) get :index, params: { shop: "foobar" } assert_equal "/?shop=foobar.myshopify.com", session[:return_to] end @@ -352,6 +319,7 @@ class LoginProtectionControllerTest < ActionController::TestCase test '#activate_shopify_session with no Shopify session, sets session[:return_to]\ with non-String shop param' do with_application_test_routes do + ::ShopifyAPI::Utils::SessionUtils.stubs(:current_session_id).returns(nil) params = { shop: { id: 123 } } get :index, params: params assert_equal "/?#{params.to_query}", session[:return_to] @@ -360,6 +328,7 @@ class LoginProtectionControllerTest < ActionController::TestCase test "#activate_shopify_session with no Shopify session, when the request is a POST, sets session[:return_to]" do with_application_test_routes do + ::ShopifyAPI::Utils::SessionUtils.stubs(:current_session_id).returns(nil) request.headers["Referer"] = "https://example.com/?id=123" post :index, params: { id: "123", shop: "foobar" } assert_equal "/?id=123&shop=foobar.myshopify.com", session[:return_to] @@ -368,6 +337,7 @@ class LoginProtectionControllerTest < ActionController::TestCase test "#activate_shopify_session with no Shopify session, when the request is an XHR, returns an HTTP 401" do with_application_test_routes do + ::ShopifyAPI::Utils::SessionUtils.stubs(:current_session_id).returns(nil) get :index, params: { shop: "foobar" }, xhr: true assert_equal 401, response.status assert_match "1", response.headers["X-Shopify-API-Request-Failure-Reauthorize"] @@ -408,7 +378,7 @@ class LoginProtectionControllerTest < ActionController::TestCase test "#fullpage_redirect_to, when the shop params is missing, sends a post message to the shop in the session" do with_application_test_routes do example_shop = "shop.myshopify.com" - ShopifyAPI::Utils::SessionUtils.expects(:load_current_session) + ShopifyApp::SessionRepository.expects(:load_session) .returns(ShopifyAPI::Auth::Session.new(shop: example_shop)) get :redirect assert_fullpage_redirected(example_shop, response) @@ -417,7 +387,7 @@ class LoginProtectionControllerTest < ActionController::TestCase test "#fullpage_redirect_to raises an exception when no Shopify domains are available" do with_application_test_routes do - ShopifyAPI::Utils::SessionUtils.expects(:load_current_session) + ShopifyAPI::Utils::SessionUtils.expects(:current_session_id) .returns(nil) assert_raise ::ShopifyApp::ShopifyDomainNotFound do get :redirect @@ -516,22 +486,4 @@ def with_custom_login_url(url) ensure ShopifyApp.configure { |config| config.login_url = original_url } end - - def mock_jwt_payload - { - "iss" => "https://#{@shop}/admin", - "dest" => "https://#{@shop}", - "aud" => ShopifyApp.configuration.api_key, - "sub" => "123", - "exp" => 1.day.from_now.to_i, - "nbf" => 1.day.ago.to_i, - "iat" => Time.now.to_i, - "jti" => "abc", - "sid" => "abc123", - } - end - - def mock_jwt_token(secret) - ::JWT.encode(mock_jwt_payload, secret, "HS256") - end end diff --git a/test/test_helper.rb b/test/test_helper.rb index cf78da916..ce758d969 100644 --- a/test/test_helper.rb +++ b/test/test_helper.rb @@ -56,6 +56,7 @@ def mock_session(shop: "my-shop.myshopify.com", scope: ShopifyApp.configuration. mock_session.stubs(:shop).returns(shop) mock_session.stubs(:access_token).returns("a-new-user_token!") mock_session.stubs(:scope).returns(ShopifyAPI::Auth::AuthScopes.new(scope)) + mock_session.stubs(:shopify_session_id).returns(1) mock_session end