Conversation
📊 Packages dist files size differenceThanks for the PR! Here is the difference in size of the packages dist files between the base branch and the PR.
|
|||||||||||||||
|
After first read (code and your PR message), I'm also a bit doubtfull... for two main reasons:
I'm all open to change my mind if you guys think this is one of the must-have components for UI in the years to come. But really have no definitive opinion here, simply observing this shows once more there is a whole lot of UI/UX patterns that requires the same elements in back-end, and I'm more and more thinking we either introduce some components/bundle and generic way to configure RateLimiting, Logging, and even stateless TwigComponent with endpoints (LiveComponents without all the form/model part but with actions) .. or we don't but should probably not multiply such bundle/package, everytime with their own templates, css, security, exception, configuration logic. Not clear answers here in my mind 🤷 |
|
Thanks for your feedback @smnandre The easyadmin part is only on the demo app. But I agree that this component is quite opiniated and could lead to many configuration options that we don't want to maintain. You pointed me to using Live Components for this feature, which is very smart.
Then we can document a tutorial recipe to create such component for app developers. |
Draft. This pull request is opened as a draft on purpose. I have doubts about the value of this feature, and the demo needs more work. Read the "Author's doubts" section first.
Videos
Screen.Recording.2026-09-09.at.13.02.13.mov
Screen.Recording.2026-09-09.at.13.13.23.mov
Author's doubts
I am not convinced this pattern should land in Symfony UX. Coupling a "click-to-reveal" with an asynchronous backend call encourages an architecture that degrades both the Developer Experience (DX) and the User Experience (UX):
Recommended alternatives:
The demo needs rework
The demo application (apps/disclose-demo) needs to be reworked before this PR can be considered ready. Feedback is welcome on the current version, and on whether the component itself is worth shipping at all.
Symfony UX Disclose renders protected values (emails, phone numbers, identifiers) behind a masked trigger. The real value never enters the initial HTML: it only reaches the browser after an explicit click, through a server endpoint that enforces authorization, a rate limit and an audit trail before returning anything.
Objective
Personally identifiable data shown to every authorized user is an exfiltration vector: any user with legitimate access, acting with bad intentions, can harvest thousands of records in seconds by opening pages or scraping the HTML, unnoticed.
Disclose changes the economics of mass disclosure:
Design choices
The value lives server-side only
A masked display is not protection: the value stays in the DOM and is trivial to extract. The component renders a signed reference instead, and the raw value exists only on the server. The reference is an HMAC-signed disclose context, so it cannot be tampered with to target other records.
The bundle owns the endpoint
DiscloseBundle owns the route. In a single place it verifies the signed context, resolves the subject, checks the discloser authorization, consumes a rate limiting token and writes an audit record before any value is returned. Applications cannot accidentally skip one step.
Data source agnostic resolution
Subject resolution is pluggable through SubjectResolverInterface. Doctrine ORM and MongoDB ODM ship as optional built-in resolvers, and a context provider turns a subject object into a signed reference. The contracts never import Doctrine: a resolver, a discloser and a context provider all work on plain objects.
Race-safe rate limiting
Rate limiter consume() is a read-modify-write on the cache. Without a lock, a burst of simultaneous requests can race past the quota. The Lock component is a hard requirement: the bundle registers its own flock lock factory and proposes it as the default lock_factory. Several framework limiters can be combined through the RateLimiter CompoundLimiter (for example a burst window plus a daily quota). Persisting the quotas in a DBAL-backed cache pool keeps them across restarts.
Audit and events share one status
DiscloseStatus is a single enum shared by the audit logger and the disclosure events. The log level is decided in the logger, the consumer. The events (attempt, success, rejected) never carry the disclosed value: listeners cannot observe the sensitive data.
Frontend weight stays on the server
The Twig component exposes two customizable views. A toggle mode keeps a single trigger in place (an eye that swaps between a slashed and an open pictogram). The revealed content can be authored as an inline reveal block inside the component tag: it is compiled into the tag but never rendered on the initial mount, and the endpoint renders it on click with the resolved subject. By default the fetched value is inserted as plain text; HTML injection is opt-in and only for your own server-side templates.
Packages
Security notes