Closed TheAbyss closed 2 months ago
Hi there 👋
I just wanted to clarify a few things, to make sure we are all on the same page about your current functionality and what you are hoping to do! 😃
Currently, we store and use associated user info for multi-store permission controls/navigation, fraud prevention, and onboarding via Online Access Token Exchange. This approach forces many redirects degrading admin portal performance.
With token exchange we wouldn't expect to see the redirects, that sounds like instead you are doing Authorization code grant with online access tokens.
With the Shopify app gem you can do both formats of OAuth, Auth Code Grant and Token Exchange. With either online or offline access tokens. But it is not necessary to use the Shopify App gem. These can both be implemented without the app gem. (Token exchange, Auth code grant) So in summary there are 4 different scenarios
And these 4 scenario can be implemented manually or you can use the Shopify App gem.
Could you confirm what OAuth flow you are doing are doing in each scenario? Could you also confirm in your current OAuth flow with the redirects, do you currently see the same user id across shops.
@lizkenyon the main issues are:
UserSessionStorageWithScopes
module there is no saving of the user info including name etc so we had to write our own module named UserSessionStorageWithScopesAndAssociatedUser
which you can see above that actually saves the name, email etc.Thank you for clarifying! @eni9889
For issue 1 and possibly 2 this sounds like an issue with the API itself. I am going to flag this with the ownership team, to look into.
For 3 I will triage this ticket for my team to look into.
Hi @eni9889,
I just checked the code that generates the response for the access_token
endpoint, and the id
that is returned for the user should be specific to the shop (i.e. it will be different for each shop). This behaviour is true for both the authorization code flow (the one that uses the callback) and the token exchange flow.
I also verified that the collaborator
value should be correct (and the same) for both the authorization code flow and token exchange.
If you have a specific user/shop example you can DM it to me on the Partners Slack and I can take a closer look.
Thanks!
Issue summary
Before opening this issue, I have:
shopify_app
version: latestBackground
Our biggest technical roadblock to migrate to BFS (Built for Shopify) is identifying associated user information on the Shopify App gem. Currently, we store and use associated user info for multi-store permission controls/navigation, fraud prevention, and onboarding via Online Access Token Exchange. This approach forces many redirects degrading admin portal performance.
Therefore, we are switching to utilize Shopify App Gem/SDK to meet and exceed Built for Shopify spirit and requirements. But we ran into a couple issues that don't allow us to migrate our Shopify authentication flow:
Expected behavior
associated_user.id
is the same value between Online Access Token Exchange vs Shopify App Gem approaches & value doesn't change between associationsassociated_user.id
for each shop associationcollaborator
boolean is true when associated user is a collaborator of a shopBONUS: expect
staff
boolean field to complete the associated_user roles set:staff end
Actual behavior
associated_user.id
is the different value between Online Access Token Exchange vs Shopify App Gem approaches & value changes between associationsassociated_user.id
for each shop associationassociated_user.id
collaborator
boolean always returns falsestaff
boolean field, the remaining role outside of collaborator and ownerSteps to reproduce the problem
Online Access Token Exchange approach to getting Associated User
Shopify App Gem approach to getting Associated User
associated_user
attributes will return nilincluded do validates :shopify_domain, presence: true end
class_methods do def store(auth_session, logged_in_user) Rails.logger.debug "Storing user session - user: #{logged_in_user.inspect} - auth_session: #{auth_session.inspect}"
user = find_or_initialize_by(shopify_user_id: logged_in_user.id) user.shopify_token = auth_session.access_token user.shopify_domain = auth_session.shop user.access_scopes = auth_session.scope.to_s user.expires_at = auth_session.expires
user.first_name = logged_in_user.first_name user.last_name = logged_in_user.last_name user.email = logged_in_user.email user.email_verified = logged_in_user.email_verified user.account_owner = logged_in_user.account_owner user.locale = logged_in_user.locale user.collaborator = logged_in_user.collaborator
user.save! user.id end
def retrieve(id) user = find_by(id: id) construct_session(user) end
def retrieve_by_shopify_user_id(user_id) user = find_by(shopify_user_id: user_id) construct_session(user) end
def destroy_by_shopify_user_id(user_id) destroy_by(shopify_user_id: user_id) end
private
def construct_session(user) return unless user
associated_user = ShopifyAPI::Auth::AssociatedUser.new( id: user.shopify_user_id, first_name: user.first_name, last_name: user.last_name, email: user.email, email_verified: user.email_verified, account_owner: user.account_owner, locale: user.locale, collaborator: user.collaborator, )
ShopifyAPI::Auth::Session.new( shop: user.shopify_domain, access_token: user.shopify_token, scope: user.access_scopes, associated_user_scope: user.access_scopes, associated_user: associated_user, expires: user.expires_at, ) end end
def access_scopes=(scopes) super(scopes) rescue NotImplementedError, NoMethodError raise NotImplementedError, "#access_scopes= must be defined to handle storing access scopes: #{scopes}" end
def access_scopes super rescue NotImplementedError, NoMethodError raise NotImplementedError, "#access_scopes= must be defined to hook into stored access scopes" end
def expires_at=(expires_at) super rescue NotImplementedError, NoMethodError if ShopifyApp.configuration.check_session_expiry_date raise NotImplementedError, "#expires_at= must be defined to handle storing the session expiry date" end end
def expires_at super rescue NotImplementedError, NoMethodError if ShopifyApp.configuration.check_session_expiry_date raise NotImplementedError, "#expires_at must be defined to check the session expiry date" end
nil end end end