masked_output
Masked Output
Masked Output provides configurable field formatters, a JSON:API normalizer,
and a Views field handler that mask sensitive field values across every Drupal
output surface — entity displays, REST/JSON:API responses, and Views pages —
with role-based bypass, permissioned on-demand reveal, and a full audit trail.
Ideal for displaying:
- Payment card numbers & account IDs
- Phone numbers and SSNs
- Email addresses in public listings
- API keys, tokens, and reference codes
- Any sensitive string or telephone field
Formatters
Mask Output (string fields)
- Show last / first N characters, mask the rest
- Mask first / last N characters, show the rest
- Custom mask symbol (default
*) - Multibyte-safe
Mask Email Output (email fields)
- Masks the local-part of an email address, preserves the domain
- Example:
[email protected]→********@example.com
Mask Pattern Output (string & telephone fields)
-
Positional pattern:
#= show original character,
*= mask, any other character = literal separator (***) ***-####→(***) ***-5309####-****-****-####→4111-****-****-1234***-**-####→***-**-6789
Role-Based Bypass
Each formatter exposes a Roles that see unmasked values setting.
Users with any selected role see the original value. Bypass is enforced
identically on entity pages, Views, and JSON:API responses. The
user.roles render cache context is applied automatically to
prevent cross-user cache poisoning.
Reveal On Demand
Enable the Reveal on demand formatter option to show a
Reveal button next to each masked value. Users with the
Reveal masked output values permission can click it to see the
original value in-place. An optional Auto re-mask timeout
automatically hides the value after N seconds. Every reveal is written to
the audit log.
JSON:API Integration
When a field's default view display uses any masked formatter, the same
masking is applied automatically to JSON:API responses — no extra
configuration needed. Role bypass is honoured for authenticated consumers.
The normalizer is registered only when the jsonapi module is
enabled.
Views Integration
Every string, email, and telephone
configurable field gains a (Masked) variant in the Views
field picker. Masking strategy, character count, pattern, mask symbol, and
bypass roles are all configurable per Views field instance, independently
of the entity display formatter.
Audit Logging & Events
-
All masking operations and every reveal action are logged to the
masked_outputwatchdog channel (visible at
Reports > Recent log messages). -
Dispatches
masked_output.post_mask
(MaskedOutputEvent) after each masking operation — event
subscribers can override the masked value. -
hook_masked_output_alter(&$masked, $original, $context)
for procedural extensibility.
Developer API
-
Plugin-based strategy system — register custom mask
strategies by annotating a class with#[MaskStrategy(...)]
inPlugin/MaskStrategy/. -
MaskingServiceInterface— inject and call
maskString(),maskEmail(),
maskPattern()programmatically. - See
masked_output.api.phpfor the full hook and event reference.
Technical Highlights
- Service-based architecture with full dependency injection
- 69 tests / 246 assertions (Unit + Kernel)
- PHPStan level 6 — 0 errors
- PHPCS Drupal + DrupalPractice — 0 errors
- Drupal 10 and 11 tested; Drupal 13 forward-compatible
Installation
composer require drupal/masked_output:^2.1