mcpField
A
auth0-php

auth0-php

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

auth0-php について

auth0/auth0-php を使い、PHP Web アプリに Auth0 のログイン、ログアウト、プロフィール表示を追加するためのスキルです。設定、ルート、暗号化 Cookie セッション、実装時の一般的な問題を扱います。

主な機能

  1. 01Auth0 SDK の導入と設定
  2. 02ログインおよびコールバックルートの実装
  3. 03セッション確認によるプロフィールルートの保護
  4. 04暗号化 Cookie セッションの管理
  5. 05ログアウトとトークン更新への対応

活用シーン

  1. 01PHP Web アプリへの Auth0 ログイン追加
  2. 02認可コード交換用のコールバックルート構築
  3. 03ログインユーザーに限定したプロフィールページ
  4. 04PHP の 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アプリケーションにログイン、ログアウト、ユーザープロフィール機能を追加します。


前提条件

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

使用しないケース

  • JWT Bearer検証を行うPHP API - ステートレスなAPIトークン検証にはauth0-php-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はセッションクッキーの暗号化に使用します。openssl rand -hex 32で生成してください。

3. Auth0 Dashboardを設定する

Auth0 Applicationの設定で、次のようにします。

  • Application Type:Regular Web Application
  • Allowed Callback URLs:http://localhost:3000/callback
  • Allowed Logout URLs:http://localhost:3000
4. Authの設定を作成する

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

<?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インスタンスを1つ作成し、再利用してください。認証情報をハードコードせず、必ず環境変数を使用してください。

仕組み: SDKは、cookieSecretからHKDF-SHA256を介して導出したキーを使い、AES-256-GCMでセッションデータ(トークン、ユーザープロフィール)を暗号化します。デフォルトでは、セッションデータは暗号化されたクッキーに保存されるため、サーバー側のデータベースは不要です。

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文字列を返します。ユーザーをその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()はコードをトークンと交換し、セッションを確立します。トークン交換は失敗する可能性があるため(コードの期限切れ、CSRFの不一致など)、必ずtry/catchで囲んでください。

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 リクエストを送信できません
ウェブアプリで STRATEGY_API を使用するウェブアプリでは、セッションベースの認証に SdkConfiguration::STRATEGY_REGULAR を使用する必要があります
domain に https:// を含む完全な URL を渡すdomain には、https://my-tenant.us.auth0.com ではなく、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): stringAuth0 のログアウト URL 文字列を返します
renew$auth0->renew(?array $params): self期限切れのアクセストークンを更新します(offline_access スコープが必要です)
clear$auth0->clear(bool $transient = true): selfAuth0 からログアウトせずにローカルセッションをクリアします

認証情報オブジェクト

認証に成功すると、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 - 多要素認証を追加します

クイックリファレンス

ウェブアプリ用の 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)

詳細ドキュメント


参考資料

コメント