mcpField
A
auth0-php-api

auth0-php-api

github.com/auth0セキュリティ55259 回インストール1 日前に更新
リポジトリ

auth0-php-api について

auth0-php-apiは、auth0/auth0-phpのAPIモードでAuth0アクセストークンを検証し、PHP APIエンドポイントを保護するためのスキルです。トークン検査、再利用可能なミドルウェア、スコープ、エンドポイントのテストを扱い、セッション型WebアプリではなくAPIを対象とします。

主な機能

  1. 01APIモードでのAuth0 SDK設定
  2. 02JWT署名とクレームの検証
  3. 03再利用可能なBearerトークンガードミドルウェア
  4. 04保護されたルートのスコープ確認
  5. 05公開・保護エンドポイントのテスト

活用シーン

  1. 01PHP APIルートへのAuth0トークン検証の追加
  2. 02許可されたスコープに基づくエンドポイント制限
  3. 03トークンを露出せずに公開・保護ルートをテスト

インストール

コマンドをコピーしてターミナルで実行します。

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

インストール後、Claude Code で/auth0-php-apiを入力して読み込まれたか確認します。

同梱ファイル

SKILL.md と一緒にインストールされる参考資料やスクリプト。

references/
api.md
integration.md
setup.md

詳細

Auth0 PHP API 連携

auth0/auth0-php の API モード(STRATEGY_API)で JWT アクセストークンを検証し、PHP API エンドポイントを保護します。


前提条件

  • 拡張機能 mbstring、openssl、json を含む PHP 8.2 以降
  • Composer がインストールされていること
  • Auth0 API リソースが設定されていること(Application ではなく、API である必要があります)
  • Auth0 の設定がまだ済んでいない場合は、まず auth0-quickstart スキルを使用してください

使用しないケース

  • ログイン/ログアウトフローを備えた PHP ウェブアプリケーション - セッションベースの認証には auth0-php を使用してください
  • Laravel アプリケーション - API ガードを標準でサポートする auth0/laravel-auth0 を使用してください
  • Symfony アプリケーション - セキュリティバンドルを備えた auth0/symfony を使用してください
  • シングルページアプリケーション - クライアント側の認証には auth0-react、auth0-vue、または auth0-angular を使用してください
  • トークンの発行 - このスキルはアクセストークンの検証用であり、発行用ではありません

クイックスタートのワークフロー

1. SDK をインストールする
composer require auth0/auth0-php vlucas/phpdotenv guzzlehttp/guzzle guzzlehttp/psr7 "symfony/cache:^7.0"
  • auth0/auth0-php - Auth0 SDK(v8.x)
  • vlucas/phpdotenv - .env ファイルを $_ENV に読み込みます
  • guzzlehttp/guzzle + guzzlehttp/psr7 - SDK が必要とする PSR-18 HTTP クライアント
  • symfony/cache - JWKS キーのキャッシュに使用する PSR-6 キャッシュ(本番環境で推奨)
2. Auth0 API を作成する

Auth0 に API を作成する必要があります(Application ではありません)。

停止して、先にユーザーに確認してください。

ほかの作業を行う前に、次の質問をそのまま尋ね、回答を待ってください。

"Auth0 API リソースをどのように作成しますか?

  1. 自動 - Auth0 CLI スクリプトを実行してリソースを作成し、正確な値を .env に自動で書き込みます。
  2. 手動 - Auth0 Dashboard(または auth0 apis create)でご自身で API を作成し、Domain と Audience を教えてください。

どちらにしますか?(1 = 自動/2 = 手動)"

ユーザーが回答するまで、セットアップ手順に進まないでください。手動をデフォルトにしないでください。

ユーザーが「自動」を選んだ場合は、CLI スクリプトの全手順が記載されたセットアップガイドに従ってください。自動の手順では .env が作成されるため、以下のステップ 3 を省略し、ステップ 4 に進んでください。

ユーザーが「手動」を選んだ場合は、詳細な手順が記載されたセットアップガイドの「手動セットアップ」セクションに従ってください。その後、以下のステップ 3 に進んでください。

手動で API を作成する場合のクイックリファレンス:

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

または、Auth0 Dashboard -> Applications -> APIs で手動作成します。

3. 環境を設定する

.env を作成します。

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

AUTH0_DOMAIN は Auth0 テナントのドメインです(https:// は含めません)。AUTH0_AUDIENCE は、Auth0 で API リソースを作成したときに設定した API 識別子です。

4. API モードで Auth0 を初期化する

SDK を初期化するために auth0.php を作成します。

<?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);

ウェブアプリモードとの主な違い:

  • STRATEGY_API - ステートレスで、セッションや Cookie を使用しません
  • RS256 の検証では clientId は不要です(HS256 の場合のみ必要です)
  • audience には、許可する Audience 文字列の配列を指定できます
  • tokenCache は、JWKS キャッシュ用の PSR-6 CacheItemPoolInterface です
5. ミドルウェア関数を作成する

SDK には組み込みのミドルウェアがないため、再利用可能なガード関数を作成します。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() は、指定した場所から Bearer トークンを検索し、JWKS エンドポイントに対して署名を検証して、クレーム(issuer、audience、有効期限)を検証します。server パラメーターは、確認対象となる $_SERVER のキー名の配列(例:['HTTP_AUTHORIZATION'])であり、$_SERVER 自体ではありません。有効なトークンが見つかった場合は TokenInterface を返し、見つからない場合は null を返します(例外はスローしません)。

6. API ルートを作成する

フロントコントローラーとして index.php を作成します。

<?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. アクセストークンのクレーム

デコードされた JWT クレームは連想配列として返されます。

$claims = requireAuth($auth0);

$userId = $claims['sub'];       // user/client ID
$scopes = $claims['scope'];     // space-separated granted scopes
$issuer = $claims['iss'];       // issuer (your Auth0 domain URL)
$audience = $claims['aud'];     // audience (string or array)
$expiration = $claims['exp'];   // expiration timestamp

Token オブジェクトの型付きアクセサーメソッドも使用できます。

$token = $auth0->getBearerToken(server: ['HTTP_AUTHORIZATION']);
if ($token !== null) {
    $subject = $token->getSubject();     // returns ?string
    $issuer = $token->getIssuer();       // returns ?string
    $audience = $token->getAudience();   // returns ?array
    $expiration = $token->getExpiration(); // returns ?int
}
8. CORS ヘッダーを追加する

ブラウザー上で動作する SPA から API にリクエストが届く場合は、CORS ヘッダーを追加します。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;
    }
}

次に、既存の require 'auth0.php' 行より前に、index.php の先頭に以下の行を追加します。

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

更新後の index.php の先頭部分は次のようになります。

<?php

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

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

// ... rest of index.php

プリフライトの OPTIONS リクエストがトークンの検証を行わずに処理を終了するよう、CORS は認証より先に処理する必要があります。

9. API をテストする

エージェントへの指示: PHP の組み込みサーバーを起動し、認証情報なしで基本的なエンドポイントが動作することを確認します。

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 は {"message":"Public endpoint - no authentication required"} を返すはずです。
  • /api/private は HTTP 401 を返すはずです。

どちらも成功した場合、ミドルウェアは動作しています。完了したら、バックグラウンドで起動したサーバーを終了します。

kill %1 2>/dev/null

失敗時の確認: サーバーの起動に失敗する場合や /api/public でエラーが発生する場合は、PHP の構文エラーまたは .env の不足を確認してください。5〜6 回試しても失敗する場合は、AskUserQuestion を使って、ユーザーの環境について詳しく尋ねてください。

エージェントへの指示:基本テストが成功したら停止して、ユーザーに確認してください。

public/401 のテストが成功したら、ユーザーに尋ねます。

「API のセットアップが完了し、ミドルウェアも動作しています(トークンなしの場合、public は 200 を返し、private は 401 を返します)。実際のアクセストークンを使ったテストのセットアップもお手伝いしましょうか?

  1. はい — テスト用トークンを取得するために、M2M アプリケーションの設定をお手伝いします。
  2. いいえ — 今回はこれで終了します。

どちらにしますか?」

ユーザーが「はい」と言わない限り、M2M/トークンのセットアップに進まないでください。「いいえ」と言われた場合は、完了した内容をまとめて終了してください。

エージェントへの指示(M2M アプリのセットアップ — テストを希望する場合のみ):

ユーザーがテストのセットアップを選択した場合は、次のように尋ねます。

「保護されたエンドポイントをテストするには、この API のトークンをリクエストする権限を持つ Machine-to-Machine(M2M)アプリケーションが必要です。

  1. 新規作成 — 新しい M2M アプリケーションを作成し、この API の利用を許可します。
  2. 既存のアプリケーションを使用 — すでに M2M アプリケーションがある場合は、Client ID を教えてください。この API の利用を許可します。

どちらにしますか?(1 = 新規作成/2 = 既存のアプリケーションを使用)」

ユーザーが回答するまで先に進まないでください。テナント内にある既存のアプリケーションを、確認せずに選択しないでください。

ユーザーが「新規作成」を選択した場合:

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

JSON を jq で解析して client_id を抽出します。--reveal-secrets は使用しないでください。エージェントのコンテキストにクライアントシークレットを決して公開しないでください。 次に、クライアントグラントを作成します。

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

ユーザーが「既存のアプリケーションを使用」を選択した場合: Client ID を尋ねます。次に、この API の利用を許可するクライアントグラントを作成します。

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

グラントがすでに存在する場合(409 conflict)は問題ありません。アプリケーションはすでに許可されています。 エージェントへの指示(トークンの隔離 — 重要):

エージェントは、アクセストークンの値を直接見たり、表示したりしてはなりません。トークンのセキュリティに関するルール:

  • auth0 test token を単独で実行しないでください。トークンが標準出力に出力されます
  • ユーザーに会話内でトークンを貼り付けるよう求めないでください
  • トークンの値をエコー、出力、ログに記録しないでください
  • トークンをファイルに保存しないでください

安全なテスト方法(単一コマンドチェーン):

ユーザーが保護されたエンドポイントのテストを明示的に依頼した場合は、トークンをシェル変数に取り込み、直ちに使用する単一コマンドチェーンを使ってください。

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

ルール:

  1. ユーザーから明示的にテストを依頼された場合にのみ使用してください
  2. トークンの取得と curl は、必ず単一の && コマンドに連結してください
  3. echo $TEST_TOKEN や、トークンの値をそのまま出力するコマンドは絶対に追加しないでください
  4. トークンの取得に失敗した場合(変数が空の場合)は、M2M アプリが認可されていない可能性があることを伝えてください
  5. クライアント ID は必須です。M2M の設定が完了していない場合は、まずユーザーに確認してください

ユーザーがテストを依頼していない場合は、手動で実行するコマンドを提示してください。

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

サーバーを起動します:

php -S localhost:8000 index.php

公開エンドポイントをテストします(トークンは不要です):

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

トークンなしで保護されたエンドポイントをテストします(401 が返るはずです):

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

トークンを使って保護されたエンドポイントをテストします:

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

スコープ付きエンドポイントをテストします:

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

Auth0 Dashboard -> APIs -> Test タブ、または上記の M2M フローでテストトークンを取得します。


よくある間違い

間違い修正方法
ソースコードで domain や audience をハードコードする常に環境変数から読み込みます。コードに認証情報を埋め込まないでください
API に STRATEGY_REGULAR を使用するAPI モードでは SdkConfiguration::STRATEGY_API を使用する必要があります。セッションと Cookie が無効になります
PSR-18 HTTP クライアントをインストールせずに使用するguzzlehttp/guzzle または別の PSR-18 クライアントが必要です。ない場合、SDK は JWKS を取得できません
JWKS キーをキャッシュしないPSR-6 キャッシュがない場合、SDK はリクエストごとに JWKS を取得します。必ず tokenCache を設定してください
audience に文字列を渡すaudience は配列である必要があります。'https://my-api.example.com' ではなく、['https://my-api.example.com'] を指定してください
domain に https:// を含む完全な URL を渡すdomain には https://my-tenant.us.auth0.com ではなく、my-tenant.us.auth0.com のようなドメインのみを指定してください
トークンの種類を指定せずに decode() を使用するdecode() を手動で呼び出す場合は、必ず tokenType: Token::TYPE_ACCESS_TOKEN を渡してください
例外メッセージをユーザーに表示する実際のエラーには error_log() を使用し、一般的な JSON エラーメッセージを返してください
アクセストークンではなく ID トークンを使用するAPI 認証には アクセストークン を使用する必要があります。ID トークンはクライアントアプリ用です
Auth0 で API ではなく Application を作成するAPI リソースを作成する必要があります(Applications -> APIs)。Application は正しい audience を持つアクセストークンを発行しません
clientId を設定し、RS256 で必要だと想定するRS256 では clientId は任意です。SDK は JWKS エンドポイントに対して検証します
RS256 の検証に clientSecret を使用するclientSecret が必要なのは HS256 のみです。RS256 では JWKS の公開鍵を使用します
$_SERVER をそのまま getBearerToken() に渡すserver パラメーターには、検索するキー名の配列(例:['HTTP_AUTHORIZATION'])を指定します。$_SERVER 自体ではありません

主要な SDK メソッド

メソッド戻り値用途
getBearerToken?TokenInterface指定された $_SERVER のキーから Bearer トークンを検索し、署名を検証してクレームを検証します。トークンが見つからない場合、または検証に失敗した場合は null を返します(例外はスローしません)。
decodeTokenInterfaceJWT 文字列を手動でデコードして検証します
configurationSdkConfigurationSDK 設定インスタンスにアクセスします
Token::toArrayarrayすべてのトークンクレームを連想配列として返します
Token::getSubject?stringsub クレーム(ユーザー/クライアント ID)を返します
Token::getIssuer?stringiss クレームを返します
Token::getAudience?arrayaud クレームを返します
Token::getExpiration?intexp クレーム(Unix タイムスタンプ)を返します

関連スキル

  • auth0-php - セッションベースの認証でログイン/ログアウトする PHP ウェブアプリ向け
  • auth0-quickstart - Auth0 の基本設定とフレームワークの検出
  • auth0-cli - ターミナルから Auth0 リソースを管理
  • auth0-mfa - 多要素認証を追加

クイックリファレンス

API 用の SdkConfiguration:

$configuration = new SdkConfiguration(
    strategy: SdkConfiguration::STRATEGY_API,       // required - stateless mode
    domain: $_ENV['AUTH0_DOMAIN'],                   // required
    audience: [$_ENV['AUTH0_AUDIENCE']],             // required - array of identifiers
    tokenAlgorithm: 'RS256',                        // default
    tokenCache: $psrCacheAdapter,                    // recommended for production
    tokenCacheTtl: 600,                             // JWKS cache TTL in seconds
);

トークンの検証:

$token = $auth0->getBearerToken(server: ['HTTP_AUTHORIZATION']);  // returns ?TokenInterface
$claims = $token->toArray();                         // all claims as array
$userId = $token->getSubject();                      // sub claim

手動デコード:

use Auth0\SDK\Token;

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

環境変数:

  • AUTH0_DOMAIN - Auth0 テナントのドメイン(例:tenant.us.auth0.com)
  • AUTH0_AUDIENCE - API 識別子(例:https://api.example.com)

一般的なユースケース:

  • ルートの保護 -> requireAuth($auth0)(ステップ 5 を参照)
  • スコープの適用 -> requireAuth($auth0, ['read:messages'])(ステップ 5 を参照)
  • CORS の設定 -> 統合ガイド
  • 複数 audience の検証 -> 統合ガイド
  • 高度な設定 -> API リファレンス

詳細ドキュメント

  • セットアップガイド - Auth0 CLI のセットアップ、環境設定、テストトークンの取得
  • 統合ガイド - スコープ、権限、ミドルウェア、複数のオーディエンス、CORS、エラー処理
  • API リファレンス - API モード向けの SDK API 全般、設定オプション、トークンメソッド

参考資料

コメント