Skip to contentAgentic IAM Day 2026 | Oct 28 | Virtual (opens in a new tab)
PlainID

Blog

XACML, OPA, and PBAC: How to Modernize Legacy Authorization Without Rebuilding Everything

Hila Shitrit Nissim · Oct 20, 2026

A regional bank gets a request from its regulator during a routine examination. The examiner wants every customer account a specific relationship manager could view during March, across both the loan origination system and the new mobile banking API. The loan system makes its access decisions through a XACML policy decision point the bank deployed in 2014. The mobile API runs on Kubernetes, and the platform team protects it with Open Policy Agent sidecars running Rego policies they wrote two years ago.

Both systems enforce the same business rule: relationship managers see accounts in their assigned region. The rule lives in two places, in two languages, maintained by two teams. A reorganization in January redrew the regions. The platform team updated the region attribute in Rego within a week. Nobody touched the XACML policy, because the two engineers who understood it left in 2021. For three months the same relationship manager saw one set of customers in the loan system and a different set in the mobile app. The bank found out when the examiner did.

Your estate probably looks something like this. XACML engines sit inside core systems that nobody wants to touch. OPA spread through the cloud-native side of the house one team at a time. Hardcoded authorization checks fill the gaps in older applications and in everything built under deadline pressure. Each piece made sense when someone chose it. Together they leave you with policy drift, slow audits, and a business rule that needs three different specialists to change.

Most advice on modernizing this stack treats it as a choice of winner. Rip out XACML, standardize on OPA, rewrite the policies in Rego. That advice comes mostly from vendors in the OPA ecosystem, and it skips the part enterprise architects care about. A rewrite of every policy and every enforcement point across hundreds of applications is a multi-year project with real outage risk, and when it finishes you still have no business-readable policy and no central place to govern it.

There’s a cheaper path. Separate three things the legacy stack fused together: the authorization model, the place you manage policy, and the points where you enforce it. Policy-based access control (PBAC) gives you the model. A central management layer governs policy across every engine. Your existing enforcement points stay where they are and change only the question they ask and the service they ask it of. This guide covers what XACML and OPA each got right, where each one stalls at enterprise scale, and a migration sequence that modernizes authorization one business rule at a time.

What XACML got right, and where it stalled

XACML, the eXtensible Access Control Markup Language, came out of the OASIS standards body in the early 2000s, and version 3.0 was ratified in 2013. Its lasting contribution is architectural. XACML split authorization into four roles. A policy enforcement point (PEP) intercepts a request inside the application. A policy decision point (PDP) evaluates it, a policy administration point (PAP) is where people author and manage policy, and a policy information point (PIP) supplies attributes the decision needs. That vocabulary still describes nearly every authorization architecture built since, including OPA deployments and modern authorization platforms.

XACML also made externalized authorization a mainstream idea. Before it, developers wrote access checks into application code, and changing a rule meant changing and redeploying the application. XACML moved the rule out of the code and into a policy the PDP could evaluate. Any application with a PEP could ask the same question and get a consistent answer.

The trouble started with the policies themselves. XACML policies are XML, and a rule a business analyst could state in one sentence often runs to dozens of lines of nested targets, conditions and combining algorithms. Writing or reviewing one took a specialist. Most enterprises ended up with a few people who could read the policy store, and when those people left, the policies froze. The bank in the opening scenario followed that pattern exactly.

Performance and tooling added friction. Classic XACML deployments ran a central PDP that applications called over the network for every decision, which added latency, especially for fine-grained checks fired many times per request. PAP tooling varied by vendor and rarely gave business owners a view they could understand. XACML still works inside the systems that run it, but extending it to microservices, data platforms and AI agents means stretching a 2013 design over workloads it never anticipated.

What OPA fixed, and what it left to you

Open Policy Agent arrived in 2016 and became a graduated project of the Cloud Native Computing Foundation in 2021. OPA kept the core idea of a decision engine separate from application code. It changed nearly everything about how teams run one. You write policy in Rego, a declarative language built for querying structured data, and you run OPA as a sidecar, a library or a standalone service next to each workload. Decisions happen locally in microseconds to milliseconds, with no network round trip to a central PDP. For Kubernetes admission control, infrastructure policy and microservice authorization, that design fits well, and developers adopted it fast.

OPA’s design leaves several jobs to you. It ships as an engine with APIs for loading policy bundles and emitting decision logs. The bundle server, the review and approval workflow, the decision log pipeline and the analysis on top of it are yours to build or buy. Teams usually wire this together through Git repositories and CI pipelines, which works for one team. Across forty teams it produces forty Rego codebases with forty sets of conventions, and the policy drift XACML suffered from comes back in a new language.

Rego also stays a developer’s language. A compliance officer can’t read a Rego module and confirm it matches the regulation, which means the people accountable for access policy can’t review the thing that enforces it. Data distribution adds another job. OPA decides against the data it holds in memory, so somebody has to keep attributes such as user region, account ownership and data classification current in every sidecar. The region attribute in the bank scenario got updated in Rego because a platform engineer noticed the reorg. Nothing in the architecture guaranteed it.

XACML vs OPA is the wrong comparison for an enterprise estate

Set XACML and OPA side by side and the differences look large. One uses XML and the other Rego. One leans on a central PDP while the other runs distributed sidecars, and one came out of a standards body while the other grew from open source. For an enterprise architect running both, those differences matter less than what the two share. Each one is a policy language plus a decision engine. Neither gives you a business-readable model of access, a single place to govern policy across every engine in the estate, or evidence an auditor can follow from a rule to a decision across systems.

That gap is where modernization pays off, and it changes the plan. You don’t have to pick XACML or OPA and migrate everything to the winner. You need a layer above both that holds policy in one model and one place, then delivers decisions to enforcement points wherever they sit. Some of those points will be XACML-era PEPs inside core banking applications. Others will be Envoy sidecars, API gateways, database queries or AI agent frameworks. Externalized authorization succeeds at enterprise scale when the externalizing happens once, centrally, instead of once per engine.

The rest of this guide follows from that claim. The authorization model moves to PBAC, policy management moves to a central layer, and enforcement stays distributed with as few changes to existing PEPs as possible. You migrate business rules one at a time and prove each one before retiring the old copy.

What PBAC adds on top of either engine

PBAC gives organizations a unified way to control what every identity (human, non-human, and AI agent) can access, do, and expose in real time. It expresses access rules in business terms that combine identity attributes, resource attributes, and context. The bank’s rule reads as written: relationship managers can view customer accounts in their assigned region, during business hours, from managed devices.

PBAC builds on role-based and attribute-based models without throwing them away. Roles still work as attributes, and attribute-based conditions still drive decisions. PBAC adds governance around the rule: versioning, ownership, business-readable authoring, and one policy rendered to many enforcement points. PlainID’s comparison of RBAC, ABAC, ReBAC, and PBAC walks through where each model fits and why policy-based approaches scale better across mixed estates.

Runtime evaluation is the other half. A PBAC decision gets made at the moment of request, using live attributes, so a reorg that changes a relationship manager’s region takes effect on the next request in every system that asks. That property would have prevented the three months of drift in the opening scenario, and it’s the core of making access decisions at request time across applications, APIs and data.

Readability changes who owns policy. A compliance officer can open the regional access policy, read it, and sign off that it matches the bank’s obligations. Developers can still work in code against the same policy, so security and engineering edit one source of truth from two directions instead of maintaining two copies that drift apart.

How to modernize legacy authorization without rebuilding everything

Inventory every place authorization decisions happen

Start with a map, because most enterprises undercount their decision points by a wide margin. Look for XACML PDPs and the applications calling them, OPA sidecars and the services they protect, and authorization checks hardcoded in application code. Then check the less obvious places. Database views that filter rows, API gateway rules, stored procedures that check entitlements, and SaaS admin consoles all make authorization decisions, and each one holds a fragment of your access policy.

Record what each decision point protects, which business rules it enforces, who maintains it and what attributes it reads. Duplicate rules show up fast. The bank in the scenario would have found its regional access rule in at least three places: XACML, Rego and a SQL view in the reporting warehouse. Discovery across identities, applications and data speeds this up, since manual inventories miss whatever nobody remembers building.

Keep your enforcement points and change what they ask

Your PEPs are the expensive part to rebuild, because they sit inside application code, gateway configuration and data access paths that teams have tested over years. Leave them in place where you can. A XACML-era PEP already intercepts requests and asks a decision point for an answer, so modernizing it often means pointing it at a new decision service and adapting the request format. Rewriting the application around it is rarely needed. OPA sidecars can follow a similar path, with the sidecar delegating decisions to a policy layer that manages rules centrally.

Standards are making these swaps easier. The OpenID Foundation’s AuthZEN working group is defining a common API between enforcement points and decision points. Once your PEPs speak a standard interface, changing the engine behind them stops being an integration project. For newer workloads, put enforcement in the infrastructure your platform team already runs. That covers API gateways for north-south traffic and the service mesh for traffic between services, with authorization enforced inside an Istio mesh so application code stays untouched.

Translate policy by business rule

Line-by-line translation from XACML to Rego, or from either to a new platform, carries over every redundancy and every forgotten exception in the old policy store. Translate by business rule instead. Pull each rule from your inventory, write it in one sentence, confirm it with the business owner, and author it once in the new model. The regional access rule becomes one policy, replacing its XACML, Rego, and SQL versions.

Expect to find rules nobody can justify. Legacy policy stores collect conditions added for a single audit finding, exceptions granted to executives who left years ago, and test rules that reached production. Each one you delete reduces your audit surface. Keep the translated policies under version control with environment promotion, a practice covered in PlainID’s piece on managing policy as versioned code, so every change has an author, a reviewer, and a timestamp.

Run old and new decisions side by side before cutover

Before a translated policy takes over enforcement, run it in parallel with the legacy engine against live traffic and compare the decisions. Matches confirm the translation. Mismatches fall into two groups. Some are translation errors, which you fix. Others reveal drift in the legacy system, where the old engine has been making the wrong decision for months, and those are often the most valuable findings of the whole project. The bank’s comparison would have flagged every relationship manager whose XACML region no longer matched HR.

Set a threshold for cutover, such as two weeks with no unexplained mismatches on a given rule, and switch that rule alone. Keep the legacy engine running for the rules you haven’t migrated yet. Moving one business rule at a time lets you stop at any point with a working system, which a big-bang rewrite can’t offer.

Retire hardcoded logic last

Hardcoded checks take the most work to remove, because each one needs a code change, a test cycle and a release. Leave them until the central policy layer has proven itself on externalized engines. Then retire them application by application, starting with the apps that change most often, since those carry the highest maintenance cost. Each retired check removes a piece of policy only one developer understood. It also removes a dependency that slows releases and multiplies during mergers, acquisitions and cloud migrations, when authorization logic from separate systems has to be reconciled.

Where AI agents and data raise the stakes

Modernization used to be about applications and APIs. It now has to cover the data layer and AI agents, two places neither XACML nor OPA was designed for. Agents send prompts, retrieve documents into context windows, call MCP tools and generate responses, and each of those steps needs an authorization decision tied to both the agent and the human it serves. Adapting a 2013 XML engine or a per-team Rego codebase to that flow means building a new governance model from scratch for every framework your AI teams adopt.

A central PBAC layer avoids that rebuild. The data categories and attribute policies you translated from your legacy estate extend to agent requests. The rule that a relationship manager sees only regional accounts applies when the manager asks an AI assistant about a customer, enforced before retrieval so out-of-region records never enter the agent’s context. PlainID explains where legacy IAM loses track of agents and covers the guardrails each agentic system needs at input, data, tool and output stages.

How PlainID modernizes legacy authorization

PlainID’s Runtime Authorization Platform delivers the architecture this guide describes. It combines centralized policy management with distributed enforcement for human, non-human and AI agent identities. Policies live in one management layer with one policy language and one decision engine. Compliance and security owners author policy through a no-code builder while developers work in code against the same policies, with versioning and promotion between environments through CI/CD. PlainID consumes identity data from Entra ID, Okta, Ping Identity and other identity providers to enrich each decision, so attributes come from their systems of record instead of hand-maintained copies.

Enforcement runs through pre-built Authorizers that cover API gateways, microservices and service meshes, data platforms through the SQL Authorizer, applications, and AI agent frameworks, including authorization libraries for LangChain and LangGraph. PlainID decouples management from enforcement, so your team can change the stack underneath without re-architecting authorization. Decisions return in milliseconds at the point of access, and policy changes reach every enforcement point in under 60 seconds with no development cycle. PlainID also takes part in the AuthZEN group working on open authorization standards.

PlainID provides what OPA leaves to you. It includes a complete management layer, pre-built enforcement connectors and enterprise support, and its scope runs past application users to every identity type. The PlainID Authorization Graph maps every access decision as a relational chain from identity provider to application, API, agent and data, and explains decisions in business language your examiners can read. PlainID reports that dynamic policies cut development overhead by 30 to 50 percent and role management overhead by up to 90 percent compared with static role management, with enterprise deployment in under two weeks.

PlainID is recognized by KuppingerCole as a Market Leader and Product Leader in policy-based access management. It secures over 35 million identities and processes billions of authorization decisions daily for Fortune 500 enterprises, including Citibank, Cisco and Bank Leumi, under a 99.99 percent uptime SLA.

Related articles