Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Handle invalid session token #1823

Merged
merged 10 commits into from
Apr 19, 2024
Merged

Handle invalid session token #1823

merged 10 commits into from
Apr 19, 2024

Conversation

zzooeeyy
Copy link
Contributor

@zzooeeyy zzooeeyy commented Apr 3, 2024

What this PR does

  • Handle invalid session token errors
  • Redirect to app-bridge-next bounce page if session token is invalid & Auth header doesn't exist
  • Respond with 401 if auth header exists and session token is invalid. If the app's front-end is using CDN version of app bridge next, it'll automatically be retried. otherwise, the 401 error will be exposed.

Tophatting-

Checklist

Before submitting the PR, please consider if any of the following are needed:

  • [ will update once feature is released ] Update CHANGELOG.md if the changes would impact users
  • [will update once feature is released ] Update README.md, if appropriate.
  • [will update once feature is released ] Update any relevant pages in /docs, if necessary

@zzooeeyy zzooeeyy force-pushed the handle-invalid-session-token branch 2 times, most recently from e3e43cf to eeb490b Compare April 10, 2024 15:49
@zzooeeyy zzooeeyy force-pushed the handle-invalid-session-token branch from fadc394 to 815f60a Compare April 16, 2024 22:54
@zzooeeyy zzooeeyy force-pushed the handle-invalid-session-token branch from 815f60a to 3bcaff0 Compare April 16, 2024 22:58
@zzooeeyy zzooeeyy force-pushed the handle-invalid-session-token branch from 659f8a3 to 6ff99df Compare April 16, 2024 23:29
@zzooeeyy zzooeeyy force-pushed the handle-invalid-session-token branch from 6ff99df to b65aac4 Compare April 16, 2024 23:33
@zzooeeyy zzooeeyy marked this pull request as ready for review April 17, 2024 19:26
@zzooeeyy zzooeeyy requested a review from a team as a code owner April 17, 2024 19:26
if ShopifyApp.configuration.use_new_embedded_auth_strategy?
include ShopifyApp::TokenExchange
include ShopifyApp::EmbeddedApp
around_action :activate_shopify_session
Copy link
Contributor

Choose a reason for hiding this comment

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

I noticed that the docs mention EnsureInstalled provides the installed_shop_session method and EnsureHasSession provides current_shopify_session. I think TokenExchange#activate_shopify_session provides current_shopify_session, right? Should it be different for EnsureInstalled? Or maybe provide both methods?

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Yess, so EnsureInstalled concern itself implements the installed_shop_session method, so it'll still be able to be used. Having TokenExchange enabled, they'll just get both 'current_shopify_sessionandinstalled_shop_session` !

retrieve_session_from_token_exchange if current_shopify_session.blank? || should_exchange_expired_token?
begin
retrieve_session_from_token_exchange if current_shopify_session.blank? || should_exchange_expired_token?
rescue *INVALID_SHOPIFY_ID_TOKEN_ERRORS => e
Copy link
Contributor

Choose a reason for hiding this comment

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

Could we rescue these errors for the entire activate_shopify_session method? with_token_refetch might also end up raising this error, since it performs token exchange.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

I was pairing with Mike and we thought that's not worth it since we already check for validity before we call token exchange, it'd just be adding extra work for the slightest chance that it might expire within that timeframe... There are leeways in-place to ensure we don't encounter these problems..?

Copy link
Contributor

Choose a reason for hiding this comment

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

Oh but what if we don't perform token exchange there because current_shopify_session exists and then do during with_token_refetch? In that case it wouldn't be double validation/rescue, right?

Copy link
Contributor Author

Choose a reason for hiding this comment

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

hmmm I think this will cause a "Double render/redirect" error since this is around the controller action.. so if we handle the error and respond with retry or redirect to bounce page and the controller action also renders... 🤔

Copy link
Contributor

Choose a reason for hiding this comment

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

If the action raises a token exchange error it will likely not have rendered yet, but you're right that it could happen if the app doesn't follow the pattern of rendering/redirecting as the last thing in the action 🤔

I looked up and saw there's a performed? method we can call in the controller to avoid a double render/redirect. What if we bounce unless performed?

test/routes/sessions_routes_test.rb Show resolved Hide resolved
@@ -178,13 +183,48 @@ class TokenExchangeControllerTest < ActionController::TestCase
end
end

[
ShopifyAPI::Errors::InvalidJwtTokenError,
ShopifyAPI::Errors::CookieNotFoundError,
Copy link
Contributor

Choose a reason for hiding this comment

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

Maybe we shouldn't respond to invalid cookies with a bounce page now that we'll be using the method that exclusively checks shopify_id_token.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Yea once I use the new utility method, we won't be handling CookieNotFoundError error anymore, but it'll be checking for MissingJwtTokenError instead

Comment on lines 196 to 197
expected_redirect_url = "/my-root/patch_shopify_id_token?my_param=for-keeps&shop=my-shop.myshopify.com"
expected_redirect_url += "&shopify-reload=%2Freloaded_path%3Fmy_param%3Dfor-keeps%26shop%3Dmy-shop.myshopify.com"
Copy link
Contributor

Choose a reason for hiding this comment

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

Nit: maybe we could URL encode the param for better readability of the test?

Suggested change
expected_redirect_url = "/my-root/patch_shopify_id_token?my_param=for-keeps&shop=my-shop.myshopify.com"
expected_redirect_url += "&shopify-reload=%2Freloaded_path%3Fmy_param%3Dfor-keeps%26shop%3Dmy-shop.myshopify.com"
reload_url = CGI.escape("/reloaded_path?my_param=for-keeps&shop=#{@shop}")
expected_redirect_url = "/my-root/patch_shopify_id_token?my_param=for-keeps&shop=my-shop.myshopify.com&shopify-reload=#{reload_url}"

Copy link
Contributor Author

Choose a reason for hiding this comment

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

🤯 NICE!

# redirect_to("#{patch_session_token_url}?#{patch_session_token_params.to_query}", allow_other_host: true)
# end
redirect_to(
"#{patch_shopify_id_token_url}?#{patch_shopify_id_token_params.to_query}",
Copy link
Contributor

Choose a reason for hiding this comment

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

I wish there was a way to use the Rails URL helper method patch_shopify_id_token_path here which already does most of the URL assembly work for us, but the way our tests are setup using with_routing it's not including engine's routes, so I think it would be a bit too much work for it to be worth it.

@@ -5,8 +5,18 @@ module TokenExchange
extend ActiveSupport::Concern
include ShopifyApp::AdminAPI::WithTokenRefetch

INVALID_SHOPIFY_ID_TOKEN_ERRORS = [
ShopifyAPI::Errors::CookieNotFoundError,
Copy link
Contributor

Choose a reason for hiding this comment

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

Would we ever have a cookie present for token exchange? If it's only meant to run for embedded apps there should never be any cookies involved, right?

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Yea cookies are never involved.. but the way ShopifyAPI::current_session_id is setup, it'll throw a "CookieNotFoundError" if we pass in a nil auth header (which will be the case for the initial server load when we're not using id_token from URL params), so once I change this to use the new util method, we won't be catching this CookieNotFoundError but MissingJwtTokenError instead.

Copy link
Contributor

Choose a reason for hiding this comment

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

Hmm that's fair. I guess we can't change this without it potentially breaking apps out there, right?

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Yea exactly :(

Copy link
Contributor

@rachel-carvalho rachel-carvalho left a comment

Choose a reason for hiding this comment

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

Looks good to me, thanks Zoey!

@zzooeeyy zzooeeyy merged commit ea884a6 into main Apr 19, 2024
10 checks passed
@zzooeeyy zzooeeyy deleted the handle-invalid-session-token branch April 19, 2024 18:16
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

Successfully merging this pull request may close these issues.

3 participants