DEV Community

Auth By Example
Auth By Example

Posted on

Feature Flags Are Not Authorization

Feature flags are great for rollout. They are a terrible substitute for authorization.

A common pattern: ship an admin screen or a beta API behind a flag. Flip it off in production for most users. Flip it on for a few accounts. Ship. Move on.

That controls product exposure. It does not control who is allowed to do the thing.

What flags actually decide

A flag answers questions like:

  • Is this feature ready for this cohort?
  • Should we show the new UI in staging?
  • Are we ramping to 10% of traffic?

Authorization answers different questions:

  • Is this principal allowed to perform this action on this resource?
  • In this tenant?
  • Right now, with attributes that still hold?

Those are not the same layer. Mixing them is how "hidden" endpoints stay callable.

How it breaks in practice

Direct calls. The UI respects the flag. The client hides the button. The API still accepts POST /admin/export if the handler only checks "is the flag on?" or worse, checks nothing because "the UI is gated."

Wrong environment / wrong targeting. Someone enables admin_v2 for an internal test cohort. Targeting rules are broader than expected. Suddenly half of staging—or a production segment—can hit privileged routes that never had a real authz check.

Fail-open on outage. Flag evaluation fails (timeout, SDK error, misconfig). The code path defaults to "show feature" or "allow request" so the app does not look broken. Privileged actions become available to anyone who can reach them.

Flags as pseudo-roles. is_beta_admin = true starts as a rollout toggle and slowly becomes the only gate. There is no audit trail of grants, no revocation story, no resource-level scope—just a boolean that means "trust me."

Put authz on the handler

Gate the handler with a real authorization decision: role, attribute, or relationship check against the resource. Return 403 when the principal is not allowed—even if the flag is on for them.

Use the flag only for rollout:

  • Whether to register the route in this deploy
  • Whether to show the UI entry point
  • Whether a cohort is in the experiment

Never: whether the request is authorized.

A useful mental model:

flag on  + authz allow  → proceed
flag on  + authz deny   → 403
flag off + authz allow  → hide / 404 / not in this release (product choice)
flag off + authz deny   → still deny (security wins)
Enter fullscreen mode Exit fullscreen mode

If the flag service is down, fail closed for privileged actions. Degrade the product surface; do not open the security surface.

A quick checklist

Before shipping a "flagged" admin or beta capability:

  1. Is there an authorization check on every mutating and sensitive read path?
  2. Would a raw HTTP client with a normal user token still get denied?
  3. If flag evaluation errors, do privileged paths deny by default?
  4. Can you revoke access without waiting for a flag change or redeploy?

If the answer to any of those is no, the flag is doing authz work it was never designed for.

Rollout toggles decide who sees a feature. Authorization decides who may use it. Keep both—and do not let one pretend to be the other.

Top comments (0)