auth0-php-api

Par auth0 · agent-skills

À utiliser pour sécuriser des endpoints d'API PHP avec validation de token JWT Bearer, vérification de scopes/permissions ou authentification stateless — intègre le SDK auth0/auth0-php en mode API (`STRATEGY_API`) pour les API REST recevant des access tokens depuis des SPAs, applications mobiles ou autres clients. Se déclenche sur : auth0-php API, validation JWT PHP, `getBearerToken`, `STRATEGY_API`, Bearer auth PHP.

npx skills add https://github.com/auth0/agent-skills --skill auth0-php-api

Intégration Auth0 PHP API

Protégez les endpoints PHP API avec la validation des tokens d'accès JWT en utilisant auth0/auth0-php en mode API (STRATEGY_API).


Prérequis

  • PHP 8.2+ avec les extensions : mbstring, openssl, json
  • Composer installé
  • Ressource Auth0 API configurée (pas une Application - doit être une API)
  • Si vous n'avez pas encore Auth0, utilisez d'abord la skill auth0-quickstart

Quand NE PAS utiliser

  • Applications PHP web avec flux login/logout - Utilisez auth0-php pour l'authentification basée sur les sessions
  • Applications Laravel - Utilisez auth0/laravel-auth0 qui inclut le support intégré pour les API
  • Applications Symfony - Utilisez auth0/symfony avec son bundle de sécurité
  • Applications monopage - Utilisez auth0-react, auth0-vue, ou auth0-angular pour l'authentification côté client
  • Émission de tokens - Cette skill sert à valider les tokens d'accès, pas à les émettre

Workflow de démarrage rapide

1. Installer le SDK

composer require auth0/auth0-php vlucas/phpdotenv guzzlehttp/guzzle guzzlehttp/psr7 "symfony/cache:^7.0"
  • auth0/auth0-php - Le SDK Auth0 (v8.x)
  • vlucas/phpdotenv - Charger les fichiers .env dans $_ENV
  • guzzlehttp/guzzle + guzzlehttp/psr7 - Client HTTP PSR-18 requis par le SDK
  • symfony/cache - Cache PSR-6 pour le cache des clés JWKS (recommandé pour la production)

2. Créer une API Auth0

Vous avez besoin d'une API (pas une Application) dans Auth0.

STOP - demandez à l'utilisateur avant de continuer.

Posez exactement cette question et attendez sa réponse avant de faire quoi que ce soit d'autre :

"Comment souhaitez-vous créer la ressource Auth0 API ?

  1. Automatisé - Je vais exécuter les scripts de l'Auth0 CLI qui créent la ressource et écrivent les valeurs exactes dans votre .env automatiquement.
  2. Manuel - Vous créez l'API vous-même dans le tableau de bord Auth0 (ou via auth0 apis create) et me fournissez le Domain et Audience.

Laquelle préférez-vous ? (1 = Automatisé / 2 = Manuel)"

Ne procédez à AUCUNE étape de configuration jusqu'à ce que l'utilisateur ait répondu. Ne choisissez PAS par défaut le manuel.

Si l'utilisateur a choisi Automatisé, suivez le Guide de configuration pour les scripts CLI complets. Le chemin automatisé écrit .env pour vous - passez l'étape 3 ci-dessous et procédez directement à l'étape 4.

Si l'utilisateur a choisi Manuel, suivez le Guide de configuration (section Configuration manuelle) pour les instructions complètes. Puis continuez avec l'étape 3 ci-dessous.

Référence rapide pour la création manuelle d'une API :

# Utilisant l'Auth0 CLI
auth0 apis create \
  --name "My PHP API" \
  --identifier https://my-api.example.com \
  --json

Ou créez manuellement dans le tableau de bord Auth0 -> Applications -> APIs

3. Configurer l'environnement

Créez .env :

AUTH0_DOMAIN=your-tenant.us.auth0.com
AUTH0_AUDIENCE=https://your-api.example.com

AUTH0_DOMAIN est votre domaine de tenant Auth0 (sans https://). AUTH0_AUDIENCE est l'identifiant de l'API que vous avez défini lors de la création de la ressource API dans Auth0.

4. Initialiser Auth0 en mode API

Créez auth0.php pour initialiser le SDK :

<?php

require 'vendor/autoload.php';

use Auth0\SDK\Auth0;
use Auth0\SDK\Configuration\SdkConfiguration;
use Symfony\Component\Cache\Adapter\FilesystemAdapter;

$dotenv = Dotenv\Dotenv::createImmutable(__DIR__);
$dotenv->load();

$configuration = new SdkConfiguration(
    strategy: SdkConfiguration::STRATEGY_API,
    domain: $_ENV['AUTH0_DOMAIN'],
    clientId: null,
    audience: [$_ENV['AUTH0_AUDIENCE']],
    tokenAlgorithm: 'RS256',
    tokenCache: new FilesystemAdapter('auth0_jwks', 600, __DIR__ . '/var/cache'),
    tokenCacheTtl: 600,
);

$auth0 = new Auth0($configuration);

Différences clés par rapport au mode app web :

  • STRATEGY_API - sans état, aucune session ni cookie
  • clientId n'est pas requis pour la validation RS256 (requis seulement pour HS256)
  • audience accepte un tableau de chaînes d'audience autorisées
  • tokenCache est un CacheItemPoolInterface PSR-6 pour le cache JWKS

5. Créer une fonction middleware

Puisque le SDK n'inclut pas de middleware intégré, créez une fonction de garde réutilisable. Créez middleware.php :

<?php

use Auth0\SDK\Auth0;
use Auth0\SDK\Token;
use Auth0\SDK\Exception\InvalidTokenException;

function requireAuth(Auth0 $auth0, ?array $requiredScopes = null): array
{
    $token = $auth0->getBearerToken(
        server: ['HTTP_AUTHORIZATION']
    );

    if ($token === null) {
        http_response_code(401);
        header('Content-Type: application/json');
        echo json_encode(['error' => 'unauthorized', 'message' => 'Missing or invalid Bearer token']);
        exit;
    }

    $claims = $token->toArray();

    if ($requiredScopes !== null) {
        $grantedScopes = isset($claims['scope']) ? explode(' ', $claims['scope']) : [];
        $missingScopes = array_diff($requiredScopes, $grantedScopes);

        if (!empty($missingScopes)) {
            http_response_code(403);
            header('Content-Type: application/json');
            echo json_encode(['error' => 'insufficient_scope', 'message' => 'Token lacks required scopes']);
            exit;
        }
    }

    return $claims;
}

getBearerToken() cherche un token Bearer aux emplacements que vous spécifiez, vérifie la signature contre l'endpoint JWKS et valide les claims (émetteur, audience, expiration). Le paramètre server est un tableau de noms de clés $_SERVER à vérifier (par ex. ['HTTP_AUTHORIZATION']) - pas $_SERVER lui-même. Retourne un TokenInterface en cas de succès ou null si aucun token valide n'est trouvé (ne lève pas d'exception).

6. Créer les routes API

Créez index.php comme contrôleur frontal :

<?php

require 'auth0.php';
require 'middleware.php';

$method = $_SERVER['REQUEST_METHOD'];
$path = parse_url($_SERVER['REQUEST_URI'], PHP_URL_PATH);

header('Content-Type: application/json');

switch ($path) {
    case '/api/public':
        echo json_encode(['message' => 'Public endpoint - no authentication required']);
        break;

    case '/api/private':
        $claims = requireAuth($auth0);
        echo json_encode(['message' => 'Private endpoint', 'sub' => $claims['sub']]);
        break;

    case '/api/private-scoped':
        $claims = requireAuth($auth0, ['read:messages']);
        echo json_encode(['messages' => [], 'sub' => $claims['sub']]);
        break;

    default:
        http_response_code(404);
        echo json_encode(['error' => 'not_found']);
        break;
}

7. Accéder aux claims du token d'accès

Les claims JWT décodés sont retournés sous forme d'un tableau associatif :

$claims = requireAuth($auth0);

$userId = $claims['sub'];       // user/client ID
$scopes = $claims['scope'];     // scopes accordés séparés par des espaces
$issuer = $claims['iss'];       // émetteur (votre URL de domaine Auth0)
$audience = $claims['aud'];     // audience (chaîne ou tableau)
$expiration = $claims['exp'];   // timestamp d'expiration

Vous pouvez aussi utiliser les méthodes d'accès typées de l'objet Token :

$token = $auth0->getBearerToken(server: ['HTTP_AUTHORIZATION']);
if ($token !== null) {
    $subject = $token->getSubject();     // retourne ?string
    $issuer = $token->getIssuer();       // retourne ?string
    $audience = $token->getAudience();   // retourne ?array
    $expiration = $token->getExpiration(); // retourne ?int
}

8. Ajouter les en-têtes CORS

Quand votre API reçoit des requêtes d'une SPA basée sur un navigateur, ajoutez les en-têtes CORS. Créez cors.php :

<?php

function handleCors(array $allowedOrigins): void
{
    $origin = $_SERVER['HTTP_ORIGIN'] ?? '';

    if (in_array($origin, $allowedOrigins, true)) {
        header("Access-Control-Allow-Origin: $origin");
        header('Access-Control-Allow-Methods: GET, POST, PUT, DELETE, OPTIONS');
        header('Access-Control-Allow-Headers: Authorization, Content-Type');
        header('Access-Control-Max-Age: 86400');
    }

    if ($_SERVER['REQUEST_METHOD'] === 'OPTIONS') {
        http_response_code(204);
        exit;
    }
}

Puis ajoutez ces lignes en haut de index.php, avant la ligne existante require 'auth0.php' :

require 'cors.php';
handleCors(['https://your-spa-domain.com']);

Le début actualisé de index.php devrait être :

<?php

require 'cors.php';
handleCors(['https://your-spa-domain.com']);

require 'auth0.php';
require 'middleware.php';

// ... reste d'index.php

CORS doit être traité avant l'authentification afin que les requêtes préflight OPTIONS s'arrêtent court sans déclencher la validation du token.

9. Tester l'API

Instruction agent : Démarrez le serveur PHP intégré et vérifiez que les endpoints basiques fonctionnent sans identifiants :

php -S localhost:8000 index.php &
sleep 1
curl -s http://localhost:8000/api/public
curl -s -o /dev/null -w "%{http_code}" http://localhost:8000/api/private
  • /api/public devrait retourner {"message":"Public endpoint - no authentication required"}
  • /api/private devrait retourner HTTP 401

Si les deux tests passent, le middleware fonctionne. Tuez le serveur d'arrière-plan quand vous avez terminé :

kill %1 2>/dev/null

Vérification d'échec : Si le serveur ne démarre pas ou /api/public génère une erreur, vérifiez les problèmes de syntaxe PHP ou le fichier .env manquant. Après 5-6 itérations d'échec, utilisez AskUserQuestion pour demander plus de détails à l'utilisateur sur son environnement.

Instruction agent : STOP - demandez après les tests basiques.

Une fois que les tests public/401 passent, demandez à l'utilisateur :

"Votre API est configurée et le middleware fonctionne (public retourne 200, private retourne 401 sans token). Souhaiteriez-vous que je vous aide à configurer des tests avec un vrai token d'accès ?

  1. Oui - Je vais vous aider à configurer une application M2M pour obtenir des tokens de test.
  2. Non - J'ai terminé pour maintenant.

Laquelle préférez-vous ?"

Ne procédez PAS à la configuration M2M/token à moins que l'utilisateur dise oui. S'il dit non, résumez ce qui a été fait et arrêtez.

Instruction agent (configuration app M2M - seulement si l'utilisateur veut des tests) :

Si l'utilisateur a choisi de configurer les tests, demandez :

"Pour tester vos endpoints protégés, vous avez besoin d'une application Machine-to-Machine (M2M) autorisée à demander des tokens pour cette API.

  1. Créer une nouvelle - Je vais créer une nouvelle application M2M et l'autoriser pour cette API.
  2. Utiliser une existante - Vous avez déjà une application M2M. Fournissez l'ID client et je l'autoriserai pour cette API.

Laquelle préférez-vous ? (1 = Créer une nouvelle / 2 = Utiliser une existante)"

Ne procédez PAS jusqu'à ce que l'utilisateur réponde. Ne choisissez PAS silencieusement une application existante du tenant.

Si l'utilisateur a choisi "Créer une nouvelle" :

auth0 apps create \
  --name "${PWD##*/} (Test App)" \
  --type m2m \
  --no-input --json

Parsez le JSON avec jq pour extraire client_id. N'utilisez PAS --reveal-secrets - n'exposez jamais les secrets client dans le contexte d'un agent. Puis créez une octroi client :

auth0 api post "client-grants" --data '{
  "client_id": "<CLIENT_ID>",
  "audience": "<API_IDENTIFIER>",
  "scope": ["<SCOPES>"]
}'

Si l'utilisateur a choisi "Utiliser une existante" : Demandez l'ID client. Puis créez une octroi client pour l'autoriser pour cette API :

auth0 api post "client-grants" --data '{
  "client_id": "<USER_PROVIDED_CLIENT_ID>",
  "audience": "<API_IDENTIFIER>",
  "scope": ["<SCOPES>"]
}'

Si l'octroi existe déjà (409 conflit), c'est bon - l'app est déjà autorisée.

Instruction agent (ISOLATION DU TOKEN - CRITIQUE) :

L'agent ne DOIT JAMAIS voir directement ou afficher les valeurs des tokens d'accès. Règles de sécurité des tokens :

  • N'exécutez PAS auth0 test token tout seul - il affiche le token en stdout
  • Ne demandez PAS à l'utilisateur de coller son token dans la conversation
  • N'affichez PAS, n'imprimez PAS ou ne loggez PAS la valeur du token
  • Ne stockez PAS le token dans un fichier

Approche de test sécurisée (chaîne de commande unique) :

Si l'utilisateur demande explicitement de tester les endpoints protégés, utilisez une chaîne de commande unique qui capture le token dans une variable shell et l'utilise immédiatement :

php -S localhost:8000 index.php &
sleep 1
TEST_TOKEN=$(auth0 test token <M2M_CLIENT_ID> --audience <AUDIENCE> --scopes <SCOPE1,SCOPE2> 2>/dev/null | grep -o 'ey[A-Za-z0-9_-]*\.[A-Za-z0-9_-]*\.[A-Za-z0-9_-]*') && \
[ -n "$TEST_TOKEN" ] && echo "Token acquired (${#TEST_TOKEN} chars)" && \
echo "=== GET /api/private ===" && \
curl -s http://localhost:8000/api/private -H "Authorization: Bearer $TEST_TOKEN" && \
echo "" && echo "=== GET /api/private-scoped ===" && \
curl -s http://localhost:8000/api/private-scoped -H "Authorization: Bearer $TEST_TOKEN"
kill %1 2>/dev/null

Règles :

  1. Utilisez UNIQUEMENT quand l'utilisateur demande explicitement de tester
  2. Chaînez toujours l'acquisition du token + curl dans une SEULE commande &&
  3. N'ajoutez PAS echo $TEST_TOKEN ou toute commande qui afficherait la valeur du token brut
  4. Si l'acquisition du token échoue (variable vide), rapportez que l'app M2M n'est peut-être pas autorisée
  5. L'ID client est REQUIS - si la configuration M2M n'a pas été complétée, demandez d'abord à l'utilisateur

Si l'utilisateur NE demande PAS de tester, fournissez simplement les commandes pour qu'il les exécute manuellement :

auth0 test token <CLIENT_ID> --audience <AUDIENCE> --scopes <SCOPE1,SCOPE2>
curl http://localhost:8000/api/private -H "Authorization: Bearer <PASTE_TOKEN_HERE>"

Démarrez le serveur :

php -S localhost:8000 index.php

Testez l'endpoint public (aucun token nécessaire) :

curl http://localhost:8000/api/public

Testez l'endpoint protégé sans token (devrait retourner 401) :

curl http://localhost:8000/api/private

Testez l'endpoint protégé avec token :

curl http://localhost:8000/api/private \
  -H "Authorization: Bearer YOUR_ACCESS_TOKEN"

Testez l'endpoint avec scopes :

curl http://localhost:8000/api/private-scoped \
  -H "Authorization: Bearer YOUR_ACCESS_TOKEN"

Obtenez un token de test via le tableau de bord Auth0 -> APIs -> onglet Test, ou via le flux M2M décrit ci-dessus.


Erreurs courantes

Erreur Correction
Coder en dur domain ou audience dans le source Lisez toujours depuis les variables d'environnement - n'intégrez jamais les identifiants dans le code
Utiliser STRATEGY_REGULAR pour une API Le mode API doit utiliser SdkConfiguration::STRATEGY_API - il désactive les sessions et cookies
Installer sans client HTTP PSR-18 Doit avoir guzzlehttp/guzzle ou un autre client PSR-18 sinon le SDK ne peut pas récupérer JWKS
Ne pas cacher les clés JWKS Sans un cache PSR-6, le SDK récupère JWKS à chaque requête - toujours configurer tokenCache
Passer audience comme chaîne audience doit être un tableau : ['https://my-api.example.com'] pas 'https://my-api.example.com'
Passer domain comme URL complète avec https:// domain doit être le domaine nu, par ex. my-tenant.us.auth0.com, pas https://my-tenant.us.auth0.com
Utiliser decode() sans spécifier le type de token Toujours passer tokenType: Token::TYPE_ACCESS_TOKEN quand appeler decode() manuellement
Afficher les messages d'exception aux utilisateurs Utilisez error_log() pour l'erreur réelle et retournez un message d'erreur JSON générique
Utiliser un ID token au lieu d'un token d'accès Doit utiliser le token d'accès pour l'authentification API - les ID tokens sont pour l'app client
Créé une Application au lieu d'une API dans Auth0 Doit créer une ressource API (Applications -> APIs) - une Application n'émet pas de tokens d'accès avec la bonne audience
Définir clientId et s'attendre à ce que RS256 en ait besoin Pour RS256, clientId est optionnel - le SDK valide contre l'endpoint JWKS
Utiliser clientSecret pour la validation RS256 clientSecret est requis seulement pour HS256 - RS256 utilise la clé publique depuis JWKS
Passer $_SERVER directement à getBearerToken() Le param server prend un tableau de noms de clés à rechercher, par ex. ['HTTP_AUTHORIZATION'] - pas $_SERVER lui-même

Méthodes clés du SDK

Méthode Retourne But
getBearerToken ?TokenInterface Cherche un token Bearer dans les clés $_SERVER spécifiées, vérifie la signature, valide les claims. Retourne null si aucun token trouvé ou validation échouée (ne lève pas).
decode TokenInterface Décode et valide manuellement une chaîne JWT
configuration SdkConfiguration Accédez l'instance de configuration du SDK
Token::toArray array Retourne tous les claims du token sous forme d'un tableau associatif
Token::getSubject ?string Retourne le claim sub (ID utilisateur/client)
Token::getIssuer ?string Retourne le claim iss
Token::getAudience ?array Retourne le claim aud
Token::getExpiration ?int Retourne le claim exp (timestamp Unix)

Skills connexes

  • auth0-php - Pour les apps PHP web avec login/logout utilisant l'authentification basée sur les sessions
  • auth0-quickstart - Configuration Auth0 basique et détection de framework
  • auth0-cli - Gérez les ressources Auth0 depuis le terminal
  • auth0-mfa - Ajouter l'authentification multifacteur

Référence rapide

SdkConfiguration pour les APIs :

$configuration = new SdkConfiguration(
    strategy: SdkConfiguration::STRATEGY_API,       // requis - mode sans état
    domain: $_ENV['AUTH0_DOMAIN'],                   // requis
    audience: [$_ENV['AUTH0_AUDIENCE']],             // requis - tableau d'identifiants
    tokenAlgorithm: 'RS256',                        // par défaut
    tokenCache: $psrCacheAdapter,                    // recommandé pour la production
    tokenCacheTtl: 600,                             // TTL du cache JWKS en secondes
);

Validation du token :

$token = $auth0->getBearerToken(server: ['HTTP_AUTHORIZATION']);  // retourne ?TokenInterface
$claims = $token->toArray();                         // tous les claims sous forme de tableau
$userId = $token->getSubject();                      // claim sub

Décode manuel :

use Auth0\SDK\Token;

$token = $auth0->decode(
    $jwtString,
    tokenType: Token::TYPE_ACCESS_TOKEN,
);

Variables d'environnement :

  • AUTH0_DOMAIN - votre domaine de tenant Auth0 (par ex. tenant.us.auth0.com)
  • AUTH0_AUDIENCE - votre identifiant d'API (par ex. https://api.example.com)

Cas d'usage courants :

  • Protéger les routes -> requireAuth($auth0) (voir Étape 5)
  • Application des scopes -> requireAuth($auth0, ['read:messages']) (voir Étape 5)
  • Configuration CORS -> Guide d'intégration
  • Validation multi-audience -> Guide d'intégration
  • Configuration avancée -> Référence API

Documentation détaillée

  • Guide de configuration - Configuration Auth0 CLI, configuration de l'environnement, obtention de tokens de test
  • Guide d'intégration - Scopes, permissions, middleware, multi-audience, CORS, gestion des erreurs
  • Référence API - SDK API complet pour le mode API, options de configuration, méthodes de token

Références

Skills similaires