Weave Code
Weaver
Helps Laravel developers discover, compare, and choose open-source packages. See popularity, security, maintainers, and scores at a glance to make better decisions.
Feedback
Share your thoughts, report bugs, or suggest improvements.
Subject
Message

Nova Secret Field Laravel Package

norman-huth/nova-secret-field

A Laravel Nova field for securely storing and managing secrets like API keys and passwords. It masks sensitive values in the UI, encrypts data at rest, and lets you update credentials without exposing them. Ideal for admin panels that need safe secret handling without revealing values to users.

View on GitHub
Deep Wiki
Context7

Getting Started

This is a single-purpose Nova field: a text input rendered as dots with an eye toggle to reveal the current value. Treat it as a UI component, not a security feature. With 3 stars, zero dependents, and an archived repo, you are effectively adopting an unmaintained snippet — plan to vendor the code rather than depend on Packagist.

Minimal steps:

  1. Read the source before installing. Open src/ and resources/js/components/. You need three facts:
    • the exact namespace/class of the field (vendor is Muetze42, package nova-secret-field; the class is under Muetze42\NovaSecretField),
    • the $component string it registers (must match the Nova.booting component name in dist/js/field.js),
    • the composer.json constraint on laravel/nova — last release was 2022-07-12, so it targets Nova 4 and will not have been touched for Nova 5.
  2. Prefer a path/VCS repository or a straight copy. Given the archive status, either composer require muetze42/nova-secret-field and immediately fork it into app/Nova/Fields/Secret.php, or add it as a VCS repo pinned to a commit. Do not let a fresh composer update decide your fate.
  3. Add it to a resource form. The first real use case is an admin-managed third-party credential on a settings/integrations resource — e.g. a Stripe or SMTP password that support staff occasionally need to eyeball to confirm a paste landed correctly:
use Muetze42\NovaSecretField\Secret; // verify against src/

public function fields(Request $request)
{
    return [
        Secret::make('API Token')
            ->rules('required', 'string', 'max:255')
            ->hideFromIndex()
            ->hideFromDetail()
            ->help('Paste the token; use the eye icon to verify before saving.'),
    ];
}

Start with the field hidden from index and detail. The reveal toggle only makes sense on the form, where the user is the one who typed the value.

Implementation Patterns

Store encrypted, not plaintext. Pair the field with a model cast so the masked input maps to encrypted storage:

protected $casts = ['api_token' => 'encrypted'];

The field is a plain input; it does no hashing and no encryption for you.

Do not clobber existing secrets on a blank submit. This is the single most important pattern. Without it, an admin editing an unrelated attribute wipes the token. Either handle it in fillUsing:

Secret::make('API Token')
    ->fillUsing(function ($request, $model, $attribute) {
        if (filled($request->input($attribute))) {
            $model->{$attribute} = $request->input($attribute);
        }
    })

…or make the field ->nullable() and short-circuit in a model mutator (setApiTokenAttribute ignores empty strings). Nova's built-in Password field has this "only fill when non-empty" behaviour baked in; check whether this package's fill() does the same before assuming.

Mask the detail and index views instead of hiding them entirely. If reviewers need to know a secret exists, show a fixed mask via displayUsing on a separate read-only field, and keep the actual secret field onlyOnForms():

Text::make('API Token', 'api_token')
    ->displayUsing(fn ($value) => $value ? '••••••••' : '—')
    ->onlyOnDetail(),

Secret::make('API Token', 'api_token')
    ->onlyOnForms()
    ->rules('nullable', 'string'),

Using a distinct Text field for display avoids depending on the custom Vue component in the read-only path, so if the compiled asset breaks you still have a working detail page.

Gate the reveal behind policy. ->canSee(fn ($request) => $request->user()->can('manageSecrets')) on the form field keeps the masked input out of the DOM for users who should never touch it. Note this hides the input, not the payload — see Gotchas.

Pair with an action for rotation. A "Rotate token" action that generates a value server-side and returns it once is a better workflow than repeatedly revealing a long-lived secret in a form:

// in an Action's handle()
$model->update(['api_token' => Str::random(64)]);
return Action::message('Token rotated. Copy it from the field now.');

Testing. The reveal toggle is pure front-end, so test the write path at the HTTP level (actingAs($admin)->put('/nova-api/settings/1', [...])) and assert the token is present/absent after a partial update. That test is where you'll catch the blank-submit bug.

Gotchas and Tips

  • The value is in the JSON payload regardless of the toggle. The eye icon just swaps an input type between password and text. Anything hidden only by the mask is fully readable in the network tab. Never place it on index or detail unless you actually intend to transmit the secret to that screen.
  • Archived means no Nova 5 support. The repo was last released in 2022-07-12 and is archived. Nova 5's asset pipeline and Tailwind build changed; a stale dist/js/field.js may fail to register or render unstyled. Verify by loading any form using the field and checking the console for a missing-component warning.
  • Rebuild the assets if you fork. If you use a path repository, run the package's npm install && npm run prod (or dev) and confirm the service provider's Nova::script(...)/Nova::style(...) paths point at the rebuilt dist/.
  • sortable() and searchable() are meaningless here. Encrypted or masked values can't be sorted or searched server-side; adding those flags produces misleading UI.
  • Dark mode and Tailwind drift. Nova 4/5 restyled form controls. A custom component that hardcodes border-gray-300 or bg-white often looks broken in dark mode. Check the Vue component's classes before shipping, or accept the cosmetic debt.
  • Empty string vs null. $request->input() returns '' for an untouched blank input, not null. Use filled() rather than ! is_null() when guarding writes.
  • Extension points are thin. The field is a few dozen lines: component(), resolve(), fill(). Rather than patching around it, copy it to app/Nova/Fields/Secret.php, rename the $component to your own namespace, and own it. That removes the abandoned dependency entirely and lets you add the non-empty fill() guard directly.
  • Debugging a blank render. If the field appears as an empty div, the Vue component name in dist/js/field.js doesn't match $component. Confirm registration with Nova.booting((app, store) => { app.component('...', require('./components/Field.vue').default) }) in resources/js/field.js.
  • Given the scope, consider just not using it. Text::make('API Token')->withMeta(['type' => 'password']) gets you a masked input with zero dependencies; a 20-line custom field gets you the toggle. For a 3-star archived package with no maintainer, the build-versus-buy math is not close.
Weaver

How can I help you explore Laravel packages today?

Conversation history is not saved when not logged in.
Prompt
Add packages to context
No packages found.
3mdikram/worldmoneywords
snowsoft/config
itmosoft/laravel-subscriptions
larva/laravel-ai-qwen
diephp/sequences
users-au/socialite-provider
twidpay/twid-logger
mrnewport/laravel-plaid
mrnewport/laravel-repo
mrnewport/laravel-docsign
mrnewport/laravel-stow
mrnewport/laravel-flow
mrnewport/laravel-priceable
mrnewport/laravel-nestedterms
fredmeng/laravel-apidoc-generator
itstructure/laravel-multilingual-tools
pdustdar/elasticsearch
markupai/php-sdk
rlacerda83/lumem-microservice-shipment
uni-method/symfony-lib