web-token/jwt-signature-algorithm-hmac
HMAC signature algorithm support for the web-token/jwt framework. Provides HS256, HS384, and HS512 signing and verification using shared secrets, integrating with JWT creation and validation in PHP.
This package is not a JWT library — it is an algorithm plugin for web-token/jwt-framework. On its own it does nothing; it registers the HS256, HS384, and HS512 signature algorithms into the framework's AlgorithmManager. Install it alongside the core packages:
composer require web-token/jwt-framework:^2.2 \
web-token/jwt-signature-algorithm-hmac
(Heads-up: on ^3.0 this package is merged into web-token/jwt-library and this repo is effectively frozen — see Gotchas.)
Look first at:
README.md — the only real docs; everything else is source.Hmac.php and the three concrete classes HS256, HS384, HS512 under src/.web-token/jwt-framework — the usage contract lives there, not here.First use case — sign a token with a shared secret:
use Jose\Component\Core\AlgorithmManager;
use Jose\Component\Signature\JWSBuilder;
use Jose\Component\Signature\Serializer\CompactSerializer;
use Jose\Component\Signature\Algorithm\HS256;
use Jose\Component\KeyManagement\JWKFactory;
$algorithms = new AlgorithmManager([new HS256()]);
$jwk = JWKFactory::createFromSecret(
config('jwt.hmac_secret'), // raw string, ≥32 bytes
['alg' => 'HS256', 'use' => 'sig']
);
$builder = new JWSBuilder($algorithms);
$jws = $builder->create()
->withPayload(json_encode(['sub' => $user->id, 'exp' => time() + 3600]))
->addSignature($jwk, ['alg' => 'HS256'])
->build();
$token = (new CompactSerializer())->serialize($jws);
Verification is the mirror: JWSVerifier + AlgorithmManager([new HS256()]) + CompactSerializer::unserialize().
1. Bind the algorithm manager as a singleton. Do not construct a new AlgorithmManager per request inside controllers — wire it once in a provider:
$this->app->singleton('jose.algorithms.hmac', fn () => new AlgorithmManager([
new HS256(),
new HS384(),
new HS512(),
]));
2. Prefer the convenience factory over hand-rolled JWKs. JWKFactory::createFromSecret($s, ['alg' => 'HS256']) handles the base64url k encoding and oct key type correctly. Manually building an array with a base64-encoded k is the most common source of "invalid signature" bugs.
3. Wrap signing in a Signer service, not middleware. Typical clean shape:
class HmacJwtSigner
{
public function __construct(private AlgorithmManager $algorithms) {}
public function issue(array $claims, string $alg = 'HS256'): string { /* ... */ }
public function verify(string $token, string $alg = 'HS256'): array { /* throws */ }
}
This keeps JWSBuilder out of controllers and lets you swap to RS256/ES256 later by injecting a different AlgorithmManager — the rest of your code is untouched.
4. Pair with web-token/jwt-checker for claim validation. This package signs but does nothing about exp, nbf, aud. Add HmacJwtSigner::verify() → ClaimCheckerManager with ExpirationTimeChecker, IssuedAtChecker, etc. Verification without checks is a footgun.
5. Testing: HMAC is deterministic given secret + payload. Build a HmacJwtSigner with a fixed test secret in phpunit.xml via env('JWT_HMAC_SECRET', str_repeat('a', 32)), and assert against a golden token. No mocking needed.
6. Integration tip: If you already use firebase/php-jwt, don't add this package. This lives in a different mental model (algorithm-manager / JWK / JWS object model) and rewards you only if you need multi-algorithm support, JWKS rotation, or the full jwt-framework (JWE, JWS, JWT, key management). For a single HS256 secret, it is over-engineering.
web-token/jwt-framework:^3.0 the HMAC algorithms shipped inside web-token/jwt-library. If you composer require this repo alongside 3.x you get a version-conflict or an unused duplicate. Pin ^2.2 for the framework if you want this exact package, or drop it and require web-token/jwt-library on 3.x.strlen($secret) >= 32 at boot in a config() guard.JWKFactory::createFromSecret treats the input as raw bytes and base64url-encodes internally. If you pass a base64 string you already encoded, you'll get a double-encoded key and mismatched signatures across services. Store the raw secret in .env.alg header must match the algorithm you built the JWK with. A JWK with 'alg' => 'HS256' signed by new HS512() throws. The algorithm manager alone is not enough — the key carries its own alg.alg: none protection here. This package doesn't gate the none algorithm; the AlgorithmManager you build does. Never include none unless you truly intend unauthenticated tokens.JWSVerifier dispatches on the token's alg header, a verifier allowing HS* and RS* interchangeably lets an attacker present an RS256 public key as an HMAC secret. Keep HMAC and asymmetric verification in separate AlgorithmManager instances (separate guards in Laravel).exp isn't validated for you. Signing puts exp in the payload; nothing checks it during verify(). That's web-token/jwt-checker's job, wired manually into your verify() method.JWSVerificationFailedException, InvalidArgumentException, InvalidKeyException all bubble out of verify(). Catch the base \Throwable in your guard and translate to AuthenticationException — otherwise Laravel 500s on a malformed token.JWTGuard or a middleware that pulls Authorization: Bearer …, calls verify(), and returns the claims. Budget an hour for it.HS1 (don't), subclass Hmac and implement getAlgorithmName(). Anything non-standard should live in your own AlgorithmManager binding, not a fork of this package.How can I help you explore Laravel packages today?