PHPackages                             rawand201/filament-reveal - PHPackages - PHPackages  [Skip to content](#main-content)[PHPackages](/)[Directory](/)[Categories](/categories)[Trending](/trending)[Leaderboard](/leaderboard)[Changelog](/changelog)[Analyze](/analyze)[Collections](/collections)[Log in](/login)[Sign up](/register)

1. [Directory](/)
2. /
3. rawand201/filament-reveal

ActiveLibrary

rawand201/filament-reveal
=========================

A Filament column component for hiding and revealing sensitive data with toggle and copy functionality

v1.0.2(1mo ago)00MITPHPPHP ^8.2CI passing

Since Mar 27Pushed 1mo agoCompare

[ Source](https://github.com/RawanD201/filament-reveal)[ Packagist](https://packagist.org/packages/rawand201/filament-reveal)[ Docs](https://github.com/rawand201/filament-reveal)[ GitHub Sponsors](https://github.com/rawand201)[ RSS](/packages/rawand201-filament-reveal/feed)WikiDiscussions main Synced 1mo ago

READMEChangelogDependencies (9)Versions (4)Used By (0)

Filament Reveal
===============

[](#filament-reveal)

[![Latest Version on Packagist](https://camo.githubusercontent.com/0b040007300c41dd7010a73531e2537d51dc4f9184c35e4967b2955f0be66b59/68747470733a2f2f696d672e736869656c64732e696f2f7061636b61676973742f762f726177616e643230312f66696c616d656e742d72657665616c2e7376673f7374796c653d666c61742d737175617265)](https://packagist.org/packages/rawand201/filament-reveal)[![Total Downloads](https://camo.githubusercontent.com/0b6963808be628304517605c956c2a84bf9b38a8e25ad112a18efa6636526c81/68747470733a2f2f696d672e736869656c64732e696f2f7061636b61676973742f64742f726177616e643230312f66696c616d656e742d72657665616c2e7376673f7374796c653d666c61742d737175617265)](https://packagist.org/packages/rawand201/filament-reveal)[![License](https://camo.githubusercontent.com/ceaff2bf554eca233714d46fdb661e63ecc2dbcf424a94152d7f2c0e2c621140/68747470733a2f2f696d672e736869656c64732e696f2f7061636b61676973742f6c2f726177616e643230312f66696c616d656e742d72657665616c2e7376673f7374796c653d666c61742d737175617265)](https://packagist.org/packages/rawand201/filament-reveal)

[![CI](https://github.com/rawand201/filament-reveal/actions/workflows/ci.yml/badge.svg)](https://github.com/rawand201/filament-reveal/actions/workflows/ci.yml)

A secure, production-ready Filament package for safely revealing sensitive data — API keys, tokens, emails, and secrets — on demand in both **tables** and **infolists**, with optional password authentication, rate limiting, audit logging, and full dark mode support.

### Demo screencasts

[](#demo-screencasts)

GitHub **does not render `` in README files**, so embeds here would stay blank. Demos are **WebM** files in the repo, shown on a small **GitHub Pages** site (no YouTube/Vimeo — same files as [`docs/screencasts/`](docs/screencasts/)):

**[Watch the demos in your browser →](https://rawand201.github.io/filament-reveal/screencasts/index.html)**

ClipWhat it shows**main-demo.webm**Main flow — mask, toggle, reveal, copy**authenticate.webm**`requiresAuthentication()` / password modal**infolist.webm**`RevealEntry` on a view page**Offline:** clone the repo and open `docs/screencasts/index.html` in your browser.

> **First-time setup:** in the repo go to **Settings → Pages → Build and deployment**, set **Source** to **GitHub Actions**, then open the **Actions** tab and run **Pages** (or push to `main`). The live URL is usually `https://.github.io/filament-reveal/…`.

> Screencasts are **not** shipped in the Composer / Packagist zip (`export-ignore`) so installs stay small.

---

Features
--------

[](#features)

- **Hidden by default** — sensitive values are masked with bullets, asterisks, or a custom string
- **Tables &amp; Infolists** — `RevealColumn` for tables, `RevealEntry` for infolist view pages
- **Click to reveal** — eye-icon toggle fetches the real value via an encrypted AJAX request
- **Password authentication** — optionally require the user to enter their password before revealing
- **Click to copy** — click the revealed value to copy it to the clipboard
- **Security-first tokens** — each token is AES-256 encrypted, session-bound, user-bound, and expires in 5 minutes
- **Rate limiting** — configurable per-user request throttle (default 10/min)
- **Audit logging** — every reveal attempt (success or failure) is logged with user, model, IP, and timestamp
- **Laravel events** — fire `ColumnRevealed`, `ColumnRevealFailed`, and `UnauthorizedRevealAttempt` for security monitoring
- **Column whitelist** — only columns you explicitly allow can ever be revealed
- **Custom authorization** — override `authorizeRevealColumn()` per model for fine-grained access control
- **Multi-language** — ships with English and Arabic; easily extensible
- **Dark mode** — full Filament dark mode support
- **Zero build step** — no Vite/npm required in your project

---

Requirements
------------

[](#requirements)

VersionPHP^8.2Laravel10.x / 11.x / 12.x / 13.xFilament^4.0 | ^5.0---

Installation
------------

[](#installation)

```
composer require rawand201/filament-reveal
```

The service provider is auto-discovered. No manual registration needed.

### Publish assets

[](#publish-assets)

```
php artisan filament:assets
```

### Publish the config (optional)

[](#publish-the-config-optional)

```
php artisan vendor:publish --tag="filament-reveal-config"
```

### Publish translations (optional)

[](#publish-translations-optional)

```
php artisan vendor:publish --tag="filament-reveal-translations"
```

---

Quick Start
-----------

[](#quick-start)

### 1 — Add the trait to your model

[](#1--add-the-trait-to-your-model)

```
use Rawand\FilamentReveal\Concerns\HasRevealableColumns;

class User extends Authenticatable
{
    use HasRevealableColumns;

    // Whitelist of columns that are allowed to be revealed
    protected array $revealableColumns = [
        'api_token',
        'email',
    ];
}
```

### 2 — Add `RevealColumn` to your Filament table

[](#2--add-revealcolumn-to-your-filament-table)

```
use Rawand\FilamentReveal\Columns\RevealColumn;

public static function table(Table $table): Table
{
    return $table->columns([
        TextColumn::make('name'),

        RevealColumn::make('email'),

        RevealColumn::make('api_token')
            ->maskAsterisk()
            ->revealedColor('success')
            ->requiresAuthentication(),
    ]);
}
```

### 3 — Add `RevealEntry` to your Filament infolist (optional)

[](#3--add-revealentry-to-your-filament-infolist-optional)

```
use Rawand\FilamentReveal\Entries\RevealEntry;

public static function infolist(Schema $schema): Schema
{
    return $schema->components([
        RevealEntry::make('email'),

        RevealEntry::make('api_token')
            ->maskAsterisk()
            ->revealedColor('success')
            ->requiresAuthentication(),
    ]);
}
```

That's it. Values show `••••••••` by default and reveal when the eye icon is clicked.

---

Column &amp; Entry API
----------------------

[](#column--entry-api)

`RevealColumn` and `RevealEntry` share the same API.

### Mask

[](#mask)

Control how the value is hidden before it is revealed.

```
->maskBullet()       // ••••••••  (default)
->maskAsterisk()     // ********
->maskHash()         // ########
->mask('xxxx-xxxx') // custom string
```

Set the default globally in `config/filament-reveal.php`:

```
'default_mask' => 'bullet', // 'bullet' | 'asterisk' | 'hash' | 'your-custom-string'
```

### Revealed color

[](#revealed-color)

The color applied to the value after it is revealed.

```
->revealedColor('primary')  // blue (default)
->revealedColor('success')  // green
->revealedColor('warning')  // yellow
->revealedColor('danger')   // red
->revealedColor('info')     // light blue
->revealedColor('gray')     // gray
```

### Password authentication

[](#password-authentication)

Require the user to enter their account password before the value is revealed. A modal appears, collects the password, verifies it server-side, and only then fetches the real value.

```
->requiresAuthentication()

// Or pass a boolean/closure
->requiresAuthentication(fn () => auth()->user()->isAdmin())
```

Enable for all columns/entries by default in config:

```
'require_authentication' => true,
```

---

Model — `HasRevealableColumns` Trait
------------------------------------

[](#model--hasrevealablecolumns-trait)

### `$revealableColumns`

[](#revealablecolumns)

Explicit whitelist. Only columns listed here can ever be revealed, regardless of what the frontend requests.

```
protected array $revealableColumns = [
    'api_token',
    'secret_key',
    'email',
];
```

### `authorizeRevealColumn()`

[](#authorizerevealcolumn)

Override to add your own authorization logic. Return `false` to block the reveal.

```
public function authorizeRevealColumn(string $column, ?Authenticatable $user = null): bool
{
    // Only super-admins can reveal secret keys
    if ($column === 'secret_key') {
        return $user?->hasRole('super-admin') ?? false;
    }

    // Users can reveal their own data; admins can reveal anyone's
    return $this->id === $user?->id || $user?->hasRole('admin');
}
```

### `getRevealableColumnValue()`

[](#getrevealablecolumnvalue)

Override to transform the value before it is sent to the browser.

```
public function getRevealableColumnValue(string $column): mixed
{
    $value = parent::getRevealableColumnValue($column);

    if ($column === 'api_token') {
        return 'Bearer ' . $value;
    }

    return $value;
}
```

---

Configuration
-------------

[](#configuration)

Full reference for `config/filament-reveal.php`:

```
return [
    // Color of the revealed value
    'revealed_color' => 'primary', // primary | success | warning | danger | info | gray

    // Default mask style
    'default_mask' => 'bullet', // bullet | asterisk | hash | custom-string

    // Eye icon size
    'icon_size' => 'md', // sm | md | lg

    // Allow searching masked columns (disabled for security)
    'searchable' => false,

    // Require password before revealing (global default)
    'require_authentication' => false,

    // Token validity window in seconds
    'token_expiry' => 300, // 5 minutes

    // Per-user rate limiting
    'rate_limit' => [
        'max_attempts' => 10,
        'decay_minutes' => 1,
    ],

    // Log successful reveals to Laravel's log channel
    'audit_logging' => true,

    // Log failed / unauthorized reveal attempts
    'log_failed_attempts' => true,

    // Include the client IP in log entries and events
    'log_ip_address' => true,

    // Middleware applied to the reveal endpoint
    'middleware' => [
        'web',
    ],
];
```

---

Security Model
--------------

[](#security-model)

### How tokens work

[](#how-tokens-work)

When a table or infolist is rendered, each `RevealColumn` / `RevealEntry` cell receives a short-lived encrypted token instead of the actual value. The token is generated server-side and contains:

FieldContent`r`Record ID`c`Column name`m`Model class`t`Issued-at timestamp`s`HMAC of the current session ID`u`Authenticated user IDThe token is encrypted with AES-256-CBC using your `APP_KEY`. Even though it is visible in the page source, its contents cannot be read or tampered with without the key.

### What happens when the eye icon is clicked

[](#what-happens-when-the-eye-icon-is-clicked)

1. Alpine.js POSTs the token to the obfuscated endpoint (`/x-fr-{hash}`)
2. The server verifies:
    - The user is authenticated (all configured guards are checked)
    - The token decrypts successfully
    - The token has not expired (default 5 min)
    - The token's session fingerprint matches the current session
    - The token's user ID matches the authenticated user
    - The column is in the model's `$revealableColumns` whitelist
    - `authorizeRevealColumn()` returns `true`
3. The rate limiter is checked (default 10 requests/minute per user)
4. The real value is returned and rendered in the browser
5. A `ColumnRevealed` event is fired and the attempt is logged

### Defence layers

[](#defence-layers)

LayerWhat it preventsAES-256 encrypted tokenToken contents cannot be read or forgedSession bindingA token stolen from devtools cannot be used in a different browserUser bindingAdmin A cannot use a token generated for Admin B5-minute expiryCaptured tokens become worthless quicklyColumn whitelistCannot reveal columns not explicitly allowed on the model`authorizeRevealColumn()`Per-record, per-column, per-user access controlCSRF token requiredCross-site request forgery blockedRate limitingBrute force and enumeration attacks throttledObfuscated endpointEndpoint URL is not guessable without the `APP_KEY`Audit loggingEvery attempt is traceable---

Laravel Events
--------------

[](#laravel-events)

Listen to these events for security monitoring, alerting, or additional logging.

### `ColumnRevealed` — successful reveal

[](#columnrevealed--successful-reveal)

```
use Rawand\FilamentReveal\Events\ColumnRevealed;

Event::listen(ColumnRevealed::class, function (ColumnRevealed $event) {
    // $event->user         — the authenticated user
    // $event->modelClass   — e.g. "App\Models\User"
    // $event->recordId     — e.g. "42"
    // $event->columnName   — e.g. "api_token"
    // $event->ipAddress    — client IP (if log_ip_address is true)
    // $event->metadata     — ['user_agent' => '...']
});
```

### `UnauthorizedRevealAttempt` — authorization failed

[](#unauthorizedrevealattempt--authorization-failed)

```
use Rawand\FilamentReveal\Events\UnauthorizedRevealAttempt;

Event::listen(UnauthorizedRevealAttempt::class, function ($event) {
    Log::critical('Unauthorized reveal attempt', [
        'user_id' => $event->user->id,
        'column'  => $event->columnName,
        'ip'      => $event->ipAddress,
    ]);
});
```

### `ColumnRevealFailed` — token invalid, expired, or rate-limited

[](#columnrevealfailed--token-invalid-expired-or-rate-limited)

```
use Rawand\FilamentReveal\Events\ColumnRevealFailed;

Event::listen(ColumnRevealFailed::class, function ($event) {
    Log::warning('Reveal failed', ['reason' => $event->reason]);
});
```

---

Advanced Examples
-----------------

[](#advanced-examples)

### Role-based column access

[](#role-based-column-access)

```
public function authorizeRevealColumn(string $column, ?Authenticatable $user = null): bool
{
    return match ($column) {
        'secret_key' => $user?->hasRole('super-admin'),
        'api_token'  => $user?->hasAnyRole(['admin', 'developer']),
        default      => $user !== null,
    };
}
```

### Own-record-only access

[](#own-record-only-access)

```
public function authorizeRevealColumn(string $column, ?Authenticatable $user = null): bool
{
    return $this->getKey() === $user?->getKey()
        || $user?->hasRole('admin');
}
```

### Environment-conditional authentication

[](#environment-conditional-authentication)

```
RevealColumn::make('api_token')
    ->requiresAuthentication(fn () => app()->isProduction())
```

---

Translations
------------

[](#translations)

The package ships with **English** (`en`) and **Arabic** (`ar`). Add your own by publishing and creating a new locale file:

```
php artisan vendor:publish --tag="filament-reveal-translations"
```

This creates `lang/vendor/filament-reveal/{locale}/reveal-column.php`. Copy an existing file and translate the values.

---

Frequently Asked Questions
--------------------------

[](#frequently-asked-questions)

**Does the actual value ever appear in the page HTML?**No. Only an encrypted token is embedded in the page. The real value is fetched via a separate authenticated AJAX request, only when the user explicitly clicks the eye icon.

**Can I use it outside a Filament Resource?**Yes — any Filament table that uses `InteractsWithTable` supports `RevealColumn`, and any infolist that uses `InteractsWithInfolists` supports `RevealEntry`. This includes pages, relation managers, and widgets.

**What if my app uses a non-default auth guard?**The controller and the authentication modal both iterate over all guards defined in `config/auth.guards`, so custom guards work automatically.

**Does it work with Filament multi-tenancy?**Yes. Add your tenancy check inside `authorizeRevealColumn()` to restrict reveals to the current tenant's records.

**What happens if `APP_KEY` rotates?**All existing tokens become invalid immediately (they fail decryption). New tokens are issued on the next page load. No data is lost — only the short-lived reveal tokens are affected.

---

Changelog
---------

[](#changelog)

Please see [CHANGELOG](CHANGELOG.md) for more information on what has changed recently.

Compatibility
-------------

[](#compatibility)

- **PHP**: ^8.2
- **Laravel**: 10.x / 11.x / 12.x / 13.x
- **Filament**: ^4.0 | ^5.0

Support
-------

[](#support)

- **Bug reports &amp; feature requests**: please use GitHub Issues.
- **Security reports**: please follow [SECURITY](SECURITY.md) (do not disclose publicly).

Upgrade guide
-------------

[](#upgrade-guide)

- **Patch/minor releases** (e.g. `1.0.x` → `1.1.x`): update via Composer as usual.
- **Major releases** (e.g. `1.x` → `2.x`): check [CHANGELOG](CHANGELOG.md) for breaking changes and upgrade notes.

Contributing
------------

[](#contributing)

Please see [CONTRIBUTING](CONTRIBUTING.md) for details.

Code of Conduct
---------------

[](#code-of-conduct)

Please see [CODE\_OF\_CONDUCT](CODE_OF_CONDUCT.md).

Security Vulnerabilities
------------------------

[](#security-vulnerabilities)

Please review [our security policy](SECURITY.md) on how to report security vulnerabilities.

License
-------

[](#license)

The MIT License (MIT). Please see [License File](LICENSE.md) for more information.

###  Health Score

38

—

LowBetter than 85% of packages

Maintenance90

Actively maintained with recent releases

Popularity0

Limited adoption so far

Community6

Small or concentrated contributor base

Maturity48

Maturing project, gaining track record

 Bus Factor1

Top contributor holds 100% of commits — single point of failure

How is this calculated?**Maintenance (25%)** — Last commit recency, latest release date, and issue-to-star ratio. Uses a 2-year decay window.

**Popularity (30%)** — Total and monthly downloads, GitHub stars, and forks. Logarithmic scaling prevents top-heavy scores.

**Community (15%)** — Contributors, dependents, forks, watchers, and maintainers. Measures real ecosystem engagement.

**Maturity (30%)** — Project age, version count, PHP version support, and release stability.

###  Release Activity

Cadence

Every ~0 days

Total

3

Last Release

46d ago

### Community

Maintainers

![](https://www.gravatar.com/avatar/245c78bdee9764a2637094945a47605f349cd68eefbd6e8b316473fad0cbc7e5?d=identicon)[RawanD201](/maintainers/RawanD201)

---

Top Contributors

[![RawanD201](https://avatars.githubusercontent.com/u/73317192?v=4)](https://github.com/RawanD201 "RawanD201 (13 commits)")

---

Tags

filamentphpfilamentphp-pluginphpsensitive-datalaravelpasswordhidesecretfilamentcolumnSensitive datareveal

###  Code Quality

TestsPest

Code StyleLaravel Pint

### Embed Badge

![Health badge](/badges/rawand201-filament-reveal/health.svg)

```
[![Health](https://phpackages.com/badges/rawand201-filament-reveal/health.svg)](https://phpackages.com/packages/rawand201-filament-reveal)
```

###  Alternatives

[bezhansalleh/filament-shield

Filament support for `spatie/laravel-permission`.

2.8k2.9M88](/packages/bezhansalleh-filament-shield)[pboivin/filament-peek

Full-screen page preview modal for Filament

253319.6k12](/packages/pboivin-filament-peek)[croustibat/filament-jobs-monitor

Background Jobs monitoring like Horizon for all drivers for FilamentPHP

254255.2k6](/packages/croustibat-filament-jobs-monitor)[dotswan/filament-map-picker

Easily pick and retrieve geo-coordinates using a map-based interface in your Filament applications.

124139.3k2](/packages/dotswan-filament-map-picker)[stephenjude/filament-jetstream

A Laravel starter kit built with Filament inspired by Jetstream.

17554.3k2](/packages/stephenjude-filament-jetstream)[creagia/filament-code-field

A Filamentphp input field to edit or view code data.

58289.3k3](/packages/creagia-filament-code-field)

PHPackages © 2026

[Directory](/)[Categories](/categories)[Trending](/trending)[Changelog](/changelog)[Analyze](/analyze)
