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

Encryption Laravel Package

twofas/encryption

A Laravel encryption package from TwoFAS that provides straightforward helpers for securely encrypting and decrypting sensitive application data, with configurable keys, framework-friendly integration, and a simple API for common encryption tasks. It helps protect secrets and personal data across La...

View on GitHub
Deep Wiki
Context7

Getting Started

  1. Install and inspect the source before wiring it in.

    composer require twofas/encryption
    

    With zero dependents and no star history, treat the package as "read the source first." Open vendor/twofas/encryption/src/ and identify the concrete encrypter class, its constructor signature, and whether it exposes encrypt/decrypt, encryptString/decryptString, or a facade. The public surface is small enough to read end-to-end in a few minutes.

  2. Check for Laravel integration. Look at the package's composer.json for an extra.laravel.providers entry. If a service provider is present it is likely auto-discovered; if not, register it manually in bootstrap/providers.php (Laravel 11+) or config/app.php (Laravel 10 and below). If a config file ships, publish it:

    php artisan vendor:publish --provider="TwoFas\Encryption\EncryptionServiceProvider"
    
  3. Configure the key explicitly. Store the encryption key in .env rather than in the published config. If the package expects a raw binary key versus a base64-encoded string is the single most common source of "why does decryption fail" issues — verify by reading the constructor or any Key value object it ships with.

    TWOFAS_ENCRYPTION_KEY=base64:...
    
  4. First realistic use case: consuming a TwoFAS backup export. The package exists to encrypt/decrypt data in the format the TwoFAS mobile app produces. The quickest validation is to take a known encrypted backup payload (a fixture from the app or a test vector from the package's own tests), decrypt it, confirm the plaintext structure matches the app's JSON schema, then re-encrypt and confirm the app can read it back. Do this in a tinker session or a throwaway test before touching production code:

    $encrypter = app(\TwoFas\Encryption\Encrypter::class);
    $plain = $encrypter->decrypt($ciphertextFromBackupFile);
    

Implementation Patterns

Wrap the package behind your own contract. Because the package is a low-adoption dependency, do not let TwoFas\Encryption\* types leak across your codebase. Define an interface in your domain layer and bind the package behind it:

// app/Contracts/BackupCipher.php
interface BackupCipher
{
    public function encrypt(string $plaintext): string;
    public function decrypt(string $ciphertext): string;
}

// app/Services/TwoFasBackupCipher.php
final class TwoFasBackupCipher implements BackupCipher
{
    public function __construct(
        private readonly \TwoFas\Encryption\Encrypter $encrypter,
    ) {}

    public function encrypt(string $plaintext): string
    {
        return $this->encrypter->encrypt($plaintext);
    }

    public function decrypt(string $ciphertext): string
    {
        return $this->encrypter->decrypt($ciphertext);
    }
}

Bind it in a service provider: $this->app->bind(BackupCipher::class, TwoFasBackupCipher::class);. This makes swapping to a maintained implementation (e.g. illuminate/encryption with a compatible cipher, or defuse/php-encryption) a one-line change if the package goes stale.

Backup import pipeline. The most likely day-to-day flow is: user uploads their TwoFAS export → you decrypt → validate schema → persist secrets → discard the ciphertext. Keep decryption in a queued job so a malformed payload cannot hang a web request, and wrap the decrypt call in a try/catch that maps failures to a single user-facing "invalid backup password or corrupt file" message (do not leak whether the key, the format, or the password was wrong).

final class ImportTwoFasBackup implements ShouldQueue
{
    public function __construct(
        private readonly string $ciphertext,
        private readonly int $userId,
    ) {}

    public function handle(BackupCipher $cipher): void
    {
        try {
            $json = $cipher->decrypt($this->ciphertext);
        } catch (\Throwable $e) {
            report($e);
            throw new BackupImportFailed('Could not read backup file.');
        }

        $payload = json_decode($json, true, flags: JSON_THROW_ON_ERROR);
        // map services/accounts into your models...
    }
}

Encrypted model columns. If you reuse the package for at-rest encryption of stored secrets (rather than backup compatibility), a custom cast keeps it clean. Only do this if you have verified the package's output is stable and you never need to read the values from another system — for at-rest secrets, Laravel's built-in encrypted cast is the safer default and the package should be reserved for TwoFAS format interop.

protected function casts(): array
{
    return [
        'totp_secret' => EncryptedSecret::class,
    ];
}

Key rotation with dual-read. Store a key version alongside each ciphertext (v1, v2, …) and attempt decryption with the current key first, falling back to the prior key on auth failure. Re-encrypt opportunistically on successful read. Never rotate by decrypting all rows in a single request — batch it through a queued job with checkpointing.

Integration with HTTP boundaries. When decrypting data received from an external endpoint (backup sync service, admin panel), treat the ciphertext as untrusted: enforce a max size before decrypting, and reject payloads that fail base64 decoding before they reach the encrypter.

Gotchas and Tips

  • Key format is the number-one failure mode. The package may expect raw bytes, hex, or base64. base64_decode($env, true) returning false or a 16-byte key when 32 are required will produce silent auth-tag failures, not clear errors. Assert the key length in a boot-time check and fail fast in CI.

  • Binary vs. encoded output. Many small encrypter libraries return raw binary from encrypt(). If you store that in a TEXT/VARCHAR column or ship it through JSON, it will corrupt. Confirm whether the package base64-encodes internally; if not, wrap both directions yourself and be consistent everywhere.

  • Auth-tag / MAC failures usually mean the wrong key or truncated ciphertext, not a corrupted message. Distinguish these in your logs (log the key version, payload length, and exception class — never the key or plaintext). This saves hours of misdiagnosis.

  • Empty-string and null handling. Many encrypter implementations treat null differently from '', and decrypt('') may throw. Guard at your contract boundary: decide once whether null means "no secret" and short-circuit before calling the package.

  • Format compatibility is not guaranteed by version. The package is tied to a particular TwoFAS app encryption version. If the mobile app revises its scheme, this package will not silently keep up — pin the version in composer.json, and add a fixture-based regression test using a real export so you find out about format drift in CI rather than at import time.

  • Low-maintenance risk. Zero dependents means no ecosystem pressure to keep it compatible with current PHP/Laravel versions and no community bug reports. Check the last commit date and the php constraint in composer.json. If it predates your PHP version, run the test suite locally before adopting; you may need a "replace" fork.

  • Debugging tip: use a known-answer test. Generate one ciphertext from the TwoFAS app (or the package's own test fixtures), hard-code it in a test, and assert decrypt → re-encrypt → decrypt round-trips. This single test catches key-format, encoding, and version-mismatch bugs in one place.

  • Extension point: don't subclass the encrypter. Wrap it (see the contract above) rather than extending it, so you own the exception mapping and key selection and can replace the underlying implementation without touching call sites.

  • Never log ciphertext alongside config dumps. Laravel's dd(config('twofas-encryption')) in a shared log is a realistic way to leak the key; keep the key only in .env and reference it through the wrapper, not directly in config.

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.
socialdept/tenant-domains
tautvydasr/blade-class-names
socialdept/color-scales
maherelgamil/controller
coleus/widgets
coleus/table
coleus/users
coleus/music
coleus/health
coleus/calendar
coleus/notes
coleus/support
coleus/settings
coleus/apps
seampact/laravel
emmyconceptng/laravel-ng-postcode
jjsquady/round-robin
skyraptor/chatter
edlugz/sasapay
abbasudo/artisan-route