Skip to main content
All guides

Security and access

Permission models for multi-location businesses

Once a business runs more than one location, roles stop being enough. Scoped access is one of the few requirements that genuinely multiplies the cost of a system, and it is almost never in the initial brief.

Updated March 23, 2026 · 8 min read

The short answer

  • With more than one location, the authorization question is not whether a user can edit jobs but whether they can edit this job, which means every permission check needs a subject as well as a verb.
  • Scope composes along several dimensions at once (location, region, business unit, customer, and the user's own records), so a user's effective access is the resolved combination, not a single role name.
  • Enforce scope in the data access layer so that unscoped queries are impossible to write, and treat interface-level hiding as presentation only, never as a control.
  • Aggregate reporting leaks information that detail views correctly withhold, so totals, counts, and benchmarks need their own scoping and minimum-cohort rules.
  • Retrofitting scoped access into a live system touches every query, screen, report, export, and test, which is why it belongs in the design phase rather than a later release.

Most systems start with one location and a short list of roles: admin, manager, staff. That model holds until the business opens a second site, acquires a competitor, signs its first franchisee, or reorganizes into business units. At that point the role list stops describing anything useful, because two people can hold the identical role and still have no business seeing each other's data. Scoped access is one of the few requirements that genuinely multiplies the cost of a system, and it is almost never written down in the initial brief.

Roles answer the wrong question

Plain role-based access control answers a question about verbs. Can this user edit jobs? Can this user issue refunds? Can this user view payroll? In a single-location business that is sufficient, because there is only one set of jobs, refunds, and payroll records, and holding the permission is the same thing as being allowed to act.

Add a second location and the question changes shape. It is no longer whether the user can edit jobs; it is whether they can edit this job. The permission now needs a subject. A store manager at one site and a store manager at another hold the same role, run the same screens, and must never touch each other's work orders, customers, inventory counts, or timesheets. The role tells you what kind of action is allowed. Something else has to tell you which records it applies to.

The usual first attempt is to add a location column to the user record and filter by it. That survives about a year. It fails the first time someone needs access to two sites, the first time a regional role appears, and the first time a person holds a different job at different locations: a technician at one branch who also manages another. A user's relationship to the business is not one value; it is a set of grants, each attached to a scope.

The dimensions that scope access

Scope is rarely one-dimensional. Most operating businesses need several dimensions at once, and the design problem is how they compose rather than which one to pick. The effective answer for a given user, action, and record is the resolution of all of them together.

Scoping dimensionExample ruleWhere it must be enforcedCommon mistake
LocationA store manager may edit work orders belonging to the sites they are assigned to, and read nothing at other sites.Query layer: every read and write on location-owned tables carries the caller's permitted location set.Storing a single location on the user record, which cannot express a manager who covers two sites.
Region or districtA district manager reads operational data for every site in their district and writes only at sites where they hold an explicit role.Query layer, plus a separate write check, because the read scope and the write scope are different sets.Treating read access as implying write access, so a reporting role silently becomes an editing role.
Business unit or brandA user in the commercial division may not see residential jobs, even at a shared location.Domain model: the unit is an attribute of the record, not of the site, and must be resolved independently of location.Assuming the org chart is a strict tree, when shared sites and dual-branded operations make it a graph.
Franchise or tenantA franchisee sees full detail for their own operation and only aggregate, anonymized benchmarks for the network.Data access layer for detail, and a separate aggregation service with a minimum-cohort rule for benchmarks.Building benchmarks from the same query path as detail views, which leaks identifiable figures at small cohort sizes.
Customer or accountAn external portal user sees only the jobs, invoices, and documents belonging to their own account.Data access layer, with external identities held in a separate scope model from employee identities.Reusing the internal role model for external users, so an internal role change quietly widens portal access.
Ownership of the recordA technician may edit the work orders assigned to them and read the rest of the day's schedule at their site.Row-level rule combining assignment with location, evaluated on every read, write, and export path.Implementing ownership only in the list screen, leaving direct record access open to anyone with the link.
Scoping dimensions, and where each one has to be enforced

Composition needs an explicit rule, decided once and written down. The workable default is that grants are additive within a dimension and restrictive across them: a user's permitted locations are the union of their grants, and a record is visible only if it satisfies every applicable dimension. Deny should win over allow, and an absent grant should mean no access rather than full access. These are ordinary decisions, but if they are made implicitly by whoever writes the first query, the system will contradict itself within months.

The awkward cases that break naive models

Every multi-location business has a handful of situations that a simple model cannot express. They are not edge cases. They are the normal operating reality, and they should be used as the acceptance tests for the design.

  • A manager covering a second site for six weeks. The grant needs a start and end date, because access that has to be removed manually will not be removed.
  • A regional role that reads everywhere and writes nowhere. Read scope and write scope are separate sets, and collapsing them into one hierarchy is the most common structural error in these models.
  • A franchisee who must see network benchmarks but never another franchisee's detail. Two different access paths to related numbers, with different rules.
  • A head-office analyst who legitimately needs everything. Unrestricted scope should be an explicit, named, logged grant held by few people, not the accidental result of an empty filter.
  • A person who holds different roles at different sites: a senior technician at one, a manager at another. The user's role is meaningless without the location it applies to.
  • A location that closes or is sold. Historical records still need scoped access for people who no longer work anywhere current.
  • A support engineer acting on behalf of a customer. Impersonation is a legitimate feature and needs its own grant, time limit, and audit record.

The pattern underneath all of these is that a grant is a small object in its own right: an identity, a role, a scope, and a validity period. Once grants are modeled that way, temporary cover, read-only regional access, and delegated support all become data rather than code changes.

Enforce scope where the data is read

Scope has to be enforced at the data access layer, the place where queries are constructed, not in the interface. An interface can only hide what it happens to render. It cannot protect an API route called directly, a report generator, a scheduled export, a webhook handler, a bulk action, or the next screen someone builds in a hurry.

The failure mode is a hidden menu item mistaken for a permission. A button is removed for users who should not perform an action, everyone agrees the action is now restricted, and the underlying endpoint remains open to any authenticated user who reaches it. The interface should reflect a decision made elsewhere, not be the place the decision is made.

Practically, that means a single authorization component that answers a clear allow-or-deny question for a given identity, action, and record, and a data access layer that applies the caller's scope automatically. Resolve scope once per request from grants rather than recomputing it in each query. Fail closed when scope cannot be determined. Cover the model with tests that assert denial as carefully as they assert access, including tests written from the perspective of a user who should see nothing.

Designing this as a dedicated, well-tested domain concern, rather than sprinkling permission checks through the UI, keeps authorization consistent and auditable as the product grows.
Rebuilding for the next decade, an anonymized B-Team engagement

Reporting leaks what detail correctly hides

Aggregation is where carefully scoped systems give data away. A franchisee who cannot open another franchisee's job list can still learn a great deal from a network average if the cohort is small enough, or from a ranked leaderboard, or from a total that changes when one site is added to a filter. The detail view is closed and the summary is open, and both are working exactly as built.

Treat every aggregate as a disclosure decision. Reports need their own scope resolution rather than inheriting whatever the underlying tables allow. Apply a minimum cohort size before a comparative figure is shown, suppress rather than round when the cohort is too small, and be careful with any control that lets a viewer change the population of a comparison, since differencing two permitted aggregates can reconstruct a value neither was meant to reveal. Exports and scheduled reports deserve particular attention, because they run outside a user session and often under a service account with broader access than the person receiving the file.

Why retrofitting is so expensive

Adding scoped access to a system that was built without it is not a feature. It is a change to every query in the codebase, every screen that renders a list, every report, every export, every integration that reads on a user's behalf, and every test that assumed a global view of the data. There is no partial version that is safe, because a single unscoped query in a rarely used screen undoes the work everywhere else.

The retrofit also arrives with a data problem. Historical records often have no reliable location, unit, or owner attached, because nothing ever required one, so the migration includes deciding who owns a decade of ambiguous rows. Then the organizational questions surface for the first time (whether a district manager should approve spending outside their district, whether a franchisee's data belongs to the franchisee), and those take longer to settle than the code does.

The cheap version of this work happens during design, before there are records to reclassify or queries to audit. Ask early how many locations, regions, brands, and external parties the business expects to have, and whether anyone will ever need to see across them. If the answer is more than one of anything, model scope as a first-class concern from the start. It is the difference between a design constraint and a rewrite.

Common questions

Is role-based access control enough for a multi-location business?
On its own, no. Roles define which actions a user may perform, but a multi-location business also has to answer which records those actions apply to. You need roles combined with scope (location, region, business unit, customer, or ownership) resolved together for each request.
Where should location scoping be enforced?
In the data access layer, where queries are built, so that every read and write carries the caller's permitted scope automatically. Interface-level hiding is presentation, not a control, because it does not protect API routes, exports, scheduled reports, or bulk actions. Make the scoped path the default and require an explicit, reviewable exception for anything broader.
How do you handle a manager who temporarily covers a second location?
Model access as time-bounded grants rather than a single location field on the user record. A grant carries an identity, a role, a scope, and a validity period, so temporary cover expires on its own. Access that depends on someone remembering to remove it will eventually be left in place.
Can a regional manager have read access without write access?
Yes, and this is a requirement worth designing for explicitly. Read scope and write scope should be separate sets rather than levels of one hierarchy, so a regional role can read every site in its region while writing only where an explicit role is held. Collapsing the two is one of the most common structural mistakes in these models.
How do you show franchise benchmarks without exposing another franchisee's data?
Serve aggregates through a separate path from detail views, with their own scope rules. Apply a minimum cohort size before any comparative figure is shown, suppress the figure rather than rounding it when the cohort is too small, and restrict controls that let a viewer change the comparison population, since differencing two permitted totals can reconstruct a hidden value.
How much does it cost to add scoped permissions later?
Substantially more than building it in, because the change reaches every query, list screen, report, export, integration, and test in the system. It also requires assigning ownership to historical records that were never captured with a location or business unit, and settling organizational questions that were never asked. Design for scope at the start if the business has, or expects, more than one location.

Want this applied to your operation?

The first conversation is a no-cost fit discussion about the problem, its importance, and the people involved. We respond within one business day.

Start a conversation