Skip to main content

Migrating to layered authorization

Most real systems start with Role-Based Access Control (RBAC) and stay there until the model breaks under new requirements. This guide covers the typical migration path: adding Policy-Based Access Control (PBAC) for contextual overrides, then introducing Fine-Grained Access (FGA) for object-level decisions, without disrupting production traffic.

The starting point

A typical RBAC-only deployment on SecureAuth Connect looks like this:

  • Users have a role attribute in an identity pool.
  • The role is mapped into an access token claim.
  • Policies attached at the application, scope, or API level read the claim and allow or deny.

This works well until one of the following happens:

  • You need context-driven rules. Time-based access, location checks, risk-based step-up, or device posture cannot be expressed with a role alone.
  • You need object-level access. "Alice can read documents she owns" is not a role, it is a relationship to specific objects.
  • Roles start to sprawl. Every new access variation produces a new role, and the role vocabulary becomes unmanageable.

Each of these signals a different layer.

Target model

The recommended target is a layered model where each layer does what it is best at:

  • RBAC for coarse feature access: does the role allow the action at all.
  • PBAC for contextual overrides: require MFA for a sensitive scope, block risky geographies, enforce time windows.
  • FGA for object-level access: is this specific record owned by or shared with this subject.

All three layers evaluate through the same policy engine on SecureAuth, so a single attached policy can combine role checks, context checks, and relationship checks.

Phase 1. Add PBAC on top of RBAC

Start here when context-driven rules need to layer onto existing roles.

  1. Identify the first contextual rule you need. For example: "admin role can grant refunds, but refunds over $1,000 require MFA completed in the current session."
  2. Author a PBAC policy that extends the existing role check. Read the role claim and the amr claim together, and deny if the high-value path is taken without MFA. See How policies work.
  3. Attach the policy to the scope or API where the rule belongs. Keep the original RBAC policy in place at the coarser level if it still applies elsewhere.
  4. Test the change in policy test mode with sample inputs that cover both the normal and the escalated path.
  5. Publish. PBAC starts taking effect for new token requests and new API calls.

Repeat per contextual rule. Each new PBAC policy is additive; it does not break existing RBAC decisions.

Phase 2. Introduce FGA for object-level access

Start here when access depends on which specific object is being touched, not on the user's role.

  1. Pick a single object type to model first. A common starter is documents or records that have clear owners.
  2. Write the permission system schema. Define the object type, the relations (owner, editor, viewer), and the permissions each relation grants. See Permission systems and Fine-Grained Access (FGA).
  3. Populate the relationship store. Import ownership data from your existing database into the permission system. For many products this is a one-time migration of the existing "who owns what" table.
  4. Wire up the check. Update your service to call the SecureAuth permission check at the point where it currently does an ownership lookup. Treat both the existing check and the SecureAuth check as parallel sources of truth during the rollout window.
  5. Shadow mode first. Log discrepancies between the existing check and the SecureAuth check without changing the caller's behavior. Fix them until they match.
  6. Cut over. Remove the old check and rely on the SecureAuth check. Keep the old code path dormant for a release cycle in case a rollback is needed.

Rollout tactics

A few patterns make layered rollouts safer:

  • Shadow evaluation. Run the new policy or FGA check alongside the existing logic, log the decisions, and compare. Only cut over once the two agree on production traffic.
  • Per-application progressive enforcement. Attach the new policy to one lower-risk application first. Expand to more applications once signals are stable.
  • Policies as code. Version policies in Git and roll them out through declarative configuration import. This keeps the migration reviewable and reversible.
  • Decision audit during rollout. Watch the decision logs during each phase. Surprising denials usually trace to a missing claim or a subject-type mismatch, both of which are visible in the logs.

Anti-patterns to avoid

  • Pushing everything into RBAC. Creating roles like manager-us-after-hours-mfa-required compounds into unmanageable role sprawl. Express that rule as PBAC instead.
  • Pushing everything into FGA. FGA is the right tool for object-level decisions at scale, but it is not a replacement for simple role or scope checks. Keep coarse decisions coarse.
  • Hard-cutover with no shadow mode. Cutting over without parallel evaluation means production traffic is the test suite.

See also