PHP (Laravel / Symfony / vanilla)
Setup
composer require firebase/php-jwt guzzlehttp/guzzle
Vanilla PHP middleware
<?php
use Firebase\JWT\JWT;
use Firebase\JWT\JWK;
use GuzzleHttp\Client;
$authUrl = getenv('AUTH_URL') ?: 'http://auth:4000';
function currentUser(string $authUrl): array {
$token = $_COOKIE['identsphere_at']
?? (str_starts_with($_SERVER['HTTP_AUTHORIZATION'] ?? '', 'Bearer ')
? substr($_SERVER['HTTP_AUTHORIZATION'], 7) : null);
if (!$token) {
http_response_code(401);
exit(json_encode(['error' => 'unauthenticated']));
}
static $jwks = null;
if ($jwks === null) {
$client = new Client();
$jwks = json_decode((string) $client->get("$authUrl/.well-known/jwks.json")->getBody(), true);
}
try {
$decoded = JWT::decode($token, JWK::parseKeySet($jwks));
return (array) $decoded;
} catch (\Throwable $e) {
http_response_code(401);
exit(json_encode(['error' => 'invalid token']));
}
}
// usage
$claims = currentUser($authUrl);
echo json_encode(['user_id' => $claims['sub'], 'org_id' => $claims['org']]);
Laravel middleware
// app/Http/Middleware/AuthenticateWithIdentSphere.php
namespace App\Http\Middleware;
use Closure;
use Firebase\JWT\JWT;
use Firebase\JWT\JWK;
class AuthenticateWithIdentSphere {
public function handle($request, Closure $next) {
$token = $request->cookie('identsphere_at')
?? str_replace('Bearer ', '', (string) $request->header('Authorization'));
if (!$token) abort(401);
$jwks = cache()->remember('identsphere_jwks', 300, function () {
return json_decode(file_get_contents(config('app.auth_url') . '/.well-known/jwks.json'), true);
});
try {
$decoded = JWT::decode($token, JWK::parseKeySet($jwks));
$request->merge(['identsphere_user' => (array) $decoded]);
return $next($request);
} catch (\Throwable $e) {
abort(401);
}
}
}
Register in app/Http/Kernel.php and apply to routes:
Route::middleware(AuthenticateWithIdentSphere::class)->group(function () {
Route::get('/api/me', fn (Request $r) => response()->json($r->get('identsphere_user')));
});
Symfony
Build a custom Authenticator that extracts the JWT and validates it via firebase/php-jwt. The flow mirrors the Laravel example.
Proxying login through PHP
$client = new \GuzzleHttp\Client();
$upstream = $client->post("$authUrl/v1/auth/login", [
'json' => json_decode(file_get_contents('php://input'), true),
'http_errors' => false,
]);
foreach ($upstream->getHeader('Set-Cookie') as $cookie) {
header("Set-Cookie: $cookie", false);
}
http_response_code($upstream->getStatusCode());
echo (string) $upstream->getBody();