Implement Shopify Plus access control patterns with staff permissions, multi-location management, and Shopify Organization features. Use when building apps for Shopify Plus merchants, implementing per-staff permissions, or managing multi-store organizations. Trigger with phrases like "shopify permissions", "shopify staff", "shopify Plus organization", "shopify roles", "shopify multi-location".
Implement role-based access control for Shopify Plus apps using Shopify's staff member permissions, multi-location features, and Organization-level access.
read_users scope for querying staff permissionsQuery staff members via GraphQL to get their access scopes, then map those scopes to app-level roles (admin, manager, fulfillment, viewer). Staff permissions mirror app scopes like read_products, write_orders, etc.
See Staff Query and Role Mapping for the complete GraphQL query, role definitions, and matching logic.
In embedded apps, use online access tokens to get per-staff permissions from session.onlineAccessInfo. For Shopify Plus stores with multiple locations, restrict fulfillment and inventory operations to authorized locations per user.
See Permission Middleware and Location Access for Remix loader examples and location access control.
Shopify Plus Organization API enables multi-store management with organization-level, store-level admin, and store-level staff roles. Log all access decisions (allowed and denied) for compliance auditing.
See Organization API and Audit Trail for the Organization query and audit implementation.
| Issue | Cause | Solution |
|---|---|---|
No onlineAccessInfo | Using offline token | Use online access tokens for per-user permissions |
| Staff can't access feature | Merchant restricted their permissions | Staff must request access from store owner |
| Organization API 403 | Not on Shopify Plus | Organization features require Plus plan |
| Location not found | Location deactivated | Query active locations before operations |
// Remix action with permission guard
export async function action({ request }: ActionFunctionArgs) {
const { admin, session } = await authenticate.admin(request);
const role = determineRole(
session.onlineAccessInfo?.associated_user_scope?.split(",") || []
);
if (!canPerformAction(role, "manage_products")) {
return json({ error: "Insufficient permissions" }, { status: 403 });
}
// ... perform the action
}