php-istio/jwt-authentication-bundle
A Symfony bundle that authenticates requests using JWT tokens issued by Istio's request authentication policy, validating headers and claims so your PHP services can trust identity passed through the service mesh.
This is a Symfony bundle, not a Laravel package — the repo is php-istio/jwt-authentication-bundle and v2.0.0 is a major bump whose headline change is Symfony 6 support. So the first step for a Laravel developer is deciding why you'd pull it in:
RequestAuthentication + AuthorizationPolicy, and you want in-app JWT validation (defense in depth, local testing without the sidecar, or per-route claims checks). In that case treat the bundle as a reference implementation of Istio JWT validation, not as a drop-in dependency.composer require php-istio/jwt-authentication-bundle:^2.0 and register the bundle in config/bundles.php.Minimal Laravel-side steps if you go the "reference implementation" route:
composer require php-istio/jwt-authentication-bundle:^2.0
DependencyInjection/ extension class first — it's the authoritative list of config keys (issuer, audience, JWKS/public key source, header name).JwtFromIstio middleware or a Laravel Guard that takes the bearer token, validates signature/iss/aud/exp, maps sub/custom claims to an Eloquent user, and rejects anything the sidecar should already have rejected (belt and braces).If you're not on Istio, stop here — this package buys you nothing over firebase/php-jwt or lcobucci/jwt.
Pattern 1 — Symfony app: install and configure the bundle.
Register the bundle, then configure issuer/audience/keys in config/packages/istio_jwt_authentication.yaml (exact key names per the bundle's README — verify against Configuration.php, don't trust blog posts). Point the security.yaml firewall at the bundle's authenticator factory.
Pattern 2 — Laravel: borrow the validation core behind an interface.
Wrap the bundle's token validator (or reimplement its checks) behind a small contract so your app code doesn't depend on Symfony's TokenInterface:
interface IstioTokenValidator {
/** @throws InvalidToken */
public function validate(string $rawToken): Claims;
}
Bind it in a service provider, then use it from middleware:
final class AuthenticateIstioJwt
{
public function __construct(private IstioTokenValidator $validator) {}
public function handle($request, Closure $next)
{
$claims = $this->validator->validate($request->bearerToken() ?? '');
Auth::setUser(new IstioUser($claims->sub, $claims->all()));
return $next($request);
}
}
Pattern 3 — JWKS with caching.
Istio's RequestAuthentication publishes keys at a JWKS endpoint. Don't fetch it per request. Cache the key set (Cache::remember, 10–60 min TTL, keyed by kid) and refresh on unknown kid once, then fail closed. Watch for clock skew: allow ~60s leeway on exp/nbf.
Pattern 4 — Claim-based authorization.
Put your policy in the JWT, not in Laravel gates you have to keep in sync with Istio. Map a claim (e.g. groups or a custom roles) to Laravel's Gate::before so both layers agree.
Pattern 5 — Local development without a sidecar.
Have a LocalJwtServiceProvider (or .env flag) that swaps in a dev key pair so php artisan serve works without Envoy. Keep the production binding strict — never allow unsigned tokens in production.
Integration tips.
iss against the cluster-specific Istio issuer (https://kubernetes.default.svc.cluster.local by default), not a generic one.kid, iss, aud on failure, never the token itself.config/*.php. Installing it pulls Symfony contracts (security-bundle, framework-bundle, http-foundation). Check composer why symfony/security-bundle after install — a stray Symfony security stack in a Laravel app is a real maintenance smell.^2.0 deliberately and run your test suite on the PHP version you actually deploy. If you were on 1.1.x with Symfony 5, expect a hard stop.RequestAuthentication rejects invalid tokens at the mesh edge; the app-layer check exists to catch misconfigured AuthorizationPolicy and direct-to-pod traffic. Test that a request bypassing Envoy is still rejected.none and HMAC-vs-RSA confusion explicitly), and whether the audience check is exact-match or subset.base64url split) before blaming the validator. A 401 with a valid-looking token is usually iss mismatch (trailing slash!), clock skew, or a kid cache miss.IstioTokenValidator adapter and the claim→user mapping. Everything Symfony-flavoured (TokenInterface, firewall config) is what v2.0.0 may have changed — keep that surface thin.lcobucci/jwt + a JWKS cache gives you the same guarantees in ~80 lines with no framework baggage. Given the size of this bundle, that's a legitimate choice, not a cop-out.How can I help you explore Laravel packages today?