mcpField
A
auth0-php

auth0-php

github.com/auth0安全55254 次安装1 天前更新
代码仓库

关于 auth0-php

帮助开发者使用 auth0/auth0-php 为 PHP Web 应用添加 Auth0 登录、登出和个人资料访问。内容涵盖配置、路由、加密 Cookie 会话及常见实现错误。

主要功能

  1. 01安装并配置 Auth0 SDK
  2. 02实现登录和回调路由
  3. 03通过会话检查保护个人资料路由
  4. 04管理加密 Cookie 会话
  5. 05处理登出和令牌续期

适用场景

  1. 01为 PHP Web 应用添加 Auth0 登录
  2. 02构建授权码交换回调路由
  3. 03限制仅登录用户访问个人资料页
  4. 04排查 PHP Auth0 配置问题

安装

复制下面的命令,在终端中运行。

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

安装后在 Claude Code 中输入/auth0-php确认已加载。

包含的文件

除 SKILL.md 外,安装时一起下载的参考资料和脚本。

references/
api.md
integration.md
setup.md

详细介绍

Auth0 PHP Web App Integration

使用 auth0/auth0-php 为 PHP Web 应用添加登录、注销和用户资料功能。


前置条件

  • PHP 8.2+,并安装以下扩展:mbstring、openssl、json
  • 已安装 Composer
  • 已配置 Auth0 Regular Web Application(不是 API,必须是 Application)
  • 如果尚未设置 Auth0,请先使用 auth0-quickstart skill

不适用场景

  • 使用 JWT Bearer 验证的 PHP API——使用 auth0-php-api 进行无状态 API 令牌验证
  • Laravel 应用——使用专用的 Laravel 集成 auth0/laravel-auth0
  • Symfony 应用——使用专用的 Symfony 集成 auth0/symfony
  • 单页应用——使用 auth0-react、auth0-vue 或 auth0-angular 进行客户端身份验证
  • Next.js 应用——使用 auth0-nextjs,它同时处理客户端和服务器端
  • Node.js Web 应用——使用 auth0-express 或 auth0-fastify 进行基于会话的身份验证

快速入门工作流

1. 安装 SDK
composer require auth0/auth0-php vlucas/phpdotenv guzzlehttp/guzzle guzzlehttp/psr7
  • auth0/auth0-php - Auth0 SDK
  • vlucas/phpdotenv - 将 .env 文件加载到 $_ENV
  • guzzlehttp/guzzle + guzzlehttp/psr7 - SDK 所需的 PSR-18 HTTP 客户端
2. 配置环境

创建 .env:

AUTH0_DOMAIN=your-tenant.us.auth0.com
AUTH0_CLIENT_ID=your_client_id
AUTH0_CLIENT_SECRET=your_client_secret
AUTH0_COOKIE_SECRET=your_generated_secret
AUTH0_REDIRECT_URI=http://localhost:3000/callback

AUTH0_DOMAIN 是你的 Auth0 租户域名(不含 https://)。AUTH0_CLIENT_ID 和 AUTH0_CLIENT_SECRET 来自 Auth0 Application 设置。AUTH0_COOKIE_SECRET 用于加密会话 Cookie——使用 openssl rand -hex 32 生成。

3. 配置 Auth0 控制面板

在 Auth0 Application 设置中:

  • Application Type:Regular Web Application
  • Allowed Callback URLs:http://localhost:3000/callback
  • Allowed Logout URLs:http://localhost:3000
4. 创建身份验证配置

创建 auth0.php 以初始化 SDK:

<?php

require 'vendor/autoload.php';

use Auth0\SDK\Auth0;
use Auth0\SDK\Configuration\SdkConfiguration;

// Load environment variables
$dotenv = Dotenv\Dotenv::createImmutable(__DIR__);
$dotenv->load();

$configuration = new SdkConfiguration(
    strategy: SdkConfiguration::STRATEGY_REGULAR,
    domain: $_ENV['AUTH0_DOMAIN'],
    clientId: $_ENV['AUTH0_CLIENT_ID'],
    clientSecret: $_ENV['AUTH0_CLIENT_SECRET'],
    cookieSecret: $_ENV['AUTH0_COOKIE_SECRET'],
    redirectUri: $_ENV['AUTH0_REDIRECT_URI'],
    scope: ['openid', 'profile', 'email'],
);

$auth0 = new Auth0($configuration);

创建一个 Auth0 实例并重复使用。切勿硬编码凭据——务必使用环境变量。

工作原理: SDK 使用通过 HKDF-SHA256 从 cookieSecret 派生的密钥,以 AES-256-GCM 加密会话数据(令牌、用户资料)。默认情况下,会话数据存储在加密 Cookie 中——无需服务器端数据库。

5. 创建首页(路由器)

创建 index.php 作为简单的前端控制器。请先创建 routes/ 目录:

<?php

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

if ($path === '/style.css') {
    header('Content-Type: text/css');
    readfile(__DIR__ . '/style.css');
    exit;
}

require 'auth0.php';

switch ($path) {
    case '/':
        require 'routes/home.php';
        break;
    case '/login':
        require 'routes/login.php';
        break;
    case '/callback':
        require 'routes/callback.php';
        break;
    case '/profile':
        require 'routes/profile.php';
        break;
    case '/logout':
        require 'routes/logout.php';
        break;
    default:
        http_response_code(404);
        echo 'Not found';
        break;
}

/style.css 的静态文件处理程序位于 require 'auth0.php' 之前,因此加载样式表时不会初始化 SDK。

6. 添加样式表

创建 style.css:

* {
    margin: 0;
    padding: 0;
    box-sizing: border-box;
}

body {
    font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, sans-serif;
    background: #f5f7fa;
    color: #1a1a2e;
    line-height: 1.6;
    min-height: 100vh;
}

.container {
    max-width: 800px;
    margin: 0 auto;
    padding: 40px 20px;
}

.card {
    background: #fff;
    border-radius: 12px;
    padding: 28px;
    margin-bottom: 20px;
    box-shadow: 0 2px 8px rgba(0, 0, 0, 0.06);
    border: 1px solid #e8ecf0;
}

.card.center {
    text-align: center;
    padding: 60px 28px;
}

h1 {
    font-size: 1.5rem;
    font-weight: 600;
    margin-bottom: 4px;
}

h2 {
    font-size: 1.1rem;
    font-weight: 600;
    margin-bottom: 16px;
    color: #444;
}

.subtitle {
    color: #666;
    font-size: 0.95rem;
}

.card.center .subtitle {
    margin: 12px 0 28px;
}

.user-header {
    display: flex;
    align-items: center;
    gap: 16px;
}

.avatar {
    width: 48px;
    height: 48px;
    border-radius: 50%;
    object-fit: cover;
}

.avatar-lg {
    width: 72px;
    height: 72px;
}

.nav-links {
    margin-top: 20px;
    display: flex;
    gap: 12px;
}

.top-nav {
    display: flex;
    justify-content: space-between;
    align-items: center;
    margin-bottom: 20px;
}

.btn {
    display: inline-block;
    padding: 10px 20px;
    border-radius: 8px;
    text-decoration: none;
    font-size: 0.9rem;
    font-weight: 500;
    transition: all 0.15s ease;
}

.btn-primary {
    background: #635bff;
    color: #fff;
}

.btn-primary:hover {
    background: #4b44d4;
}

.btn-secondary {
    background: #f0f0f5;
    color: #444;
}

.btn-secondary:hover {
    background: #e4e4ec;
}

.btn-back {
    background: none;
    color: #635bff;
    padding: 10px 0;
}

.btn-back:hover {
    color: #4b44d4;
}

.info-table {
    width: 100%;
    border-collapse: collapse;
}

.info-table tr {
    border-bottom: 1px solid #f0f0f5;
}

.info-table tr:last-child {
    border-bottom: none;
}

.info-table td {
    padding: 10px 0;
    vertical-align: top;
}

.info-table .label {
    font-weight: 500;
    color: #666;
    width: 160px;
    font-size: 0.85rem;
    text-transform: uppercase;
    letter-spacing: 0.02em;
}

.info-table .value {
    color: #1a1a2e;
    word-break: break-all;
}

.token-box {
    background: #f8f9fb;
    border: 1px solid #e8ecf0;
    border-radius: 8px;
    padding: 14px;
    font-size: 0.8rem;
    font-family: 'SF Mono', Monaco, 'Cascadia Code', monospace;
    word-break: break-all;
    white-space: pre-wrap;
    max-height: 120px;
    overflow-y: auto;
    margin-bottom: 16px;
}
7. 添加主页路由

创建 routes/home.php:

<?php

$credentials = $auth0->getCredentials();
?>
<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>Auth0 PHP App</title>
    <link rel="stylesheet" href="/style.css">
</head>
<body>
    <div class="container">
        <?php if ($credentials): ?>
            <div class="card">
                <div class="user-header">
                    <img src="<?= htmlspecialchars($credentials->user['picture'] ?? '') ?>" alt="avatar" class="avatar" />
                    <div>
                        <h1>Hello, <?= htmlspecialchars($credentials->user['name'] ?? 'User') ?>!</h1>
                        <p class="subtitle"><?= htmlspecialchars($credentials->user['email'] ?? '') ?></p>
                    </div>
                </div>
                <nav class="nav-links">
                    <a href="/profile" class="btn btn-primary">View Profile & Tokens</a>
                    <a href="/logout" class="btn btn-secondary">Logout</a>
                </nav>
            </div>
        <?php else: ?>
            <div class="card center">
                <h1>Auth0 PHP Web App</h1>
                <p class="subtitle">Session-based authentication with Auth0 SDK</p>
                <a href="/login" class="btn btn-primary">Login</a>
            </div>
        <?php endif; ?>
    </div>
</body>
</html>
8. 添加登录路由

创建 routes/login.php:

<?php

header('Location: ' . $auth0->login());
exit;

login() 会返回一个指向 Auth0 Universal Login 页面的 URL 字符串。你必须将用户重定向到该页面。

9. 添加回调路由

创建 routes/callback.php:

<?php

if (null !== $auth0->getExchangeParameters()) {
    try {
        $auth0->exchange();
        header('Location: /');
        exit;
    } catch (\Exception $e) {
        error_log('Auth0 callback error: ' . $e->getMessage());
        http_response_code(400);
        echo "Authentication failed. Please try again.";
        exit;
    }
}

header('Location: /');
exit;

getExchangeParameters() 会检查回调是否包含授权码参数。exchange() 会用授权码换取令牌并建立会话。务必使用 try/catch 包裹,因为令牌交换可能失败(例如授权码过期、CSRF 不匹配)。

10. 添加资料路由(受保护)

创建 routes/profile.php:

<?php

$credentials = $auth0->getCredentials();

if (null === $credentials) {
    header('Location: /login');
    exit;
}

$user = $credentials->user;
?>
<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>Profile - Auth0 PHP App</title>
    <link rel="stylesheet" href="/style.css">
</head>
<body>
    <div class="container">
        <nav class="top-nav">
            <a href="/" class="btn btn-back">&larr; Back to Home</a>
            <a href="/logout" class="btn btn-secondary">Logout</a>
        </nav>

        <div class="card">
            <div class="user-header">
                <img src="<?= htmlspecialchars($user['picture'] ?? '') ?>" alt="avatar" class="avatar avatar-lg" />
                <div>
                    <h1><?= htmlspecialchars($user['name'] ?? 'User') ?></h1>
                    <p class="subtitle"><?= htmlspecialchars($user['email'] ?? '') ?></p>
                </div>
            </div>
        </div>

        <div class="card">
            <h2>User Profile Claims</h2>
            <table class="info-table">
                <?php foreach ($user as $key => $value): ?>
                <tr>
                    <td class="label"><?= htmlspecialchars($key) ?></td>
                    <td class="value"><?= htmlspecialchars(is_array($value) ? json_encode($value) : (string)$value) ?></td>
                </tr>
                <?php endforeach; ?>
            </table>
        </div>

        <div class="card">
            <h2>ID Token</h2>
            <pre class="token-box"><?= htmlspecialchars($credentials->idToken ?? 'N/A') ?></pre>
        </div>

        <div class="card">
            <h2>Access Token</h2>
            <pre class="token-box"><?= htmlspecialchars($credentials->accessToken ?? 'N/A') ?></pre>
            <table class="info-table">
                <tr>
                    <td class="label">Expires</td>
                    <td class="value"><?= $credentials->accessTokenExpiration ? date('Y-m-d H:i:s', $credentials->accessTokenExpiration) . ' (' . ($credentials->accessTokenExpired ? 'EXPIRED' : 'valid') . ')' : 'N/A' ?></td>
                </tr>
                <tr>
                    <td class="label">Scopes</td>
                    <td class="value"><?= htmlspecialchars(implode(', ', $credentials->accessTokenScope ?? [])) ?></td>
                </tr>
            </table>
        </div>

        <?php if ($credentials->refreshToken): ?>
        <div class="card">
            <h2>Refresh Token</h2>
            <pre class="token-box"><?= htmlspecialchars($credentials->refreshToken) ?></pre>
        </div>
        <?php endif; ?>
    </div>
</body>
</html>

getCredentials() 会返回用户的会话数据;如果用户尚未登录,则返回 null。资料页会显示所有用户声明和令牌,供开发期间验证。

11. 添加注销路由

创建 routes/logout.php:

<?php

header('Location: ' . $auth0->logout(returnUri: 'http://localhost:3000'));
exit;

logout() 会返回 Auth0 注销 URL。将用户重定向到该 URL。returnUri 是用户注销后 Auth0 将其发送到的位置——该地址必须列在 Allowed Logout URLs 中。在生产环境中,请替换为你的实际域名。

12. 测试应用
php -S localhost:3000 index.php

访问 http://localhost:3000/login 以启动登录流程。


常见错误

错误修复方法
在源代码中硬编码 domain、clientId 或 clientSecret始终从环境变量读取,切勿将凭据嵌入代码
使用旧版 auth0-PHP(版本低于 8.0)要求 PHP 8.2+ 和 SDK v8.x;旧版本的 API 不同
安装时未配备 PSR-18 HTTP 客户端必须配备 PSR-18 客户端(例如 guzzlehttp/guzzle),否则 SDK 无法发起 HTTP 请求
在 Web 应用中使用 STRATEGY_APIWeb 应用必须使用 SdkConfiguration::STRATEGY_REGULAR 进行基于会话的身份验证
将带 https:// 的完整 URL 作为 domain 传入domain 应为纯域名,例如 my-tenant.us.auth0.com,而不是 https://my-tenant.us.auth0.com
忘记设置 cookieSecret会话加密所必需;没有它,SDK 会抛出 ConfigurationException
调用 exchange() 前未检查 getExchangeParameters()不带参数调用 exchange() 会导致错误;务必先检查
未处理回调中的错误exchange() 可能失败,务必用 try/catch 包裹
在 Auth0 中将应用创建为 SPA 类型必须使用 Regular Web Application 类型进行服务端身份验证
未在 Auth0 Dashboard 中配置回调 URL必须将 http://localhost:3000/callback 添加到 Allowed Callback URLs
直接使用 $_SESSIONSDK 管理自己的加密 Cookie 会话;除非配置了自定义 SessionStore,否则不要使用 $_SESSION
部署时未设置 cookieSecure: true在生产环境中必须设为 true,否则 Cookie 会通过 HTTP 发送
调用 login() 或 logout() 后未重定向两者都返回 URL 字符串,而非响应;必须使用 header('Location: ...')
回调时出现“Network error resulted in unfulfilled request”通常表示 AUTH0_CLIENT_SECRET 错误,而非实际网络问题;请检查 .env 中的凭据

SDK 关键方法

方法签名用途
login$auth0->login(?string $redirectUrl, ?array $params): string返回授权 URL 字符串,将用户重定向到该 URL
exchange$auth0->exchange(?string $redirectUri, ?string $code, ?string $state): bool用授权码交换令牌,并建立会话
getCredentials$auth0->getCredentials(): ?object返回当前会话凭据;如果没有则返回 null
getExchangeParameters$auth0->getExchangeParameters(): ?object检查回调是否包含交换参数
logout$auth0->logout(?string $returnUri, ?array $params): string返回 Auth0 登出 URL 字符串
renew$auth0->renew(?array $params): self刷新已过期的访问令牌(需要 offline_access 范围)
clear$auth0->clear(bool $transient = true): self清除本地会话,但不执行 Auth0 登出

凭据对象

身份验证成功后,getCredentials() 会返回一个包含以下内容的对象:

$credentials = $auth0->getCredentials();

$credentials->user;                    // array - user profile claims
$credentials->idToken;                 // string - raw ID token
$credentials->accessToken;             // string - access token
$credentials->refreshToken;            // string|null - refresh token (requires offline_access)
$credentials->accessTokenExpiration;   // int - expiration timestamp
$credentials->accessTokenExpired;      // bool - whether token is expired
$credentials->accessTokenScope;        // array - granted scopes

用户资料声明($credentials->user):

  • sub - 唯一用户标识符
  • name、nickname、picture
  • email、email_verified
  • given_name、family_name
  • updated_at、locale

相关技能

  • auth0-php-api - 使用 JWT Bearer 令牌验证来保护 PHP API
  • auth0-quickstart - Auth0 基本设置和框架检测
  • auth0-cli - 在终端中管理 Auth0 资源
  • auth0-mfa - 添加多因素身份验证

快速参考

Web 应用的 SdkConfiguration:

$configuration = new SdkConfiguration(
    strategy: SdkConfiguration::STRATEGY_REGULAR,        // required
    domain: $_ENV['AUTH0_DOMAIN'],                        // required
    clientId: $_ENV['AUTH0_CLIENT_ID'],                   // required
    clientSecret: $_ENV['AUTH0_CLIENT_SECRET'],           // required
    cookieSecret: $_ENV['AUTH0_COOKIE_SECRET'],           // required
    redirectUri: $_ENV['AUTH0_REDIRECT_URI'],             // required
    scope: ['openid', 'profile', 'email'],               // recommended
);

路由保护模式:

$credentials = $auth0->getCredentials();
if (null === $credentials) {
    header('Location: /login');
    exit;
}

环境变量:

  • AUTH0_DOMAIN - 你的 Auth0 租户域名(例如 tenant.us.auth0.com)
  • AUTH0_CLIENT_ID - 你的应用客户端 ID
  • AUTH0_CLIENT_SECRET - 你的应用客户端密钥
  • AUTH0_COOKIE_SECRET - 加密密钥(生成命令:openssl rand -hex 32)
  • AUTH0_REDIRECT_URI - 回调 URL(例如 http://localhost:3000/callback)

详细文档

  • 设置指南 - 自动化设置脚本、环境配置、Auth0 CLI 用法
  • 集成指南 - 受保护的路由、调用 API、会话管理、错误处理
  • API 参考 - 完整的 Auth0 SDK API、配置选项、会话存储、安全性

参考资料

评论

支持 Markdown