-
Notifications
You must be signed in to change notification settings - Fork 706
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
Add token exchange concern #1817
Conversation
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Had some general questions, not necessarily things we need / want to apply.
|
||
if ShopifyApp.configuration.use_new_embedded_auth_strategy? | ||
include ShopifyApp::RetrieveSessionFromTokenExchange | ||
around_action :activate_shopify_session |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Just wondering - is this repeated in both paths because we need the after_action
to kick in after this in the else
branch?
Not that I mind the repetition, just curious.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I repeated it so that it's explicit in which action is called, if we leave this as a shared/common action outside of this condition, then we're implicitly forcing the method name to be the same between the 2 concerns. Which would also work, but I feel like less implicit dependency the better? 🤔
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Yeah I agree - less magic, more good.
@@ -6,14 +6,20 @@ module EnsureHasSession | |||
|
|||
included do | |||
include ShopifyApp::Localization | |||
include ShopifyApp::LoginProtection | |||
|
|||
if ShopifyApp.configuration.use_new_embedded_auth_strategy? |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I believe these conditions should only apply if the app is embedded, right? We'd still want to use LoginProtection
for non-embedded?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Yes that's right, use_new_embedded_auth_strategy
is only true if app is embedded AND has wip_new_embedded_auth_strategy
enabled.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Oh perfect, I missed that part :)
# frozen_string_literal: true | ||
|
||
module ShopifyApp | ||
module RetrieveSessionFromTokenExchange |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Could we call this just TokenExchange
? Would that be confusing?
module RetrieveSessionFromTokenExchange | ||
extend ActiveSupport::Concern | ||
|
||
def activate_shopify_session |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
A couple of these methods seem to be the same for both cases, right? Should we create one or more helper concerns for them?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
yes and no --?
Some of the functions might change as we add more error handling (e.g. current_shopify_session might raise and rescue error so we can redirect to bounce page to get session token)
Some methods are only extracted to call 1 single method from the ShopifyAPI, which I think we should keep as is to reduce even more magic from more concerns..
I think the only method that makes sense is online_token_configured?
this doesn't seem like it belongs in the concerns but in the configuration itself.. I've extracted this one so we don't check the business logic from the concerns - bcc2e1e
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
That's fair - I think if we expect the code to change then it won't make sense to extract them. We can live with some minor duplications here, as long as we're aware we'll need to consider both cases when making changes.
# TODO: Right now JWT Middleware only updates env['jwt.shopify_domain'] from request headers tokens, | ||
# which won't work for new installs. | ||
# we need to update the middleware to also update the env['jwt.shopify_domain'] from the query params |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This should happen as part of this PR, right?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Hmmm not necessarily. We won't always have session token in the query params (beta flag can still be turned off), so our feature shouldn't rely on having session tokens from the params. Thus I think that could be handled as a part of another ticket to add support for using id_token
from param.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
But won't that mean apps won't be able to do the first install flow with the setting turned on?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Yess, so the EnsureInstalled
concern doesn't work yet. We'll have to handle reloading from AppBridge with session token in request headers (part of this ticket: https://github.com/Shopify/develop-app-access/issues/145) to make the first install flow work. That's why EnsureInstalled
doesn't work with token exchange yet
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
That makes sense
# respond_to_invalid_session_token | ||
return | ||
rescue ShopifyAPI::Errors::HttpResponseError => error | ||
ShopifyApp::Logger.info( |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Should we log these error messages as info
?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Good catch, I'll change them to :error!
5e741e5
to
4fbe18c
Compare
4fbe18c
to
b53fe3d
Compare
# TODO: Right now JWT Middleware only updates env['jwt.shopify_domain'] from request headers tokens, | ||
# which won't work for new installs. | ||
# we need to update the middleware to also update the env['jwt.shopify_domain'] from the query params |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
That makes sense
module RetrieveSessionFromTokenExchange | ||
extend ActiveSupport::Concern | ||
|
||
def activate_shopify_session |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
That's fair - I think if we expect the code to change then it won't make sense to extract them. We can live with some minor duplications here, as long as we're aware we'll need to consider both cases when making changes.
I think this makes sense, assuming the TODOs will be resolved before we release :) |
This looks good to me! One question: should we have unit tests around the |
Great callout, we should definitely add some tests for the new paths. |
Yes we totally should, I was going to tackle that after we handle all the special invalid token cases. That way we can ensure we have all the test cases and ways to recover in 1 go... I've added that another task so we don't forget about it: |
I'd encourage you to test the happy path in the happy path PR, and the sad path in the sad path PR! Doing all the tests at once can make it harder to discover what caused errors, whereas doing them more iteratively makes it more obvious when a change is breaking an assumption. |
Thanks @ragalie & @paulomarg -- I've updated this PR with tests! |
|
||
test "Exchanges offline token when session doesn't exist" do | ||
with_application_test_routes do | ||
ShopifyAPI::Utils::SessionUtils.expects(:current_session_id).twice.with( |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I'd maybe go with a stubs
here instead of an expectation. It doesn't seem essential to the test outcome that this gets called twice; that's an implementation detail. The thing we care about is that the exchange_token
happens once, which you're expecting below. What do you think?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Sure I don't disagree with that!
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Left a comment on test tactics but otherwise LGTM!
What this PR does
Tophat:
📹 https://videobin.shopify.io/v/J4j8l2
Happy path for token exchange
Note on what else needs to be implemented in other tasks:
id_token
from URL params.Impact on existing apps:
config.wip_new_embedded_auth_strategy
Checklist
Before submitting the PR, please consider if any of the following are needed:
CHANGELOG.md
if the changes would impact usersREADME.md
, if appropriate./docs
, if necessary