# Migrate authorization models without accidental access changes

> Use an expand, shadow, migrate, and contract sequence so old and new authorization models coexist while you measure every decision difference.

Canonical URL: https://www.devobs.io/articles/zero-downtime-authorization-model-migrations/
By: Owen Park
Published: 2026-07-25T16:13:08.859Z
Updated: 2026-09-05
Section: Architecture

Change an authorization model as a compatibility migration, not as a schema edit. The safe sequence is expand, shadow, migrate, switch, and contract. At every stage, old and new application versions must make an intentional decision from data they understand.

## Write the security invariants first

Define the permissions that must never expand and the workflows that must not be revoked. For example: only project owners can delete a project; existing editors can keep editing during the rollout; a user from tenant A can never acquire a path to tenant B.

Turn these into a decision corpus containing realistic subjects, resources, relationships, and expected outcomes. Include negative cases and ambiguous legacy data. A migration that checks only happy-path grants will miss the dangerous half of authorization behavior.

The [OpenFGA model migration guide](https://openfga.dev/docs/modeling/migrating/migrating-models) describes creating a new model and changing the model identifier used for checks. That indirection is valuable: model publication and application rollout become separate events.

## Expand before changing meaning

Publish a new model that can represent both legacy and future data. Deploy readers capable of understanding both versions while they still enforce the old decision. If the data shape changes, start dual writes or write a canonical record from which both representations can be derived.

Suppose “editor” becomes “contributor” plus a per-resource can-publish relation. During expansion, old services still write editor; new services write contributor and the compatibility form. Make retries idempotent and observe mismatches. Do not delete editor yet.

This is the same compatibility discipline used for multitenant schemas. Microsoft’s [multitenant storage guidance](https://learn.microsoft.com/en-us/azure/architecture/guide/multitenant/approaches/storage-data) recommends backward compatibility with at least one previous schema version and sequencing destructive changes across versions to support rollback.

## Shadow decisions before enforcement

For each production authorization check, evaluate the current and candidate models against the same logical request. Enforce only the current result, but record a structured difference:

- old allow, new deny: availability or workflow regression;
- old deny, new allow: possible privilege expansion;
- error or timeout in either path: operational incompatibility;
- matching result with a different reasoning path: useful migration evidence.

Sample routine matches if volume demands it, but retain all differences. Protect the log because subjects and resource identifiers may be sensitive. Attach model versions, data revision, caller version, and request correlation ID so a difference can be reproduced.

Set explicit acceptance thresholds. A security-sensitive migration should normally require every unexplained deny-to-allow difference to be resolved, rather than hidden in an aggregate percentage.

## Backfill with checkpoints and rollback

Backfill relationships in bounded, restartable batches. Count source records, successful transformations, rejected records, and destination records. Quarantine ambiguity instead of guessing. Continue dual writes until the backfill watermark has passed and drift remains zero for an agreed observation window.

Switch enforcement behind a reversible configuration that pins a precise model version. Roll out by internal tenant, then low-risk cohorts, then broadly. During rollback, restore the old decision path while preserving new writes; otherwise recovery creates data loss that complicates the next attempt.

Contract only after all supported application versions read the new representation, no old writers remain, the rollback window closes, and audit queries can explain decisions made under either version.

## Make the cutover reviewable

The release checklist should name the two model IDs, invariants, shadow-diff query, backfill watermark, dual-write health metric, cohort order, rollback trigger, and owner. Test concurrent old and new binaries, not merely two unit-test suites in isolation.

Run the comparison against freshly written and backfilled records separately. That split reveals dual-write defects that aggregate decision metrics can hide.

Start by capturing one week of representative authorization requests as a sanitized decision corpus. Run both models over it in CI, then use the same comparison format for production shadowing.

## Source references

- <https://openfga.dev/docs/modeling/migrating/migrating-models>
- <https://learn.microsoft.com/en-us/azure/architecture/guide/multitenant/approaches/storage-data>
