Getting user permissions from Admin App Extension

I have an embedded app and an associated Admin UI Extension. The extension allows users to edit orders.

My app requires the write_orders scope when installing.

A client needs the admin UI extension to be disabled for users that don’t have the write_orderspermission, but they want to still give them full access to the embedded app.

How do I get the users access scopes from the admin UI extension?

Here is what I tried to do:

  • use the built-in authenticated fetch to hit my backend from the extension
  • do a token exchange for an online session token
  • check the session.onlineAccessInfo.associated_user_scope for write_orders.

However, when I do that, the associated_user_scope exactly matches the access_scopes of my app - which means every user has write_orders access. Which seems weird, because according to the documentation here on online access tokens, it should be scoped to the logged in user.

The only thing I can think of is that I have direct_api_mode = “offline” set up in my root app config .toml. However, this direct offline access is a requirement for my app, so it can’t be changed.

Is there a similar setting in the shopify.extension.toml file? Shouldn’t we still be able to do an online token exchange on the server? Or in the global shopify object (on the client) it would be nice to generate an offline or online idToken.

Hi @ozzyonfire

Did you know about the AppBridge’s scopes API?

Docs: Scopes API

As long as a scope is defined as optional in your shopify.app.toml then you can request it dynamically from your embedded app:

const response = await shopify.scopes.request([
  'write_orders',
]);

if (response.result === 'granted-all') {
  console.log('Merchant granted access');
} else if (response.result === 'declined-all') {
  console.log('Merchant declined access');
}

console.log(response.detail.granted);

I use this approach to conditionally ask for scopes only if the customer tries to enable a feature in my app that requires it.

Thanks for the reply, this looks really cool, but I don’t think it helps me in my situation.

  1. This looks like it’s for AppBridge, but doesn’t look like it’s supported in the Admin UI Extension
  2. This would request the permission for my app, which once granted, would put me in the same position.

But this did make me look into the shopify global variable inside the Admin UI Extension and I can see that there is a .session() function attached to shopify.auth. It’s not documented, but I wonder what this does and if I can get a proper online session from it?

I just did some quick testing around this and it seems like it does the same thing as the idToken() call…

Before suggesting an approach, a few things would help narrow down the right answer — happy to dig in further once you can share more context:

Clarifying questions

  1. What does “disabled” mean to you? A few flavors are possible, with different mechanisms:
    • Hidden entirely (no entry point in the admin UI).
    • Visible but collapsed/empty (the user sees “this exists but isn’t available right now”).
    • Visible and rendered, but read-only or with a “you don’t have permission” message.
  2. What kind of extension is it? Admin action, admin block, admin link, modal? should_render only exists for admin actions, so the available levers vary by target.
  3. Is the embedded app currently letting this same user write orders? With direct_api_mode = “offline”, the app’s direct API calls run as the app’s offline token, not the user’s — so a user without write_orders may already be able to write orders through the embedded app today. Whether that’s intentional changes the shape of the answer:
    • If intentional: the extension is being held to a different permission model than the app (deferring to Shopify’s role system instead of the app’s own logic). Worth confirming that’s the deliberate stance.
    • If unintentional: the underlying ask might actually be broader than the extension.
  4. What’s driving the requirement — compliance, UX, or security? Compliance usually means hide entirely; UX often means a disabled state with explanation; security requires backend enforcement regardless of UI
    choice. Knowing which one is the actual goal helps pick the right mechanism (and avoids over-engineering the others).
  5. Is write_orders the one scope you care about, or representative of a broader pattern? If it’s a recurring need across scopes, a generic permission-check helper is probably the right design rather than a
    one-off check.

Potentially related docs

Once you can share answers (especially #3), I can put together a more concrete recommendation.

Hi Bill,

Thanks for the response. I’ll try my best to answer your questions and provide a little more context.

  1. I was thinking of just rendering a message saying you don’t have permission to use this tool.
  2. should_render api looks cool. Didn’t know this existed… The use case matches mine pretty much exactly mentioning user permissions, but I don’t really know how to get those permissions. The extension renders as a modal on the admin order page.
  3. Yes the embedded app let’s users create orders. The access_scope for creating or editing orders is the same write_orders . Also, some customers like their users to only create orders using my app and no other mechanism.
  4. Driving requirement sounds like a mix of compliance and UX. Disabling the extension via the should_render API would solve this.
  5. A permission check helper would be great. I only use write_orders because that’s all I expect that I get access too. For example, I have another tool in the app that allows users to delete orders, however some stores don’t want thier users to delete orders - they remove this permission in their user settings on their store, but the only access scope I see is write_orders so if something more granular exists that would be great!

I’ve read through all the docs, maybe I am mis-interpreting what the online sessions tokens should do, but I can do an online token exchange for a user that does not have permission to write orders (according to the Shopify Users Permissions) and the resulting associated_user_scope will include write_orders. Maybe this is a bug?

User permissions:

Same user after online token exchange:

[shopify-api/INFO] Creating new session | {shop: matts-tech-test.myshopify.com, isOnline: true}
12:56:18 │      web-frontend-backend │ associated_user_scope
read_all_orders,read_locations,unauthenticated_read_product_listings,unauthenticated_write_checkouts,unauthenticated_write_customers,write_assigned_fulfillment_orders,write_merchant_managed_fulfillment_orders,write_orders,write_payment_terms,write_third_party_fulfillment_orders,read_order_edits

Is there an API call for getting specific users permissions? down to the granulated items that are in the screenshot?

@Bill-Shopify Any update on this? Sorry to be a pain, but I have customers that are eager to get this resolved.

@Bill-Shopify Bumping this again… Let me know if you need any more information from me.

Any update here? Would love to hear from someone…