---
title: "Add JWT Authentication to Your Swoole API"
description: "Secure your Swoole API using JWT authentication with JWKS."
canonicalUrl: "https://zuplo.com/use-cases/api-key-auth/php/swoole/jwt-backend"
framework: "Swoole"
language: "PHP"
authStrategy: "JWT with JWKS"
pageType: use-case
---

# Add JWT Authentication to Your Swoole API

Secure your Swoole API using JWT authentication with JWKS.

## How Zuplo Handles It

Let Zuplo issue short-lived JWTs signed with a JWKS your Swoole backend can verify — no long-lived API keys touch your origin.

## Swoole Backend Code

```php
<?php

use Swoole\Http\Server;
use Swoole\Http\Request;
use Swoole\Http\Response;
use Firebase\JWT\JWT;
use Firebase\JWT\JWK;

const ISSUER = 'https://my-api-a32f34.zuplo.api/__zuplo/issuer';
const JWKS_URI = ISSUER . '/.well-known/jwks.json';

$jwksCache = null;
$jwksCacheTime = null;

function fetchJwks(): array {
    global $jwksCache, $jwksCacheTime;

    if ($jwksCache === null || time() - $jwksCacheTime > 600) {
        $response = file_get_contents(JWKS_URI);
        $jwksCache = json_decode($response, true);
        $jwksCacheTime = time();
    }
    return $jwksCache;
}

function authenticateJwt(Request $request): array {
    $authHeader = $request->header['authorization'] ?? '';

    if (empty($authHeader) || !str_starts_with($authHeader, 'Bearer ')) {
        return ['error' => 'No token provided', 'user' => null];
    }

    $token = substr($authHeader, 7);

    try {
        $jwks = fetchJwks();
        $decoded = JWT::decode($token, JWK::parseKeySet($jwks), ['RS256']);

        if ($decoded->iss !== ISSUER) {
            return ['error' => 'Invalid issuer', 'user' => null];
        }

        return ['error' => null, 'user' => $decoded];
    } catch (\Exception $e) {
        return ['error' => $e->getMessage(), 'user' => null];
    }
}

$server = new Server('0.0.0.0', 9501);

$server->on('request', function (Request $request, Response $response) {
    $response->header('Content-Type', 'application/json');

    $path = $request->server['request_uri'];

    if ($path === '/protected') {
        $auth = authenticateJwt($request);

        if ($auth['error'] !== null) {
            $response->status(401);
            $response->end(json_encode([
                'error' => 'Unauthorized',
                'details' => $auth['error']
            ]));
            return;
        }

        $response->end(json_encode([
            'message' => 'Access granted',
            'user' => $auth['user']
        ]));
        return;
    }

    $response->status(404);
    $response->end(json_encode(['error' => 'Not found']));
});

$server->start();
```

## Example Request

```bash
curl -X GET \
  'https://your-api.zuplo.dev/your-route' \
  -H 'Authorization: Bearer YOUR_API_KEY'
```

## Learn More

- [API Key Authentication on Zuplo](https://zuplo.com/docs/policies/api-key-auth-inbound)
- [JWT Authentication on Zuplo](https://zuplo.com/docs/policies/open-id-jwt-auth-inbound)
- [All use cases](https://zuplo.com/use-cases)
