innobyte/token-bundle
A PHP token bundle for generating, storing, and validating tokens through a simple API. Useful for token-based workflows and lightweight integration into Laravel or other PHP applications, with configurable handling for common token use cases.
First, the reality check. innobyte/token-bundle is a Symfony2 bundle, not a Laravel package. There is no service provider, no facade, no config publish, and nothing that will autoload into Laravel's container. As a Laravel developer your two realistic entry points are (a) treat it as a design reference for how to model one-time access tokens, or (b) treat it as a Symfony sidecar you call over HTTP. Direct composer require into a Laravel app is not a path.
If you are in a Symfony app (the only supported case):
composer require innobyte/token-bundle — and immediately check the composer.json PHP/Doctrine constraints. This is a Symfony2-era bundle; expect it to fight with PHP 8 and modern Doctrine.AppKernel.php (or bundles.php on newer Symfony).doctrine:schema:update --dump-sql or generate a migration for the token table.Resources/config/services.yml and inject it — that is your day-to-day handle.Where to look first, in this order:
Entity/Token.php — the actual data model. You want the fields: token value, an access/scope identifier, an expiry, and a consumed flag. This file is the whole package in miniature.DependencyInjection/Configuration.php — the config tree; tells you what TTL and entity-class options exist.Resources/config/services.yml — service ids and whether they are public (relevant for debugging).generate(...) and consume(...) semantics.First use case: a scoped, single-use link. Generate a token with access user.confirm_email bound to a user, email https://app/confirm?token=…, and on the route consume it only if the token's access matches. That is the canonical shape this bundle was built for.
Pattern A — native Symfony usage (the intended path). Inject the token manager, generate against a named access scope, build the URL, then consume on the landing route and branch on the result (valid / expired / already consumed).
// Shape of the flow — verify method names against the bundle source.
$token = $tokenManager->generate('user.confirm_email', $user, $ttl);
// ... later, in the controller ...
if (!$tokenManager->consume($tokenString, 'user.confirm_email')) {
// expired, unknown, wrong scope, or already used
}
Pattern B — Laravel sidecar. Only worth it if you are already running Symfony somewhere. Expose two endpoints (POST /tokens, POST /tokens/consume) from a minimal Symfony app and call them with Http:: from Laravel. For a package this small, the operational cost almost always exceeds the benefit.
Pattern C — port the model, not the code (recommended). Recreate the semantics as an Eloquent model. You want: token (stored hashed), access (string scope), expires_at, consumed_at, and a nullable morphs/foreignId owner. Then wrap it in a small TokenService with issue(string $access, ?Model $owner, Duration $ttl) and redeem(string $plain, string $access): bool. This gets you the bundle's useful idea — scoped, consumable tokens — in idiomatic Laravel in an afternoon.
Pattern D — read it as a spec. Even if you use Laravel's built-in URL::temporarySignedRoute or Password::createToken, the bundle's access-scoping concept is worth stealing: it prevents a password-reset token from being redeemed as an invite token, which signed URLs alone do not do.
Workflows that pay off day to day:
where('expires_at', '<', now())->delete()) — the bundle's own cleanup story is thin, and this table grows fast.Integration tip: return the result of consumption as an enum (Valid, Expired, AlreadyUsed, WrongScope) rather than a bool. The bundle's boolean-ish contract forces you to guess why a link failed, and users ask.
auth:sanctum, you will be confused for a day.UPDATE tokens SET consumed_at = NOW() WHERE token = ? AND consumed_at IS NULL and check affected rows. This is the single most common bug in token systems.access string.DateInterval depending on the config node, and expect timezone surprises if your DB and PHP clocks disagree. Assert on the generated expiry in a test rather than trusting the default.bin/console debug:container <id> (or app/console container:debug on Symfony2) — services in modern Symfony are private by default, so $container->get() in a controller will fail even though the bundle "works."composer require fails, it is almost certainly the platform constraint, not a missing extension. Read composer.json before debugging anything else.resolve_target_entities to attach your own owner relation. Both are stable-ish; neither is documented well outside the source.URL::temporarySignedRoute for tamper-proof links, Password::createToken for resets, and a 60-line custom tokens table for anything multi-scope. Reach for the port only when you specifically need the access-scope + single-consumption pairing.How can I help you explore Laravel packages today?