メインコンテンツへスキップ

AI を使って Auth0 を統合する

Claude Code、Cursor、GitHub Copilot などの AI コーディングアシスタントを使用している場合は、agent skills を使って、数分で Auth0 認証を自動的に追加できます。インストール:
npx skills add https://github.com/auth0/agent-skills --skill auth0-express
次に、AI アシスタントに次のように依頼します。
Add Auth0 authentication to my Express app
AI アシスタントが、Auth0 アプリケーションの作成、認証情報の取得、express-openid-connect のインストール、ミドルウェアの設定、ルートのセットアップを自動的に行います。agent skills の完全なドキュメント →
前提条件: 始める前に、次のものがインストールされていることを確認してください。
  • Node.js 18 LTS 以降
  • npm 10 以降、または yarn 1.22 以降
  • jq - Auth0 CLI のセットアップに必要です (省略可)
Express のバージョン互換性: このクイックスタートは Express 4.17.0 以降に対応しています。

はじめに

このガイドでは、express-openid-connect SDK を使用して、Express.js の Web アプリケーションに Auth0 を統合し、認証を追加して、ユーザープロファイル情報を表示する方法を紹介します。

1. 新しいプロジェクトを作成する

Express アプリケーション用の新しいディレクトリを作成し、Node.js プロジェクトを初期化します。
mkdir auth0-express && cd auth0-express
npm init -y
プロジェクトの構成を作成します。
touch index.js .env

2. Auth0 Express SDK をインストールする

環境変数を管理するために、Express および dotenv とあわせて express-openid-connect をインストールします。
npm install express express-openid-connect dotenv
開発時は、ファイルの変更に応じてサーバーを自動的に再起動するために、nodemon をインストールします。
npm install --save-dev nodemon
package.json を更新して、起動スクリプトを追加します。 📁 package.json
{
  "name": "auth0-express",
  "version": "1.0.0",
  "main": "index.js",
  "scripts": {
    "start": "node index.js",
    "dev": "nodemon index.js"
  },
  "dependencies": {
    "dotenv": "^16.3.1",
    "express": "^4.18.2",
    "express-openid-connect": "^2.17.1"
  },
  "devDependencies": {
    "nodemon": "^3.0.2"
  }
}

3. Auth0 App を設定する

次に、Auth0 テナントに新しいアプリケーションを作成し、環境変数をプロジェクトに追加します。 これは、CLI コマンドを実行して自動的に行うことも、Dashboard で手動で行うこともできます。
次のシェルコマンドをプロジェクトのルートディレクトリで実行し、Auth0 アプリケーションを作成して .env ファイルを生成します。macOS / Linux:
AUTH0_APP_NAME="My Express App" && \
auth0 apps create -n "${AUTH0_APP_NAME}" -t regular \
  --callbacks http://localhost:3000 \
  --logout-urls http://localhost:3000 \
  --json | jq -r '"ISSUER_BASE_URL=https://\(.domain)\nCLIENT_ID=\(.client_id)\nSECRET='$(openssl rand -hex 32)'\nBASE_URL=http://localhost:3000"' > .env
Windows (PowerShell):
$appName = "My Express App"
auth0 apps create -n $appName -t regular `
  --callbacks http://localhost:3000 `
  --logout-urls http://localhost:3000 `
  --json | ConvertFrom-Json | ForEach-Object {
    "ISSUER_BASE_URL=https://$($_.domain)`nCLIENT_ID=$($_.client_id)`nSECRET=$([guid]::NewGuid().ToString())`nBASE_URL=http://localhost:3000"
  } | Out-File .env -Encoding utf8
Auth0 CLI をまだインストールしていない場合は、次を実行します。
brew tap auth0/auth0-cli && brew install auth0
その後、auth0 login で認証します。

4. ミドルウェアを設定する

Auth0 ミドルウェアを Express アプリケーションに追加します。auth() ミドルウェアはセッションを管理し、/login/logout/callback ルートを自動的に作成します。 📁 index.js
require('dotenv').config();
const express = require('express');
const { auth } = require('express-openid-connect');

const app = express();
const port = process.env.PORT || 3000;

// Auth0 設定
const config = {
  authRequired: false,      // 公開ルートを許可する
  auth0Logout: true,        // Auth0 のログアウトエンドポイントを使用する
  secret: process.env.SECRET,
  baseURL: process.env.BASE_URL,
  clientID: process.env.CLIENT_ID,
  issuerBaseURL: process.env.ISSUER_BASE_URL,
};

// 認証ミドルウェアを適用する
app.use(auth(config));

// ホームルート - 公開
app.get('/', (req, res) => {
  res.send(req.oidc.isAuthenticated() ? 'Logged in' : 'Logged out');
});

app.listen(port, () => {
  console.log(`Server running at http://localhost:${port}`);
});
この設定で行われること:
  • authRequired: false を設定すると、認証の有無にかかわらず、デフォルトですべてのユーザーがルートにアクセスできます
  • auth0Logout: true を設定すると、ユーザーはアプリだけでなく Auth0 からもログアウトされます
  • ミドルウェアにより、/login/logout/callback のルートが自動的に自動的に追加されます
  • ユーザーセッションは暗号化された Cookie に保存されます

5. ログイン、ログアウト、ユーザープロファイル用のルートを作成する

次に、ログイン/ログアウトのリンクと、保護されたユーザープロファイルページを表示するルートを追加します。 📁 index.js
require('dotenv').config();
const express = require('express');
const { auth, requiresAuth } = require('express-openid-connect');

const app = express();
const port = process.env.PORT || 3000;

// Auth0 設定
const config = {
  authRequired: false,
  auth0Logout: true,
  secret: process.env.SECRET,
  baseURL: process.env.BASE_URL,
  clientID: process.env.CLIENT_ID,
  issuerBaseURL: process.env.ISSUER_BASE_URL,
};

// 認証ミドルウェアを適用
app.use(auth(config));

// ホームルート - ログイン/ログアウトの状態を表示
app.get('/', (req, res) => {
  const isAuthenticated = req.oidc.isAuthenticated();

  res.send(`
    <html>
      <head>
        <title>Auth0 Express Quickstart</title>
        <style>
          body { font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, sans-serif; padding: 2rem; max-width: 600px; margin: 0 auto; }
          a { color: #0066cc; text-decoration: none; margin-right: 1rem; }
          a:hover { text-decoration: underline; }
          .status { padding: 1rem; border-radius: 4px; margin: 1rem 0; }
          .logged-in { background: #d4edda; color: #155724; }
          .logged-out { background: #f8d7da; color: #721c24; }
        </style>
      </head>
      <body>
        <h1>Auth0 Express Quickstart</h1>
        <div class="status ${isAuthenticated ? 'logged-in' : 'logged-out'}">
          ${isAuthenticated ? '✓ You are logged in' : '✗ You are logged out'}
        </div>
        <nav>
          ${isAuthenticated
            ? '<a href="/profile">Profile</a> | <a href="/logout">Logout</a>'
            : '<a href="/login">Login</a>'}
        </nav>
      </body>
    </html>
  `);
});

// 保護されたプロファイルルート - 認証が必要
app.get('/profile', requiresAuth(), (req, res) => {
  const user = req.oidc.user;

  res.send(`
    <html>
      <head>
        <title>Profile - Auth0 Express</title>
        <style>
          body { font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, sans-serif; padding: 2rem; max-width: 600px; margin: 0 auto; }
          a { color: #0066cc; text-decoration: none; }
          img { border-radius: 50%; }
          pre { background: #f4f4f4; padding: 1rem; border-radius: 4px; overflow-x: auto; }
          .card { border: 1px solid #ddd; border-radius: 8px; padding: 1.5rem; margin: 1rem 0; }
        </style>
      </head>
      <body>
        <h1>User Profile</h1>
        <div class="card">
          ${user.picture ? `<img src="${user.picture}" alt="Profile" width="80" />` : ''}
          <h2>${user.name || user.nickname || 'User'}</h2>
          <p><strong>Email:</strong> ${user.email || 'N/A'}</p>
        </div>
        <h3>Full User Object</h3>
        <pre>${JSON.stringify(user, null, 2)}</pre>
        <nav>
          <a href="/">← Back to Home</a> | <a href="/logout">Logout</a>
        </nav>
      </body>
    </html>
  `);
});

app.listen(port, () => {
  console.log(`Server running at http://localhost:${port}`);
});
要点:
  • requiresAuth() ミドルウェアは /profile ルートを保護し、未認証のユーザーをログイン画面にリダイレクトします
  • req.oidc.user には、認証済みユーザーのユーザープロファイル情報が含まれています
  • req.oidc.isAuthenticated() は、ログイン状態を示すブール値を返します
  • ログインおよびログアウトのルート (/login/logout) は、auth() ミドルウェアによって自動的に作成されます

6. アプリを実行する

開発サーバーを起動します。
npm run dev
ブラウザで http://localhost:3000 を開きます。
チェックポイントこれで、正しく動作する Auth0 のログインページが表示されるはずです。次のことを確認してください。
  1. “Login” をクリックする - Auth0 の Universal Login ページにリダイレクトされます
  2. 認証を完了する - アプリにリダイレクトされます
  3. “/profile” にアクセスする - ユーザー情報が表示されます
  4. “Logout” をクリックする - アプリと Auth0 の両方からログアウトされます

応用

認証が必要な個々のルートを保護するには、requiresAuth() ミドルウェアを使用します。
const { auth, requiresAuth } = require('express-openid-connect');

app.use(auth({ authRequired: false }));

// パブリックルート
app.get('/', (req, res) => {
  res.send('Welcome! This is public.');
});

// 保護されたルート
app.get('/dashboard', requiresAuth(), (req, res) => {
  res.send(`Hello ${req.oidc.user.name}, welcome to your dashboard!`);
});

app.get('/settings', requiresAuth(), (req, res) => {
  res.send('Settings page - only for authenticated users');
});
Express Router を使用して、特定のパス配下にあるすべてのルートを保護することもできます。
const protectedRouter = express.Router();

// このルーター内のすべてのルートで認証が必要
protectedRouter.use(requiresAuth());

protectedRouter.get('/dashboard', (req, res) => {
  res.send('Protected dashboard');
});

protectedRouter.get('/settings', (req, res) => {
  res.send('Protected settings');
});

app.use('/app', protectedRouter);
// ルート: /app/dashboard、/app/settings はすべて保護済み
アクセストークンが必要な外部 API を呼び出すには、SDK を設定してアクセストークンを取得するようにします。📁 index.js (更新後の設定)
const config = {
  authRequired: false,
  auth0Logout: true,
  secret: process.env.SECRET,
  baseURL: process.env.BASE_URL,
  clientID: process.env.CLIENT_ID,
  issuerBaseURL: process.env.ISSUER_BASE_URL,
  clientSecret: process.env.CLIENT_SECRET,  // 認可コードフローで必要
  authorizationParams: {
    response_type: 'code',
    audience: process.env.API_AUDIENCE,     // 使用する API の識別子
    scope: 'openid profile email read:data',
  },
};
これらを .env ファイルに追加します。
CLIENT_SECRET=your_client_secret_from_dashboard
API_AUDIENCE=https://your-api.example.com
次に、アクセストークンを使用して API を呼び出します。
app.get('/api-data', requiresAuth(), async (req, res) => {
  try {
    let { token_type, access_token, isExpired, refresh } = req.oidc.accessToken;

    // トークンの有効期限が切れている場合は更新する
    if (isExpired()) {
      const refreshed = await refresh();
      access_token = refreshed.access_token;
    }

    // 保護されたAPIを呼び出す
    const response = await fetch('https://your-api.example.com/data', {
      headers: {
        Authorization: `${token_type} ${access_token}`,
      },
    });

    const data = await response.json();
    res.json(data);
  } catch (error) {
    console.error('API call failed:', error);
    res.status(500).json({ error: 'Failed to fetch data' });
  }
});
リフレッシュトークンを取得するには、scope に offline_access を追加します。
scope: 'openid profile email offline_access read:data',
ユーザーのクレーム (ロールや権限など) に基づいてルートを保護します:
const { auth, requiresAuth, claimEquals, claimIncludes, claimCheck } = require('express-openid-connect');

app.use(auth({ authRequired: false }));

// role = 'admin' のユーザーのみ
app.get('/admin', claimEquals('role', 'admin'), (req, res) => {
  res.send('Admin dashboard');
});

// roles 配列に 'editor' が含まれるユーザー
app.get('/editor', claimIncludes('roles', 'editor'), (req, res) => {
  res.send('Editor dashboard');
});

// カスタムクレームチェック(ロジックあり)
app.get('/premium', claimCheck((req, claims) => {
  return claims.subscription === 'premium' || claims.role === 'admin';
}), (req, res) => {
  res.send('Premium content');
});
role のようなクレームは、Auth0 の Rules または Actions を使用してトークンに追加する必要があります。カスタムクレームを追加する方法の詳細はこちら
本番環境や複数のサーバーインスタンスを実行する場合は、カスタムのセッションストアを使用してください。
npm install redis connect-redis
const { auth } = require('express-openid-connect');
const { createClient } = require('redis');
const RedisStore = require('connect-redis').default;

// Redisクライアントを作成
const redisClient = createClient({
  url: process.env.REDIS_URL || 'redis://localhost:6379',
});
redisClient.connect().catch(console.error);

const config = {
  authRequired: false,
  auth0Logout: true,
  secret: process.env.SECRET,
  baseURL: process.env.BASE_URL,
  clientID: process.env.CLIENT_ID,
  issuerBaseURL: process.env.ISSUER_BASE_URL,
  session: {
    store: new RedisStore({ client: redisClient }),
  },
};

app.use(auth(config));
カスタムセッションストアを使用すべきケース:
  • 複数のサーバーインスタンスを運用している場合 (ロードバランシング)
  • セッションデータが Cookie のサイズ制限 (約4KB) を超える場合
  • サーバーの再起動後もセッションを保持する必要がある場合
  • バックチャネルログアウトを使用している場合
認証エラーに対する適切なエラー処理を追加します。
const { auth } = require('express-openid-connect');

app.use(auth({
  authRequired: false,
  auth0Logout: true,
  secret: process.env.SECRET,
  baseURL: process.env.BASE_URL,
  clientID: process.env.CLIENT_ID,
  issuerBaseURL: process.env.ISSUER_BASE_URL,
  errorOnRequiredAuth: true,  // APIルートではリダイレクトの代わりに401を返す
}));

// Custom error handler
app.use((err, req, res, next) => {
  // Handle authentication errors
  if (err.statusCode === 401) {
    // For API requests, return JSON
    if (req.accepts('json')) {
      return res.status(401).json({
        error: 'Authentication required',
        login_url: '/login',
      });
    }
    // For browser requests, redirect to login
    return res.redirect('/login');
  }

  // Log the error (don't expose details to client)
  console.error('Application error:', err.message);

  res.status(err.statusCode || 500).json({
    error: 'An unexpected error occurred',
  });
});

トラブルシューティング


次のステップ

認証が動作するようになったら、次の内容も確認してください。

リソース