How we built authorization as a platform: Lessons from scaling fine-grained access controls at Vanta | Vanta
BlogEngineering
April 16, 2026

How we built authorization as a platform: Lessons from scaling fine-grained access controls at Vanta

Written by
Eeshan Agarwal
Reviewed by
No items found.

Accelerating security solutions for small businesses 

Tagore offers strategic services to small businesses. 

A partnership that can scale 

Tagore prioritized finding a managed compliance partner with an established product, dedicated support team, and rapid release rate.

Standing out from competitors

Tagore's partnership with Vanta enhances its strategic focus and deepens client value, creating differentiation in a competitive market.

Vanta helps over 15,000 companies answer one of the hardest questions in enterprise security: who has access to what and can you prove it?

The same question applies to Vanta’s own platform. Every request to Vanta's API must resolve whether an actor should be allowed to view or modify a specific resource. In 2026, “who’s asking?” has become a surprisingly complicated question—it could be a user, a service, or an agent. As that set of actors has expanded, the system behind that decision has become more expressive, scalable, and durable.

This system is our authorization platform, and this is the story of how we built it.

The most important lesson from this journey is that choosing an authorization engine is only the starting point. The real work is turning it into a platform your product teams can actually build on.

Why authorization matters more within a trust management platform

Vanta helps companies build, demonstrate, and maintain compliance with frameworks like SOC 2, ISO 27001, HIPAA, and FedRAMP. Our customers use the platform to manage controls, run compliance tests, track risks, handle access reviews, and produce audit evidence. The data inside Vanta is inherently sensitive: security configurations, personnel records, vendor assessments, and evidence documents that customers present to their auditors.

That sensitivity makes granular access control a hard requirement, not a nice-to-have. When an enterprise customer's auditor asks, "Who can access sensitive evidence documents?", the answer must be precise, defensible, and verifiable. When a security team wants to ensure that HR can only see HR-related policies and engineering sees only engineering-related risks, the authorization system needs to support that cleanly.

The expectation cuts both ways. Customers trust Vanta to help them implement least privilege across their organizations, and they expect us to hold ourselves to the same standard. That conviction drove us to invest in authorization as a first-class capability in the platform, not an afterthought bolted onto the API layer.

The initial model: simple roles and GraphQL Shield

When Vanta's authorization model was simple with a few static roles, a single API, and a small team, GraphQL Shield was a natural fit. Shield is a middleware library for GraphQL servers that lets you define permission rules as a schema mirroring your API, with built-in caching and composable rule logic. It was practical: quick to set up, easy to reason about, and fast enough at our scale.

As the product grew, so did the requirements. Enterprise customers wanted access scoped to their organizational structures: employees limited to their assigned business units, control owners inheriting visibility into the tests and evidence mapped to their controls, and permissions flowing through hierarchies without requiring broad admin access.

To meet these needs, Shield rules grew from simple attribute checks into something much harder to manage with direct database queries to resolve ownership relationships, runtime conditions, and layers of rule composition to approximate hierarchical access patterns. Shield's model is fundamentally a predicate over the current request, evaluating whether a given user action should be allowed or denied. It has no native concept of resource relationships or role hierarchies, so we were forcing those patterns into a system that was never built to express them.

As a result, Shield started as our enforcement point and became the place where we authored all of our decision logic.

There was no natural boundary between enforcement and business logic, so the two collapsed into a single layer. Rules that should have been simple permission checks grew into code that encoded feature flag state, domain-specific conditions, and fallback hierarchies. And because that logic lived in the API layer, it had no way out. This meant that a different service, an async process, or a new product surface couldn't reuse it.

Here is the kind of rule composition that became normal for us:


A GraphQL Shield rule tree that became difficult to test, explain, and audit at scale.


These expressions were hard to test, explain, and audit at scale—and we had hundreds of them. The system wasn't broken, but it was becoming impossible to reason about. Every new requirement added more complexity to a system that lacked the primitives to express it cleanly.


The decision to rebuild 

As part of a broader north star architecture effort, we examined what most constrained product velocity. Authorization surfaced as one of the top bottlenecks. Features that depended on fine-grained access—from cross-functional collaboration to delegated workflows and secure experiences for external users—kept running into the limits of a system designed for simpler times.


We considered three paths: refactoring what we had, building a custom authorization service from scratch, or adopting a purpose-built external engine. Refactoring felt like a short-term fix given that the fundamental problem was authorization logic trapped in a middleware layer with no clean boundary. 


Building from scratch was more tempting in theory than in practice. AI can make code cheaper to write, but it does not make authorization cheaper to own. Token cost is not the bottleneck, complexity is. The hard part is modeling relationships, preserving correctness, handling edge cases, evolving policy safely, and operating the system over time. Google’s Zanzibar took years. Airbnb’s Himeji did too. The teams that have done this well treated it as a multi-year infrastructure investment, not a side project. We had real product commitments and a system already buckling under the weight of complexity. We could not afford to disappear into a custom build only to inherit years of maintenance and correctness risk.


We chose to adopt an external authorization engine and build our own platform layer on top of it.  Our enterprise customers wanted to bring a contractor into Vanta without giving them access to everything, let a control owner automatically see the tests mapped to their control, and give a partner a scoped view of only what's relevant to them.

These are all variations of the same underlying need: broad organizational permissions as a baseline, with precise delegation at the resource level without overprovisioning.

That is the model we built:

  • Organization roles like Admin, Editor, and Collaborator that define what a user can do across the platform by default
  • Resource roles like Owner, Reviewer, and Viewer that define what they can do on specific Controls, Tests, Risks, and Policies


Organization roles set the baseline, while resource-level roles and inheritance determine precise access.

Building the platform layer around the engine

Once we adopted the engine, product teams still had to work across multiple layers. Even a simple change could require updates to:

  • Enforcement in application code
  • Policy definitions in the engine's policy language
  • Data synchronization between our database and the authorization service
  • Testing and rollout sequencing

Getting that order wrong could temporarily cause users to gain or lose access incorrectly, so the platform team focused on reducing that surface area over time.

The solution was to introduce a layer of abstractions that reduced the coordination surface area. Product teams defined roles and relationships at the resource level, while the platform handled consistent patterns for enforcement, data synchronization, and inheritance—eliminating the need to coordinate changes across systems for every new resource.

One of the most consequential decisions in building that platform layer was adopting a declarative policy language. We evaluated it primarily on capability and chose it for readability.

Here is an excerpt from our actual policy definition:


Declarative policy made inheritance and parent-child access flows readable in the code itself.

A viewer can view. An owner inherits “viewer” and can also edit. An admin inherits “owner.” A control owner inherits the owner role on mapped tests. The code expresses the parent-child access flow directly, not a wiki page that someone forgot to update.

Declarative policy didn’t just simplify the system—it made it verifiable. We could reason about it, test it, and generate artifacts directly from the source of truth.

Because the policy is readable, we can generate permission matrices directly from it and check them into the codebase alongside the policy files they describe. Those matrices become a source of truth—not documentation that drifts, but something derived directly from the system itself. 

The same property that makes the policy legible to humans also makes it legible to LLMs, which means better generated tests, better code suggestions, and more effective policy analysis. A product manager can open the markdown file for any resource type and see which roles grant which permissions.

Here is what that looks like in practice for a pull request that adds a new feature-flagged permission, with policy, auto-generated docs, and tests moving together in one commit:


A single pull request where policy, generated docs, and tests evolve together.


Once your authorization model is this explicit, the understanding itself becomes portable. Not the DSL, but the comprehension. If we ever need to re-express it in a different language or framework, we know exactly what we are implementing.

Before

Authorization logic lived in GraphQL middleware and frequently reached into the database directly.


After

A centralized engine handled policy evaluation, with a Vanta-specific platform layer on top.


Shift

Product teams moved from stitching together enforcement, sync, and testing by hand to using opinionated abstractions.


Benefit

The model became easier to explain, test, extend, and eventually make self-serve.

Teaching authorization across the organization

Authorization is not just an engineering problem. Product managers need to think about access control when designing features. Designers need to understand how permissions affect what gets shown, hidden, or disabled. Customer-facing teams need to explain the model to customers configuring their workspaces.

We invested in writing an internal guide that anyone in engineering, product, or design could use. For PMs and designers, the message is simple: every product needs an authorization story before it goes live, even if that story starts coarse. For engineers, the guidance is to start simple for new product areas and refine the model as the product matures.

We also ran a major authorization audit to harden the system—validating that enforcement matched policy across the codebase and ensuring teams understood their responsibilities and had the tools to meet them. Audits like this are not glamorous, but they surface assumptions that have drifted from reality, identify enforcement gaps, and create shared understanding that makes future changes safer.

The unexpected benefit has been product comprehension. When a PM understands why access flows from a control to its child tests but not to sibling frameworks, they design better features. When a customer success manager understands the Collaborator role, they help customers configure permissions without escalating to engineering. Authorization turns out to be a surprisingly effective lens for understanding the product model itself.

What the industry is learning

For B2B SaaS companies serving enterprises, authorization is something customers interact with directly. They configure roles, define access policies, and manage permissions for their own teams. How intuitive and explainable that model is affects adoption and retention as directly as any feature you ship.

We started this journey asking the same question we help our customers answer: who has access to what, and can you prove it? Building the platform to answer that cleanly—for 15,000 customers and for ourselves—is ongoing work. Authorization evolves with every new product surface, every new customer segment, and every new compliance requirement. But the foundation is in place. Choosing an engine was only the starting point. What mattered was everything we built around it.

We use Oso as our authorization engine. Policy examples are written in Polar, its declarative policy language. There's more to cover—migrating from a legacy system without disrupting access, handling permission-aware filtering at scale, testing and auditing policy over time, and what changes when the actor making requests isn't a human.

Interested in solving problems like these? We're hiring!

Access Review Stage Content / Functionality
Across all stages
  • Easily create and save a new access review at a point in time
  • View detailed audit evidence of historical access reviews
Setup access review procedures
  • Define a global access review procedure that stakeholders can follow, ensuring consistency and mitigation of human error in reviews
  • Set your access review frequency (monthly, quarterly, etc.) and working period/deadlines
Consolidate account access data from systems
  • Integrate systems using dozens of pre-built integrations, or “connectors”. System account and HRIS data is pulled into Vanta.
  • Upcoming integrations include Zoom and Intercom (account access), and Personio (HRIS)
  • Upload access files from non-integrated systems
  • View and select systems in-scope for the review
Review, approve, and deny user access
  • Select the appropriate systems reviewer and due date
  • Get automatic notifications and reminders to systems reviewer of deadlines
  • Automatic flagging of “risky” employee accounts that have been terminated or switched departments
  • Intuitive interface to see all accounts with access, account accept/deny buttons, and notes section
  • Track progress of individual systems access reviews and see accounts that need to be removed or have access modified
  • Bulk sort, filter, and alter accounts based on account roles and employee title
Assign remediation tasks to system owners
  • Built-in remediation workflow for reviewers to request access changes and for admin to view and manage requests
  • Optional task tracker integration to create tickets for any access changes and provide visibility to the status of tickets and remediation
Verify changes to access
  • Focused view of accounts flagged for access changes for easy tracking and management
  • Automated evidence of remediation completion displayed for integrated systems
  • Manual evidence of remediation can be uploaded for non-integrated systems
Report and re-evaluate results
  • Auditor can log into Vanta to see history of all completed access reviews
  • Internals can see status of reviews in progress and also historical review detail
FEATURED VANTA RESOURCE

The ultimate guide to scaling your compliance program

Learn how to scale, manage, and optimize alongside your business goals.