ns/token-bundle
NS Token Bundle is a Symfony bundle for token-based authentication. It helps generate, store, and validate access tokens, with configurable lifetimes and simple service integration for securing APIs and user sessions.
Install the bundle
composer require ns/token-bundle
Register it
config/bundles.php:
return [
// ...
Ns\TokenBundle\NsTokenBundle::class => ['all' => true],
];
AppKernel::registerBundles().Configure the bundle in config/packages/ns_token.yaml (or app/config/config.yml on older versions). At minimum you will set the JWT secret/passphrase and the token TTL. Open src/DependencyInjection/Configuration.php in the package before guessing keys — this bundle is obscure enough (2 stars, 0 dependents) that the README may be out of sync with the actual config tree. The Configuration.php file is the source of truth.
Look at these files first, in order
README.md — intended usage and any quick-start examples.DependencyInjection/Configuration.php — the exact config keys and defaults.DependencyInjection/*Extension.php — service IDs you can inject.Resources/config/services.* — public/aliased service names.First use case: passwordless login / API auth. Generate a token after validating credentials, return it to the client, and validate it on subsequent requests. Something like:
// On login
$token = $this->get('ns_token.encoder') // confirm service ID in services.yml
->encode(['sub' => $user->getId(), 'exp' => time() + 3600]);
// On protected request
$claims = $this->get('ns_token.decoder')->decode($rawToken);
Service injection over container access. Resolve the encoder/decoder from the container once and inject them as constructor dependencies into your own services. Because this package is small and unmaintained-looking, wrapping it behind your own interface is cheap insurance if you ever swap it out:
final class TokenIssuer
{
public function __construct(private TokenEncoderInterface $encoder) {}
public function issue(User $user): string { /* ... */ }
}
Centralize token creation in one service. Don't call the encoder from controllers. Put issuance in a TokenIssuer and validation in a TokenValidator. This keeps claim structure consistent and makes it trivial to add signing-key rotation later.
Event-subscriber based validation. For API authentication, a kernel RequestEvent subscriber that reads the Authorization: Bearer <token> header, calls the decoder, and populates the TokenStorage is cleaner than sprinkling checks across controllers. Combine with Symfony's security.yaml stateless: true firewall.
Custom claim convention. Pick a small, stable claim set (sub, exp, iat, roles) and encode it as constants in your codebase. The bundle likely lets you pass an arbitrary array to the encoder — treat that as your extension point, not the bundle's own claims API.
Environment-specific secrets via env vars. Bind the config key to %env(JWT_SECRET)% so dev/staging/prod each have their own signing key. Never commit a real secret to ns_token.yaml.
Testing pattern. In tests, either point the config at a fixture secret, or inject a fake encoder/decoder into your wrapper. Do not hit a real signing key in unit tests.
Integration tips.
lexik/jwt-authentication-bundle or gesdinet/jwt-refresh-token-bundle (far more popular alternatives), check for service-name collisions before enabling both.FrameworkBundle in your kernel.Pin the version. With this few stars and no dependents, expect API drift between minor releases. Add an explicit constraint in composer.json ("ns/token-bundle": "^1.0" or whatever the tag actually is) rather than *.
Verify the package actually loads. Small bundles sometimes lack a proper *Bundle class or have a namespace mismatch. If bin/console debug:container errors, check composer.json autoload.psr-4 in the package against the class namespace.
Config key names are the #1 source of pain. Most issues people hit with obscure bundles are typos in YAML that silently fall back to defaults. After editing config, run:
bin/console debug:config ns_token
to confirm the parsed tree matches what you expect.
Clock skew. If tokens are validated across multiple hosts, allow a small leeway (30–60s) when checking exp/nbf. Check whether the bundle exposes a leeway option; if not, subtract a few seconds from your exp when issuing.
TTL defaults are often too long. Inspect the Configuration defaults — many JWT bundles ship with ttl: 3600, but some ship with a much larger default. Explicitly set ttl rather than relying on it.
Secret encoding. Some JWT bundles expect a base64-encoded secret, others expect raw bytes. Read the encoder code before pointing it at an env var, or you'll get signature-mismatch errors that look like clock-skew errors. A quick sanity check: encode then immediately decode in a tinker shell.
Debugging signature failures. Decode the token payload (middle segment) with base64_decode in a scratch script to confirm claims are what you think — this isolates format problems from signature problems. If the payload looks right but decode fails, it's the secret or algorithm.
Extension points to prefer. Since you can't rely on long-term maintenance, extend through:
security.yaml for the actual firewall,RequestEvent subscriber for header parsing.Avoid subclassing bundle internals; those are the classes most likely to change.
Migration escape hatch. Keep your wrapper interface narrow enough that migrating to firebase/php-jwt (a single, well-maintained class) or lexik/jwt-authentication-bundle is a half-day change. For a 2-star dependency, this is not paranoia — it's justifiable risk management.
Report issues upstream. With a project this small, filing a clear issue (with the debug:config output and Symfony/PHP versions) is often the fastest way to get a real fix.
How can I help you explore Laravel packages today?