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

# Add JWT Authentication to Your Phalcon API

Secure your Phalcon API using JWT authentication with JWKS.

## How Zuplo Handles It

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

## Phalcon Backend Code

```php
<?php

use Phalcon\Mvc\Micro;
use Phalcon\Http\Response;
use Firebase\JWT\JWT;
use Firebase\JWT\JWK;

$app = new Micro();

define('ISSUER', 'https://my-api-a32f34.zuplo.api/__zuplo/issuer');
define('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(Micro $app): ?object {
    $authHeader = $app->request->getHeader('Authorization');

    if (empty($authHeader) || !str_starts_with($authHeader, 'Bearer ')) {
        return null;
    }

    $token = substr($authHeader, 7);

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

        if ($decoded->iss !== ISSUER) {
            return null;
        }

        return $decoded;
    } catch (\Exception $e) {
        return null;
    }
}

// JWT middleware
$app->before(function () use ($app) {
    $path = $app->request->getURI();

    // Skip authentication for public routes
    if ($path === '/public') {
        return true;
    }

    $user = authenticateJwt($app);

    if ($user === null) {
        $response = new Response();
        $response->setStatusCode(401);
        $response->setJsonContent(['error' => 'Unauthorized']);
        $response->send();
        return false;
    }

    $app->setService('user', function () use ($user) {
        return $user;
    });

    return true;
});

$app->get('/protected', function () use ($app) {
    $response = new Response();
    $response->setJsonContent([
        'message' => 'Access granted',
        'user' => $app->getService('user')
    ]);
    return $response;
});

$app->handle($_SERVER['REQUEST_URI']);
```

## 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)
