关于 auth0-php
帮助开发者使用 auth0/auth0-php 为 PHP Web 应用添加 Auth0 登录、登出和个人资料访问。内容涵盖配置、路由、加密 Cookie 会话及常见实现错误。
主要功能
- 01安装并配置 Auth0 SDK
- 02实现登录和回调路由
- 03通过会话检查保护个人资料路由
- 04管理加密 Cookie 会话
- 05处理登出和令牌续期
适用场景
- 01为 PHP Web 应用添加 Auth0 登录
- 02构建授权码交换回调路由
- 03限制仅登录用户访问个人资料页
- 04排查 PHP Auth0 配置问题
安装
复制下面的命令,在终端中运行。
npx skills add https:/ / github.com/ auth0/ agent-skills --skill auth0-php安装后在 Claude Code 中输入/auth0-php确认已加载。
包含的文件
除 SKILL.md 外,安装时一起下载的参考资料和脚本。
references/api.mdintegration.mdsetup.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-quickstartskill
不适用场景
- 使用 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 SDKvlucas/phpdotenv- 将.env文件加载到$_ENVguzzlehttp/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">← 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_API | Web 应用必须使用 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 |
直接使用 $_SESSION | SDK 管理自己的加密 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、pictureemail、email_verifiedgiven_name、family_nameupdated_at、locale
相关技能
auth0-php-api- 使用 JWT Bearer 令牌验证来保护 PHP APIauth0-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- 你的应用客户端 IDAUTH0_CLIENT_SECRET- 你的应用客户端密钥AUTH0_COOKIE_SECRET- 加密密钥(生成命令:openssl rand -hex 32)AUTH0_REDIRECT_URI- 回调 URL(例如http://localhost:3000/callback)
详细文档
- 设置指南 - 自动化设置脚本、环境配置、Auth0 CLI 用法
- 集成指南 - 受保护的路由、调用 API、会话管理、错误处理
- API 参考 - 完整的 Auth0 SDK API、配置选项、会话存储、安全性