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 端点。内容涵盖令牌检查、可复用中间件、权限范围及端点测试,适用于 API,而非基于会话的 Web 应用。

主要功能

  1. 01以 API 模式配置 Auth0 SDK
  2. 02验证 JWT 签名及声明
  3. 03可复用的 Bearer 令牌守卫中间件
  4. 04为受保护路由检查权限范围
  5. 05测试公开及受保护端点

适用场景

  1. 01为 PHP 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 集成

在 API 模式(STRATEGY_API)下使用 auth0/auth0-php 验证 JWT 访问令牌,保护 PHP API 端点。


前置条件

  • PHP 8.2+,并启用扩展:mbstring、openssl、json
  • 已安装 Composer
  • 已配置 Auth0 API 资源(必须是 API,而不是 Application)
  • 如果尚未设置 Auth0,请先使用 auth0-quickstart 技能

不适用场景

  • 带有登录/注销流程的 PHP Web 应用 - 使用 auth0-php 进行基于会话的身份验证
  • Laravel 应用 - 使用内置 API guard 支持的 auth0/laravel-auth0
  • Symfony 应用 - 使用带有安全 bundle 的 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 中自行创建 API(或通过 auth0 apis create 创建),并向我提供 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

创建 auth0.php 以初始化 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);

与 Web 应用模式的主要区别:

  • 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 端点验证签名,并验证声明(颁发者、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 标头

当 API 接收来自基于浏览器的 SPA 的请求时,请添加 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;
    }
}

然后在 index.php 顶部、现有的 require 'auth0.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

必须先处理 CORS,再处理身份验证,这样预检 OPTIONS 请求就会直接返回,而不会触发令牌验证。

9. 测试 API

Agent 指令: 启动 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 询问用户更多环境详情。

Agent 指令:停止——基本测试通过后询问用户。

公共端点和 401 测试通过后,询问用户:

“你的 API 已设置完成,中间件也正常工作(公共端点返回 200,私有端点在没有令牌时返回 401)。需要我帮你使用真实访问令牌进行测试吗?

  1. 需要——我会帮你配置 M2M 应用以获取测试令牌。
  2. 不需要——我暂时不需要帮助。

你更倾向于哪一种?”

除非用户回答需要,否则不要继续进行 M2M/令牌设置。如果用户回答不需要,请总结已完成的工作并停止。

Agent 指令(M2M 应用设置——仅在用户需要测试时执行):

如果用户选择设置测试,请询问:

“要测试受保护的端点,你需要一个获准为此 API 请求令牌的机器对机器(M2M)应用。

  1. 创建新应用——我会创建一个新的 M2M 应用,并授权它使用此 API。
  2. 使用现有应用——你已有一个 M2M 应用。请提供客户端 ID,我会授权它使用此 API。

你更倾向于哪一种?(1 = 创建新应用 / 2 = 使用现有应用)”

在用户回答前不要继续。不要擅自从租户中选择现有应用。

如果用户选择“创建新应用”:

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

使用 jq 解析 JSON 并提取 client_id。不要使用 --reveal-secrets——切勿在 Agent 上下文中暴露客户端密钥。 然后创建客户端授权:

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

如果用户选择“使用现有应用”: 询问客户端 ID。然后创建客户端授权,以授权该应用使用此 API:

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

如果授权已存在(409 冲突),则无需处理——该应用已获授权。 Agent 指令(令牌隔离——至关重要):

Agent 绝不能直接查看或显示访问令牌值。令牌安全规则:

  • 不要单独运行 auth0 test token——该命令会将令牌输出到标准输出
  • 不要要求用户将令牌粘贴到对话中
  • 不要回显、打印或记录令牌值
  • 不要将令牌存储在文件中

安全测试方法(单命令链):

如果用户明确要求测试受保护的端点,请使用单命令链,将令牌捕获到 shell 变量中并立即使用:

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_REGULARAPI 模式必须使用 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 应为不带协议的域名,例如 my-tenant.us.auth0.com,而不是 https://my-tenant.us.auth0.com
调用 decode() 时未指定令牌类型手动调用 decode() 时,始终传入 tokenType: Token::TYPE_ACCESS_TOKEN
将异常消息输出给用户使用 error_log() 记录实际错误,并返回通用 JSON 错误消息
使用 ID 令牌而不是访问令牌API 身份验证必须使用访问令牌;ID 令牌供客户端应用使用
在 Auth0 中创建了 Application,而不是 API必须创建 API 资源(Applications -> APIs);Application 不会签发具有正确受众的访问令牌
设置 clientId 并认为 RS256 需要它对于 RS256,clientId 是可选的,SDK 会通过 JWKS 端点进行验证
使用 clientSecret 验证 RS256clientSecret 仅适用于 HS256;RS256 使用 JWKS 中的公钥
将 $_SERVER 直接传给 getBearerToken()server 参数接收要查找的键名数组,例如 ['HTTP_AUTHORIZATION'],而不是 $_SERVER 本身

主要 SDK 方法

方法返回值用途
getBearerToken?TokenInterface在指定的 $_SERVER 键中查找 Bearer 令牌,验证签名并校验声明。如果未找到令牌或验证失败,则返回 null(不会抛出异常)。
decodeTokenInterface手动解码并验证 JWT 字符串
configurationSdkConfiguration访问 SDK 配置实例
Token::toArrayarray将所有令牌声明作为关联数组返回
Token::getSubject?string返回 sub 声明(用户/客户端 ID)
Token::getIssuer?string返回 iss 声明
Token::getAudience?array返回 aud 声明
Token::getExpiration?int返回 exp 声明(Unix 时间戳)

相关技能

  • auth0-php - 用于通过基于会话的身份验证实现登录/登出的 PHP Web 应用
  • 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 设置 -> 集成指南
  • 多受众验证 -> 集成指南
  • 高级配置 -> API 参考

详细文档

  • 设置指南 - Auth0 CLI 设置、环境配置、获取测试令牌
  • 集成指南 - 作用域、权限、中间件、多受众、CORS、错误处理
  • API 参考 - API 模式的完整 SDK API、配置选项、令牌方法

参考资料

评论

支持 Markdown