This guide is for teams that run EmpowerID for identity governance and want to move fine-grained authorization out of it. That means the decisions about which purchase order a manager may approve, which record a support agent may open, and which tool an AI agent may call. EmpowerID keeps provisioning, access certification, separation of duties and its SAP connectors, and your applications keep reading the tokens it issues. What changes is where the runtime decision is made, how its rules are written and tested, and how far one set of rules reaches.
You can do it one application at a time, with Cerbos running beside EmpowerID until the decisions match, so nothing has to be switched over in one go.
Why migrating from EmpowerID is mostly a role and attribute question
EmpowerID's authorization is built from objects that also serve governance. Management roles scope what someone may administer to a set of locations. Access levels bundle operations on a resource type. PBAC policies each grant a single right or role to an assignee, with conditions on Field Types. And EmpowerID's documentation says it precalculates which users have which permissions, then checks ABAC conditions known only at access time on top of that.
So most of the work is not the decision logic. It is listing which roles, locations and attributes each application's decisions depend on today, and deciding where each one will come from at decision time. Once that list exists, the rules themselves are usually short.
What stays in EmpowerID and what replaces its authorization
Stays in EmpowerID, or in Omada as the product lines come together.
- Identity lifecycle, joiners, movers and leavers, and the Identity Warehouse
- Access requests, certification campaigns and separation of duties
- Privileged access and the SAP, Active Directory and Workday connectors
- Issuing the tokens your applications already read
Moves to Cerbos. One authorization management platform, deployed where you need it.
- Decision points run next to every workload, as a shared service, a sidecar, a DaemonSet or a serverless function, and make every decision per request.
- Cerbos Hub holds the policies, runs their tests before each build, distributes them to every decision point, and collects the audit log. It runs hosted, or on-premise and air-gapped for enterprise customers.
- Cerbos Synapse fetches attributes at decision time from the systems that own them, and connects Kong, Apigee, Envoy, Istio, Kafka, data platforms, MCP servers, AI gateways and AI coding agents to the same policies.
Key Cerbos principles to know before translating
Policies are organized by resource. A resource policy holds every rule for one kind of resource, such as purchase_order, with the actions it supports. Where EmpowerID bundles operations into access levels, Cerbos lists the actions on each rule.
Roles come from your identity system, and context makes derived roles. The roles EmpowerID assigns arrive as principal roles. A derived role adds context to a role, for example a purchasing manager who becomes an approver only for the regions they manage, which is how EmpowerID's location scoping comes across.
Scoped policies replace inheritance levels. A scoped policy applies to one business unit, country or tenant. It can override its parent, or, with REQUIRE_PARENTAL_CONSENT_FOR_ALLOWS, it can only narrow what the parent allows.
Everything is denied unless a rule allows it, and a deny wins over an allow for the same role. To make a deny apply to everyone, write it for every role, roles: ["*"].
Conditions are CEL expressions over the principal (P), the resource (R) and the request.
Missing data fails closed. A condition that hits a missing attribute does not match. Strict evaluation mode denies whenever a condition raises an error, and schemas reject a request that is missing a required attribute before any rule runs.
Nothing is precalculated. Every decision is evaluated against the request in front of it, so a role change in EmpowerID applies on the next decision.
EmpowerID to Cerbos concept mapping
| EmpowerID | Cerbos |
|---|---|
| Business roles and locations assigned to a person | Principal roles and attributes, read from the EmpowerID token or sent by the application |
| Management roles scoped to locations | Derived roles with a condition on the resource's location or region |
| Access levels, bundles of operations | The actions on each rule |
| Resource types and resource systems | Resource kinds, one resource policy each |
| PBAC policies granting a right or role | Rules in a resource policy, allowing actions for a role or derived role |
| Field Type conditions | CEL conditions on principal and resource attributes |
| Precalculated access | Per-request checks, plus query plans for lists and searches |
| The five inheritance levels of the newer decision point, from application up to global | Scoped policies, with override or parental consent per scope |
| Group projection to applications | A Cerbos check in the application, so group membership becomes an input rather than the decision |
| AuthZEN evaluation and batch calls | Cerbos PDP's AuthZEN endpoints in Cerbos PDP or Cerbos Synapse, with no change to the call |
| AuthZEN search calls | PlanResources query plans, applied by your data layer |
| MCP Gateway checks on agent tool calls | Cerbos checks from agent hooks, an MCP gateway or server, or an AI gateway, through Cerbos Synapse |
| Signed receipts and evidence | Every decision logged with its inputs and the policy version, collected in Cerbos Hub |
| Lint script and manual testing checklist | Test suites next to each policy, run by Cerbos Hub before every build and by cerbos compile locally |
A worked example of translating EmpowerID authorization to Cerbos
Here is a setup typical of a manufacturer on EmpowerID. Purchasing managers hold a management role scoped to the regions they look after. In those regions they can view purchase orders and approve them up to their own approval limit, a Field Type on their identity. Procurement admins can view and approve everywhere. Nobody approves an order they raised. In Germany, a works agreement caps regional approvals at 10,000 regardless of anyone's personal limit.
In EmpowerID, that is spread across several objects.
| What the rule needs | Where it lives in EmpowerID |
|---|---|
| Who is a purchasing manager, and for which regions | A management role scoped to locations |
| What they can do to an order | An access level bundling view and approve |
| The approval limit | A Field Type on the person, used in a PBAC condition |
| Procurement admins approving anywhere | A PBAC policy granting the right to a role |
| No approving your own order | An ABAC condition at access time |
| The German cap | A rule at a more specific inheritance level, or a separate condition |
The decisions you know must hold today are the definition of a correct migration, so write them as a Cerbos test suite first, then make the policies pass.
# tests/purchase_order_test.yaml
name: PurchaseOrderTestSuite
options:
strictEvaluation: true
principals:
ana:
id: ana
roles: ["purchasing_manager"]
attr:
managed_regions: ["emea"]
approval_limit: 25000
raj:
id: raj
roles: ["procurement_admin"]
attr: {}
tom:
id: tom
roles: ["employee"]
attr: {}
resources:
emea_small:
kind: purchase_order
id: po-1
attr: { region: emea, amount: 4000, requester_id: tom }
emea_large:
kind: purchase_order
id: po-2
attr: { region: emea, amount: 40000, requester_id: tom }
apac_small:
kind: purchase_order
id: po-3
attr: { region: apac, amount: 1000, requester_id: tom }
emea_own:
kind: purchase_order
id: po-4
attr: { region: emea, amount: 500, requester_id: ana }
de_medium:
kind: purchase_order
id: po-5
scope: de
attr: { region: emea, amount: 15000, requester_id: tom }
de_small:
kind: purchase_order
id: po-6
scope: de
attr: { region: emea, amount: 8000, requester_id: tom }
tests:
- name: Regional approvers approve within their regions and their limit
input:
principals: ["ana"]
resources: ["emea_small", "emea_large", "apac_small", "emea_own"]
actions: ["view", "approve"]
expected:
- principal: ana
resource: emea_small
actions: { view: EFFECT_ALLOW, approve: EFFECT_ALLOW }
- principal: ana
resource: emea_large
actions: { view: EFFECT_ALLOW, approve: EFFECT_DENY }
- principal: ana
resource: apac_small
actions: { view: EFFECT_DENY, approve: EFFECT_DENY }
- principal: ana
resource: emea_own
actions: { view: EFFECT_ALLOW, approve: EFFECT_DENY }
- name: Germany caps regional approvals at 10,000
input:
principals: ["ana"]
resources: ["de_medium", "de_small"]
actions: ["approve"]
expected:
- principal: ana
resource: de_medium
actions: { approve: EFFECT_DENY }
- principal: ana
resource: de_small
actions: { approve: EFFECT_ALLOW }
- name: Procurement admins approve anywhere, and other employees cannot
input:
principals: ["raj", "tom"]
resources: ["emea_large", "de_medium"]
actions: ["approve"]
expected:
- principal: raj
resource: emea_large
actions: { approve: EFFECT_ALLOW }
- principal: raj
resource: de_medium
actions: { approve: EFFECT_ALLOW }
- principal: tom
resource: emea_large
actions: { approve: EFFECT_DENY }
- principal: tom
resource: de_medium
actions: { approve: EFFECT_DENY }
The management role scoped to regions becomes a derived role.
# policies/derived_roles.yaml
apiVersion: api.cerbos.dev/v1
derivedRoles:
name: purchasing_roles
definitions:
# A purchasing manager acts as an approver for the regions they manage
- name: regional_approver
parentRoles: ["purchasing_manager"]
condition:
match:
expr: R.attr.region in P.attr.managed_regions
The access level and the PBAC policies become one resource policy.
# policies/purchase_order.yaml
apiVersion: api.cerbos.dev/v1
resourcePolicy:
version: default
resource: purchase_order
importDerivedRoles:
- purchasing_roles
rules:
# Approvers see the orders in their regions
- actions: ["view"]
effect: EFFECT_ALLOW
derivedRoles: ["regional_approver"]
# Approvers approve orders in their regions, up to their own limit
- actions: ["approve"]
effect: EFFECT_ALLOW
derivedRoles: ["regional_approver"]
condition:
match:
expr: R.attr.amount <= P.attr.approval_limit
# Procurement admins can view and approve any order
- actions: ["view", "approve"]
effect: EFFECT_ALLOW
roles: ["procurement_admin"]
# Nobody approves an order they raised themselves
- actions: ["approve"]
effect: EFFECT_DENY
roles: ["*"]
condition:
match:
expr: R.attr.requester_id == P.id
The German cap becomes a scoped policy that can only narrow what the parent allows.
# policies/purchase_order_de.yaml
apiVersion: api.cerbos.dev/v1
resourcePolicy:
version: default
resource: purchase_order
scope: de
scopePermissions: SCOPE_PERMISSIONS_REQUIRE_PARENTAL_CONSENT_FOR_ALLOWS
importDerivedRoles:
- purchasing_roles
rules:
# In Germany, regional approvers stop at 10,000 whatever their own limit
- actions: ["approve"]
effect: EFFECT_DENY
derivedRoles: ["regional_approver"]
condition:
match:
expr: R.attr.amount > 10000
Six pieces of EmpowerID configuration became three short files and a test suite. The rule that used to depend on which inheritance level won now says in one line that Germany narrows the parent and cannot widen it. Run cerbos compile locally, or let Cerbos Hub run the suite before it builds the deployment, and the policies only ship when all fourteen checks pass.
Once the attribute shapes are settled, attach a schema that makes managed_regions and approval_limit required, and set schema enforcement to reject, so a request without them is refused before any rule runs.
If your team works with an AI coding agent, the Cerbos agent skills help with the rewrite. Install them with npx skills add https://www.cerbos.dev, give the agent your rules written in plain words, and it writes each one as a Cerbos policy with a test suite and runs the tests until they pass. Your reviewers still approve every policy before it ships.

Where your EmpowerID roles and attributes come from after the migration
Make a list of every role, location and attribute your decisions use, then pick a source for each.
The EmpowerID token. Roles, groups, organizational unit and custom claims EmpowerID already puts in the tokens it issues. Your application reads them and passes them as principal roles and attributes, as our EmpowerID integration describes. Cerbos can also verify a JWT itself and read its claims in a condition.
The application. The resource's own attributes, the order's region, amount and requester, are in your database already. Send them with the request.
Cerbos Synapse. Attributes that are neither in the token nor in the application, such as an approval limit held in an HR system, are fetched by Cerbos Synapse at decision time from the system that owns them, with caching set to how often each changes. Synapse can also read attributes from EmpowerID itself. We show the pattern in looking up identity attributes at decision time.
Nothing is copied into Cerbos, so EmpowerID stays the source of truth for who holds which role, and a change there applies on the next decision.
Replacing EmpowerID's authorization calls with Cerbos
How you replace the call depends on how each application asks EmpowerID today.
Applications that call EmpowerID's AuthZEN decision point. Cerbos PDP and Cerbos Synapse both implement the AuthZEN evaluation endpoints, so an enforcement point already speaking AuthZEN keeps its call as it is and is pointed at Cerbos instead. That is what the standard is for. Roles go in the subject's cerbos.roles property, and every other property becomes an attribute.
POST /access/v1/evaluation
{
"subject": {
"type": "user",
"id": "ana",
"properties": {
"cerbos.roles": ["purchasing_manager"],
"managed_regions": ["emea"],
"approval_limit": 25000
}
},
"resource": {
"type": "purchase_order",
"id": "po-1",
"properties": { "region": "emea", "amount": 4000, "requester_id": "tom" }
},
"action": { "name": "approve" }
}
Applications that read projected groups or call EmpowerIDâs own APIs. These need a new call either way. Make it an AuthZEN call, as above, so it does not have to change again if the decision service ever does, or call Cerbos through an SDK, available for eight languages plus Laravel.
const decision = await cerbos.checkResource({
principal: {
id: "ana",
roles: ["purchasing_manager"],
attr: { managed_regions: ["emea"], approval_limit: 25000 },
},
resource: {
kind: "purchase_order",
id: "po-1",
attr: { region: "emea", amount: 4000, requester_id: "tom" },
},
actions: ["view", "approve"],
});
if (decision.isAllowed("approve")) {
// show the approve button and accept the approval
}
Put the call behind one function in each application, so the swap is one change per application.
Lists and searches. Where an application asks which records a person may see, through precalculated access or AuthZEN search, use query plans. Ask which purchase orders Ana may approve, and Cerbos returns the conditions as a filter your data layer applies, with documented adapters for Prisma, Drizzle, Mongoose, Convex, SQLAlchemy and LangChain with ChromaDB.
AI agents. Where agent tool calls go through EmpowerID's MCP Gateway, put the same check in front of them with Cerbos, in the agent harness through hooks, as with Claude Code, at an MCP gateway or in the MCP server, or at an AI gateway such as LiteLLM or agentgateway. Each call is checked with its arguments and with the person behind the agent. Credentials stay in your secrets manager or gateway.
Running Cerbos beside EmpowerID in shadow mode
Before Cerbos enforces anything, run it beside EmpowerID on real traffic.
- Send every check to both. At each application you are migrating, call Cerbos alongside EmpowerID, but keep enforcing EmpowerID's answer.
- Log both decisions. Cerbos Hub collects every Cerbos decision with its inputs, linked to the policy version that made it, so a mismatch can be traced to a rule or a missing attribute.
- Fix any mismatch. Most are attributes or locations EmpowerID resolved behind the scenes that the Cerbos request did not carry. The rest are rules that were broader or narrower than anyone remembered, often through an inheritance level. Each fix becomes a test.
- Agree the exit bar with your security team before you start, for example zero unexplained mismatches over two weeks of production traffic for the application.

Switching over application by application
Move enforcement one application at a time, once it meets the exit bar. Then retire that application's PBAC policies, access levels and group projections in EmpowerID, and leave its roles and identities where they are, because EmpowerID still governs them. Keep the old rules for a short rollback window after the last application moves, and export any authorization evidence your regulators require before you remove them.
Common challenges when migrating authorization from EmpowerID, and how to solve them
Rules spread across several objects. A single business rule in EmpowerID can depend on a management role, an access level, a Field Type and an inheritance level at once. Write the rule in plain words first, agree it with its owner, then write the policy and the test.
Inheritance order nobody remembers. When a more specific level overrides a general one, the effective rule can surprise its owners. Model each level that still matters as a scoped policy, and decide per scope whether it may override the parent or only narrow it.
Applications that enforce through group membership. Where EmpowerID projects access into groups and the application checks the group, the group was the decision. Replace the group check with a Cerbos check, and pass the group as an attribute if a rule still needs it.
Precalculated access reports. Certification and access reports that read precalculated access stay in EmpowerID, which still holds the role assignments. Cerbos answers "can this person do this, now" and "which records can this person see", and its decision log shows what was actually allowed.
Attributes nobody knew a rule depended on. Shadow mode finds these as mismatches. Add each to the attribute list, then to the token, the request or a Synapse data source.
Agent controls in Early Access. EmpowerID labels its agent products Early Access (October 2026). If agents already go through its MCP Gateway, keep that path while Cerbos runs beside it in shadow mode, then switch enforcement once the decisions match.
Planning your migration from EmpowerID
Pick the first application by two tests. It should have a clear owner who can say what the rules are meant to be, and its decisions should depend on attributes you can already supply. Applications built around one resource, such as purchase orders, claims or support cases, usually go first, and agents with a small set of tools are a good second.
Book a call and bring the roles, access levels and PBAC policies behind one application. We will model them in Cerbos with you, write tests that check they give the same decisions as today, and help you build a migration plan, timeline and risk view your security and audit teams can review. To start on your own, try Cerbos with your translated policies.
Go deeper:
- Cerbos vs EmpowerID
- EmpowerID alternatives
- How Cerbos works
- How to adopt externalized authorization (eBook), for a step by step plan to move authorization out of applications and suites
FAQ
Tagged in






