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

Token Generator Laravel Package

wernerdweight/token-generator

wernerdweight/token-generator is a Laravel/PHP package for generating unique, secure tokens for API keys, authentication, and identifiers. It offers configurable length and character sets with collision-safe generation for dependable results.

View on GitHub
Deep Wiki
Context7

Getting Started

Install and confirm what you actually got. The package is tiny (one star, no dependents, last release Feb 2023), so budget five minutes to read the source before wiring it into anything:

composer require wernerdweight/token-generator

Laravel will auto-discover the service provider if the package declares one. Open vendor/wernerdweight/token-generator/ and look at, in order:

  1. composer.json — the extra.laravel block tells you whether a provider/facade is registered, and the autoload.psr-4 namespace tells you the class name.
  2. src/ — usually a single TokenGenerator class (plus a TokenGeneratorServiceProvider and maybe a Facade). Read the public methods; that is the entire API surface.
  3. config/ — if present, publish it:
php artisan vendor:publish --tag=token-generator-config

If no config tag is documented, check the provider's mergeConfigFrom call for the real tag name, or skip it and pass options per call.

First use case: invite codes on registration. The classic day-one need is a short, human-typeable code that goes into an email or a share link. Resolve the generator from the container in php artisan tinker and confirm the signature before writing a migration:

app(\Wernerdweight\TokenGenerator\TokenGenerator::class)->generate();

Then wire it into a model:

// app/Models/Invite.php
protected static function booted(): void
{
    static::creating(function (Invite $invite) {
        $invite->code ??= app(TokenGenerator::class)->generate();
    });
}

with a migration that enforces uniqueness at the database level:

$table->string('code', 64)->unique();

If the tinker call fails, you've found the real method name (often generate(), make(), token(), or random()) — write that down before proceeding.

Implementation Patterns

Inject the generator, don't new it. Even if the package ships a facade, constructor injection keeps it swappable and testable:

final class CreateInvite
{
    public function __construct(private TokenGenerator $tokens) {}

    public function handle(int $length = 12): Invite
    {
        return Invite::create(['code' => $this->tokens->generate($length)]);
    }
}

Separate "short human code" from "long machine secret". Most token failures come from using one length for both. Two named profiles in config/token-generator.php (or simply two wrapper methods) work well:

// config/token-generator.php (names are illustrative — align with the package)
return [
    'profiles' => [
        'invite'   => ['length' => 10, 'alphabet' => 'ABCDEFGHJKLMNPQRSTUVWXYZ23456789'],
        'api_key'  => ['length' => 40], // full alphabet / CSPRNG default
    ],
];

Dropping 0/O/1/I from the invite alphabet removes 90% of support tickets about "the code doesn't work."

Wrap it in your own service. Given the package's age and lack of adoption, define an app-level interface and let the package be one implementation:

interface TokenSource { public function make(int $length): string; }

final class PackageTokenSource implements TokenSource
{
    public function __construct(private TokenGenerator $generator) {}
    public function make(int $length): string { return $this->generator->generate($length); }
}

Bind it in a service provider. Swapping to Str::random() or random_bytes() later becomes a one-line change.

Store hashes, not tokens. For API keys and reset tokens, persist hash('sha256', $token) and show the plaintext exactly once:

$plain = $this->tokens->generate(40);
$user->forceFill(['api_key_hash' => hash('sha256', $plain)])->save();
return $plain; // last time it exists in plaintext

Lookup is then where('api_key_hash', hash('sha256', $presented)), and comparison should still use hash_equals() when you compare strings directly.

Collision-safe creation. Wrap generation in a retry so the unique index is the source of truth rather than a pre-check that races:

foreach (range(1, 5) as $attempt) {
    try {
        return Invite::create(['code' => $gen->generate(10)]);
    } catch (UniqueConstraintViolationException) {
        continue;
    }
}
throw new RuntimeException('Token space exhausted — increase length.');

Factories and seeders. Generate in the factory, not the model hook, when you need deterministic-ish test data:

'code' => fn () => app(TokenGenerator::class)->generate(10),

Form requests / controllers. Validate the presented token by length and charset before hitting the database — it cheaply rejects junk and keeps query logs clean:

'code' => ['required', 'string', 'size:10', 'regex:/^[A-Z2-9]{10}$/'],

Gotchas and Tips

Do the entropy math before choosing a length. Bits ≈ length × log2(alphabetSize). A 10-char code from a 32-symbol alphabet is ~50 bits — fine for a slow, rate-limited invite flow, unacceptable for an API key. 128 bits is the usual floor for anything that authenticates.

Case-insensitive collations will bite you. utf8mb4_unicode_ci treats aB and Ab as equal, so if your alphabet is mixed-case your unique index silently rejects valid tokens — or worse, a lookup matches the wrong row. Either use a binary/case-sensitive column (->collation('utf8mb4_bin')) or restrict the alphabet to a single case.

Verify it uses a CSPRNG. The package is a thin wrapper, and older wrappers sometimes default to mt_rand/str_shuffle, which is not cryptographically secure. Check the source for random_bytes or random_int. If they're absent, do not use this for password resets, API keys, or anything security-relevant — use Str::random() / random_bytes() instead and keep the package for cosmetic codes only.

Config caching surprises. After editing the published config, run php artisan config:cache — and remember that any config value read at service-provider boot time is frozen until you clear it, which produces confusing "my change did nothing" behavior in local dev.

Watch the alphabet for URL/email safety. If the alphabet includes +, /, or =, tokens break in query strings and get mangled by email clients. Prefer a Base32/hex alphabet, or rtrim(strtr(base64_encode($raw), '+/', '-_'), '=') if you must.

Don't log tokens. Token generation inside a model hook means the value flows through Log::info($model) and exception reporters. Add the attribute to $hidden and consider a toArray() filter on the model.

Test the failure mode, not just the happy path. Assert the unique constraint actually throws, that the retry loop recovers, and that a token of the wrong length is rejected by validation. Also add a test that fails if the generator ever returns a 0-length or non-string value — cheap insurance against a package upgrade.

Extension points, in order of preference: (1) your own TokenSource interface, (2) a decorator that post-processes package output (e.g., uppercases and strips ambiguous characters), (3) binding a subclass over the package class in the container. Avoid editing vendor/.

Pin the version. Last release was 2023-02-21 with almost no community usage; treat a surprise major bump as a breaking change. composer require wernerdweight/token-generator:^1.0 (adjust to the actual constraint) plus a lock file committed is enough — but check the repo's open issues for the actual maintenance signal before betting a production auth flow on it.

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.
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
tacticmedia/scim-server-bundle
neika-ai/symfony-bundle
mattleads/agent-publishing-bundle
glavweb/framework-bundle