Skip to content

feat(spec,auth): guard that every feature-gated capability is UI-gated (generalize the create-user phone fix) #2874

Description

@os-zhuang

Context

The create-user phone bug (fixed in objectui #2406 + framework #2871) was one instance of a broader class:

The UI advertises a capability the runtime doesn't have because the backing plugin is off.

The create_user form offered a phoneNumber field, but the default backend rejects phone numbers unless the phoneNumber auth plugin is loaded (auth.plugins.phoneNumber). The fix gated the param with the established visible: 'features.phoneNumber == true' predicate.

The problem is that nothing guarantees coverage — which is exactly why phoneNumber slipped through while its neighbours were correctly gated (create_user itself is visible: 'features.admin == true', org fields are visible: 'features.organization != false'). Gating is hand-authored per call site, so gaps are silent until a user hits the error.

The class of bug

getPublicConfig().features exposes 8 capability flags (packages/plugins/plugin-auth/src/auth-manager.ts):

twoFactor · passkeys · magicLink · phoneNumber · phoneNumberOtp · sso · organization · admin

Any form field / action param / object field whose backend path requires one of these must be gated with a matching predicate. Today that discipline is unenforced.

Proposed work

  1. Audit the remaining high-risk flags — twoFactor, passkeys, magicLink, sso — across action params, object fields, and forms for capability-dependent inputs that ship ungated. (phoneNumber is done.)
  2. Guard — add a CI/test check (spec-liveness style) that regresses when a capability-dependent input has no feature gate.
  3. (Optional, more durable) introduce a declarative requiresFeature: 'phoneNumber' on param/field schemas; the renderer auto-hides when the feature is off. This turns "remember to gate" from a per-site discipline into a mechanism, replacing hand-written visible: features.X CEL.

Acceptance criteria

  • Audit report: every features.*-dependent input is gated, or the exception is documented.
  • A CI check that fails if a new capability-dependent input ships ungated.
  • (If declarative path chosen) requiresFeature supported in spec and honored by the objectui renderer.

References

Surfaced during go-live UAT while confirming the phone-field fix.

Metadata

Metadata

Assignees

No one assigned

    Labels

    Type

    No type

    Fields

    No fields configured for issues without a type.

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions