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

AI を使用して Auth0 を統合する

Claude Code、Cursor、GitHub Copilot などの AI コーディングアシスタントを使用している場合は、agent skills を使って、数分で Auth0 認証を自動的に追加できます。インストール:
npx skills add auth0/agent-skills --skill auth0-quickstart --skill auth0-fastify
次に、AI アシスタントに次のように依頼します。
Add Auth0 authentication to my Fastify app
AI アシスタントは、Auth0 アプリケーションの作成、認証情報の取得、@auth0/auth0-fastify のインストール、プラグインの設定、必要なルートとビューの作成を自動的に行います。agent skills の完全なドキュメント →
前提条件: 開始する前に、次のものがインストールされていることを確認してください。
  • Node.js 20 LTS 以降
  • npm 10 以降、または yarn 1.22 以降、または pnpm 8 以降
インストールを確認するには、次を実行します: node --version && npm --versionFastify のバージョン互換性: このクイックスタートは Fastify 5.x 以降に対応しています。

はじめに

このクイックスタートでは、Fastify アプリケーションに Auth0 認証を追加する方法を説明します。Auth0 Fastify SDK を使用して、ログイン、ログアウト、ユーザープロファイル機能を備えたセキュアな Web アプリケーションを構築します。
1

新規プロジェクトを作成

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

Auth0 Fastify SDK をインストールする

必要な依存関係をインストールする
npm install @auth0/auth0-fastify fastify dotenv @fastify/view ejs
サーバーサイドレンダリングには、ejs@fastify/view を使用しています。Fastify でサポートされている任意のテンプレートエンジンを使用できます。
package.json を更新して、start スクリプトを追加します。
package.json
{
  "name": "auth0-fastify",
  "version": "1.0.0",
  "type": "module",
  "main": "server.js",
  "scripts": {
    "start": "node server.js",
    "dev": "node --watch server.js"
  },
  "dependencies": {
    "@auth0/auth0-fastify": "^1.2.0",
    "@fastify/view": "^10.0.0",
    "dotenv": "^16.3.1",
    "ejs": "^3.1.9",
    "fastify": "^5.0.0"
  }
}
3

Auth0アプリを設定する

次に、Auth0テナントに新しいアプリケーションを作成し、プロジェクトに環境変数を追加します。Auth0アプリの設定方法は 3 つあります。Quick Setup ツール (推奨) を使う方法、CLI コマンドを実行する方法、または Dashboard で手動設定する方法です。
Auth0アプリを作成し、正しい設定値があらかじめ入力された .env ファイルをコピーします。
.env ファイルが存在することを確認します: cat .env (Mac/Linux) または type .env (Windows)
4

Auth0プラグインを設定する

Fastify サーバーを作成し、Auth0 プラグインを登録します。
server.js
import 'dotenv/config';
import Fastify from 'fastify';
import fastifyView from '@fastify/view';
import fastifyAuth0 from '@auth0/auth0-fastify';
import ejs from 'ejs';

const fastify = Fastify({ logger: true });
const port = process.env.PORT || 3000;

// ビューエンジンを登録する
await fastify.register(fastifyView, {
  engine: { ejs },
  root: './views',
});

// Auth0 プラグインを登録する
await fastify.register(fastifyAuth0, {
  domain: process.env.AUTH0_DOMAIN,
  clientId: process.env.AUTH0_CLIENT_ID,
  clientSecret: process.env.AUTH0_CLIENT_SECRET,
  appBaseUrl: process.env.APP_BASE_URL,
  sessionSecret: process.env.SESSION_SECRET,
});

// サーバーを起動する
fastify.listen({ port }, (err) => {
  if (err) {
    fastify.log.error(err);
    process.exit(1);
  }
  fastify.log.info(`Server running at http://localhost:${port}`);
});
このコードで行われること:
  • HTML テンプレートをレンダリングするためのビューエンジンを登録します
  • 認証情報を使用して Auth0 プラグインを設定します
  • /auth/login/auth/logout/auth/callback にルートを自動作成します
  • 暗号化された Cookie を使用してセッションを管理します
5

ビュー テンプレートを作成

views ディレクトリを作成し、テンプレートファイルを追加します:
Mac/Linux
mkdir views && touch views/home.ejs views/profile.ejs
Windows
New-Item -ItemType Directory -Path views
New-Item -ItemType File -Path views/home.ejs
New-Item -ItemType File -Path views/profile.ejs
ホームページのテンプレートを作成します。
views/home.ejs
<!DOCTYPE html>
<html lang="en">
<head>
  <meta charset="UTF-8">
  <meta name="viewport" content="width=device-width, initial-scale=1.0">
  <title>Auth0 Fastify クイックスタート</title>
  <style>
    body {
      font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, sans-serif;
      background: linear-gradient(135deg, #667eea 0%, #764ba2 100%);
      margin: 0;
      padding: 2rem;
      min-height: 100vh;
      display: flex;
      justify-content: center;
      align-items: center;
    }
    .container {
      background: white;
      border-radius: 20px;
      box-shadow: 0 20px 60px rgba(0, 0, 0, 0.3);
      padding: 3rem;
      max-width: 500px;
      width: 100%;
      text-align: center;
    }
    h1 {
      color: #2d3748;
      font-size: 2.5rem;
      margin-bottom: 1rem;
    }
    .status {
      padding: 1rem;
      border-radius: 10px;
      margin: 1.5rem 0;
      font-size: 1.1rem;
    }
    .logged-in {
      background: #d4edda;
      color: #155724;
    }
    .logged-out {
      background: #f8d7da;
      color: #721c24;
    }
    .button {
      display: inline-block;
      padding: 1rem 2rem;
      margin: 0.5rem;
      border-radius: 10px;
      text-decoration: none;
      font-weight: 600;
      transition: all 0.3s;
    }
    .button-primary {
      background: #667eea;
      color: white;
    }
    .button-primary:hover {
      background: #5568d3;
      transform: translateY(-2px);
    }
    .button-secondary {
      background: #e53e3e;
      color: white;
    }
    .button-secondary:hover {
      background: #c53030;
      transform: translateY(-2px);
    }
  </style>
</head>
<body>
  <div class="container">
    <h1>🚀 Auth0 Fastify</h1>
    <div class="status <%= isAuthenticated ? 'logged-in' : 'logged-out' %>">
      <%= isAuthenticated ? '✓ ログイン済みです' : '✗ ログアウトしています' %>
    </div>
    <div>
      <% if (isAuthenticated) { %>
        <a href="/profile" class="button button-primary">プロフィールを見る</a>
        <a href="/auth/logout" class="button button-secondary">ログアウト</a>
      <% } else { %>
        <a href="/auth/login" class="button button-primary">ログイン</a>
      <% } %>
    </div>
  </div>
</body>
</html>
プロフィールページのテンプレートを作成します。
views/profile.ejs
<!DOCTYPE html>
<html lang="en">
<head>
  <meta charset="UTF-8">
  <meta name="viewport" content="width=device-width, initial-scale=1.0">
  <title>Profile - Auth0 Fastify</title>
  <style>
    body {
      font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, sans-serif;
      background: linear-gradient(135deg, #667eea 0%, #764ba2 100%);
      margin: 0;
      padding: 2rem;
      min-height: 100vh;
    }
    .container {
      background: white;
      border-radius: 20px;
      box-shadow: 0 20px 60px rgba(0, 0, 0, 0.3);
      padding: 3rem;
      max-width: 700px;
      margin: 0 auto;
    }
    h1 {
      color: #2d3748;
      margin-bottom: 2rem;
    }
    .profile-card {
      display: flex;
      align-items: center;
      gap: 2rem;
      padding: 2rem;
      background: #f7fafc;
      border-radius: 15px;
      margin-bottom: 2rem;
    }
    .profile-picture {
      width: 100px;
      height: 100px;
      border-radius: 50%;
      object-fit: cover;
      border: 3px solid #667eea;
    }
    .profile-info h2 {
      margin: 0 0 0.5rem 0;
      color: #2d3748;
    }
    .profile-info p {
      margin: 0;
      color: #718096;
    }
    .user-data {
      background: #f7fafc;
      padding: 1.5rem;
      border-radius: 10px;
      overflow-x: auto;
    }
    pre {
      margin: 0;
      white-space: pre-wrap;
      word-wrap: break-word;
    }
    .button {
      display: inline-block;
      padding: 0.75rem 1.5rem;
      margin-right: 1rem;
      border-radius: 10px;
      text-decoration: none;
      font-weight: 600;
      transition: all 0.3s;
    }
    .button-primary {
      background: #667eea;
      color: white;
    }
    .button-primary:hover {
      background: #5568d3;
    }
    .button-secondary {
      background: #e53e3e;
      color: white;
    }
    .button-secondary:hover {
      background: #c53030;
    }
  </style>
</head>
<body>
  <div class="container">
    <h1>User Profile</h1>
    <div class="profile-card">
      <img src="<%= user.picture || 'https://via.placeholder.com/100' %>" alt="Profile" class="profile-picture">
      <div class="profile-info">
        <h2><%= user.name || user.nickname || 'User' %></h2>
        <p><strong>Email:</strong> <%= user.email || 'N/A' %></p>
      </div>
    </div>
    <h3>Full User Object</h3>
    <div class="user-data">
      <pre><%= JSON.stringify(user, null, 2) %></pre>
    </div>
    <div style="margin-top: 2rem;">
      <a href="/" class="button button-primary">← Back to Home</a>
      <a href="/auth/logout" class="button button-secondary">Logout</a>
    </div>
  </div>
</body>
</html>
6

ルートを作成

server.js ファイルにルートを追加します:
server.js
import 'dotenv/config';
import Fastify from 'fastify';
import fastifyView from '@fastify/view';
import fastifyAuth0 from '@auth0/auth0-fastify';
import ejs from 'ejs';

const fastify = Fastify({ logger: true });
const port = process.env.PORT || 3000;

// ビューエンジンを登録
await fastify.register(fastifyView, {
  engine: { ejs },
  root: './views',
});

// Auth0 プラグインを登録
await fastify.register(fastifyAuth0, {
  domain: process.env.AUTH0_DOMAIN,
  clientId: process.env.AUTH0_CLIENT_ID,
  clientSecret: process.env.AUTH0_CLIENT_SECRET,
  appBaseUrl: process.env.APP_BASE_URL,
  sessionSecret: process.env.SESSION_SECRET,
});

// ホームルート - 公開
fastify.get('/', async (request, reply) => {
  const session = await fastify.auth0Client.getSession({ request, reply });
  return reply.view('views/home.ejs', {
    isAuthenticated: !!session,
  });
});

// プロフィールルート - 保護
fastify.get('/profile', {
  preHandler: async (request, reply) => {
    const session = await fastify.auth0Client.getSession({ request, reply });
    if (!session) {
      return reply.redirect('/auth/login');
    }
  }
}, async (request, reply) => {
  const user = await fastify.auth0Client.getUser({ request, reply });
  return reply.view('views/profile.ejs', { user });
});

// サーバーを起動
fastify.listen({ port }, (err) => {
  if (err) {
    fastify.log.error(err);
    process.exit(1);
  }
  fastify.log.info(`Server running at http://localhost:${port}`);
});
重要なポイント:
  • ホームルートでは認証状態を確認し、その結果をテンプレートに渡します
  • プロファイルルートでは、ルートを保護するために preHandler を使用します
  • getSession() はユーザーのセッションを返し、認証されていない場合は null を返します
  • getUser() は認証済みユーザーのプロフィール情報を返します
7

アプリを起動する

開発サーバーを起動します。
npm run dev
ブラウザーで http://localhost:3000 を開きます。
Node.js 20 以降では、--watch フラグを使用すると、ファイルの変更時にサーバーが自動的に再起動します。
チェックポイントこれで、Auth0 のログインページが完全に動作するようになっているはずです。次のことを確認してください。
  1. 「Login」をクリックすると、Auth0 の Universal Login ページにリダイレクトされる
  2. 認証を完了すると、アプリにリダイレクトされる
  3. 「/profile」にアクセスすると、ユーザー情報が表示される
  4. 「Logout」をクリックすると、アプリと Auth0 の両方からログアウトされる

高度な使い方

アクセストークンが必要な外部 API を呼び出すには、オーディエンスを指定して SDK を設定します。
server.js
await fastify.register(fastifyAuth0, {
  domain: process.env.AUTH0_DOMAIN,
  clientId: process.env.AUTH0_CLIENT_ID,
  clientSecret: process.env.AUTH0_CLIENT_SECRET,
  appBaseUrl: process.env.APP_BASE_URL,
  sessionSecret: process.env.SESSION_SECRET,
  audience: process.env.AUTH0_AUDIENCE, // これを追加
});
.env ファイルに次を追加します。
.env
AUTH0_AUDIENCE=https://your-api.example.com
次に、アクセストークンを取得して使用します。
server.js
fastify.get('/api-data', {
  preHandler: async (request, reply) => {
    const session = await fastify.auth0Client.getSession({ request, reply });
    if (!session) {
      return reply.redirect('/auth/login');
    }
  }
}, async (request, reply) => {
  try {
    const { accessToken } = await fastify.auth0Client.getAccessToken({ request, reply });

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

    const data = await response.json();
    return data;
  } catch (error) {
    fastify.log.error('API call failed:', error);
    return reply.status(500).send({ error: 'Failed to fetch data' });
  }
});
デフォルトでは、Auth0 のルートは /auth/* にマウントされます。自動マウントを無効にして、カスタムルートを作成できます。
server.js
await fastify.register(fastifyAuth0, {
  domain: process.env.AUTH0_DOMAIN,
  clientId: process.env.AUTH0_CLIENT_ID,
  clientSecret: process.env.AUTH0_CLIENT_SECRET,
  appBaseUrl: process.env.APP_BASE_URL,
  sessionSecret: process.env.SESSION_SECRET,
  mountRoutes: false, // 自動マウントを無効にする
});

// カスタムログインルート
fastify.get('/custom-login', async (request, reply) => {
  const authorizationUrl = await fastify.auth0Client.startInteractiveLogin(
    {
      authorizationParams: {
        redirect_uri: `${process.env.APP_BASE_URL}/custom-callback`
      }
    },
    { request, reply }
  );
  return reply.redirect(authorizationUrl.href);
});

// カスタムコールバックルート
fastify.get('/custom-callback', async (request, reply) => {
  await fastify.auth0Client.completeInteractiveLogin(
    new URL(request.url, process.env.APP_BASE_URL),
    { request, reply }
  );
  return reply.redirect('/');
});

// カスタムログアウトルート
fastify.get('/custom-logout', async (request, reply) => {
  const logoutUrl = await fastify.auth0Client.logout(
    { returnTo: process.env.APP_BASE_URL },
    { request, reply }
  );
  return reply.redirect(logoutUrl.href);
});
カスタムコールバック URL を含めるよう、Auth0 Dashboard の Allowed Callback URLs を忘れずに更新してください。
ユーザーが複数の認証プロバイダーを 1 つのアカウントにリンクできるようにします。
server.js
await fastify.register(fastifyAuth0, {
  domain: process.env.AUTH0_DOMAIN,
  clientId: process.env.AUTH0_CLIENT_ID,
  clientSecret: process.env.AUTH0_CLIENT_SECRET,
  appBaseUrl: process.env.APP_BASE_URL,
  sessionSecret: process.env.SESSION_SECRET,
  mountConnectRoutes: true, // アカウントリンク用ルートを有効にする
});
これにより、次のルートが自動的に作成されます。
  • /auth/connect - 新しいプロバイダーをリンク
  • /auth/connect/callback - リンクのコールバックを処理
  • /auth/unconnect - プロバイダーのリンクを解除
  • /auth/unconnect/callback - リンク解除のコールバックを処理
プロフィールページにリンク用のボタンを追加します。
views/profile.ejs
<div>
  <a href="/auth/connect?connection=google-oauth2">Google アカウントをリンク</a>
  <a href="/auth/unconnect?connection=google-oauth2">Google アカウントのリンクを解除</a>
</div>
型安全性を向上させるため、プロジェクトを TypeScript に移行します。
npm install --save-dev typescript @types/node tsx
tsconfig.json を作成します。
tsconfig.json
{
  "compilerOptions": {
    "target": "ES2022",
    "module": "ESNext",
    "moduleResolution": "node",
    "esModuleInterop": true,
    "strict": true,
    "skipLibCheck": true,
    "outDir": "./dist"
  },
  "include": ["src/**/*"],
  "exclude": ["node_modules"]
}
server.jsserver.ts にリネームし、型を追加します。
server.ts
import 'dotenv/config';
import Fastify, { FastifyRequest, FastifyReply } from 'fastify';
import fastifyView from '@fastify/view';
import fastifyAuth0 from '@auth0/auth0-fastify';
import ejs from 'ejs';

const fastify = Fastify({ logger: true });
const port = process.env.PORT || 3000;

await fastify.register(fastifyView, {
  engine: { ejs },
  root: './views',
});

await fastify.register(fastifyAuth0, {
  domain: process.env.AUTH0_DOMAIN!,
  clientId: process.env.AUTH0_CLIENT_ID!,
  clientSecret: process.env.AUTH0_CLIENT_SECRET!,
  appBaseUrl: process.env.APP_BASE_URL!,
  sessionSecret: process.env.SESSION_SECRET!,
});

fastify.get('/', async (request: FastifyRequest, reply: FastifyReply) => {
  const session = await fastify.auth0Client.getSession({ request, reply });
  return reply.view('views/home.ejs', {
    isAuthenticated: !!session,
  });
});

fastify.listen({ port: Number(port) });
package.json を更新します。
package.json
{
  "scripts": {
    "dev": "tsx watch server.ts",
    "build": "tsc",
    "start": "node dist/server.js"
  }
}

トラブルシューティング


次のステップ

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

リソース