> ## Documentation Index
> Fetch the complete documentation index at: https://translations.mintlify.site/llms.txt
> Use this file to discover all available pages before exploring further.

# Angular アプリケーションにログイン機能を追加する

> このガイドでは、Auth0 Angular SDK を使用して任意の Angular アプリケーションに Auth0 を統合し、認証を追加して、ユーザープロファイル情報を表示する方法を説明します。

export const HowToSchema = () => <script type="application/ld+json">
    {'{"@context":"https://schema.org","@type":"HowTo"}'}
  </script>;

export const CreateInteractiveApp = ({placeholderText = "Auth0", appType = "regular_web", allowedCallbackUrls = ["localhost:3000"], allowedLogoutUrls = ["localhost:3000"], allowedOriginUrls = ["localhost:3000"]}) => {
  const [isAuthenticated, setIsAuthenticated] = useState(false);
  const [storeReady, setStoreReady] = useState(false);
  const [displayForm, setDisplayForm] = useState(true);
  useEffect(() => {
    const init = () => setStoreReady(true);
    if (window.rootStore) {
      window.rootStore.clientStore.setSelectedClient(null);
      window.rootStore.clientStore.setSelectedClientSecret(undefined);
      init();
    } else {
      window.addEventListener("adu:storeReady", init);
    }
    return () => {
      window.removeEventListener("adu:storeReady", init);
    };
  }, []);
  useEffect(() => {
    if (!storeReady) return;
    const disposer = autorun(() => {
      const rootStore = window.rootStore;
      setIsAuthenticated(rootStore.sessionStore.isAuthenticated);
    });
    return () => {
      disposer();
    };
  }, [storeReady]);
  if (!storeReady || typeof window === "undefined" || !displayForm) {
    return <></>;
  }
  const login = () => {
    const baseUrl = window.rootStore.config.apiBaseUrl;
    const returnTo = encodeURIComponent(window.location.href);
    window.location.href = `${baseUrl}/auth/user/login?returnTo=${returnTo}`;
  };
  const Card = ({className = "", children}) => {
    return <div className={`
          flex border rounded-2xl
          border-gray-950/10 dark:border-white/10
          py-3.5 px-4 gap-2
          text-sm text-gray-900 dark:text-gray-200
          ${className}
        `}>
        {children}
      </div>;
  };
  const Button = ({children, ...props}) => {
    return <button className="bg-[--button-primary] text-[--foreground-inverse] px-[1.125rem] py-1.5 rounded-lg font-medium" {...props}>
        {children}
      </button>;
  };
  const CreateApplicationForm = () => {
    const [name, setName] = useState("");
    const [isLoading, setIsLoading] = useState(false);
    const [error, setError] = useState("");
    const handleSubmit = async () => {
      if (!name.trim()) {
        setError("アプリケーション名は必須です");
        return;
      }
      setIsLoading(true);
      setError(null);
      try {
        await window.rootStore.clientStore.createClient({
          name: name.trim(),
          app_type: appType,
          callbacks: allowedCallbackUrls,
          allowed_logout_urls: allowedLogoutUrls,
          web_origins: allowedOriginUrls,
          client_metadata: {
            created_by: "quickstart-docs-app-creation-component"
          }
        });
        setDisplayForm(false);
      } catch (err) {
        console.error("クライアントの作成中にエラーが発生しました:", err);
        const errorMessage = err instanceof Error ? err.message : "アプリケーションの作成に失敗しました";
        setError(errorMessage);
      } finally {
        setIsLoading(false);
      }
    };
    return <Card className="flex-col items-start p-4 gap-3.75">
        <span className="font-medium text-gray-900 dark:text-gray-200">Auth0 アプリの作成</span>
        <div className="w-full flex gap-2">
          <input id="app-name" name={name} className="
              w-full max-w-[448px] h-11 py-2 px-4 
              border rounded-lg border-gray-950/10 dark:border-white/10 
              text-gray-900 dark:text-gray-200
              focus:outline-none dark:focus:outline-none
            " placeholder={`My ${placeholderText} App`} value={name} onChange={e => setName(e.target.value)} />
          <Button onClick={handleSubmit}>{isLoading ? "作成中..." : "作成"}</Button>
        </div>
        {error && <p className="text-red-500">{error}</p>}
      </Card>;
  };
  const SignInForm = () => {
    return <Card className="items-center">
        <Button onClick={login}>ログイン</Button> <span>してアプリを作成してください</span>
      </Card>;
  };
  return isAuthenticated ? <CreateApplicationForm /> : <SignInForm />;
};

export const AuthCodeBlock = ({filename, icon, language, highlight, children}) => {
  const [displayText, setDisplayText] = useState(children);
  const [copyText, setCopyText] = useState(children);
  const wrapperRef = React.useRef(null);
  useEffect(() => {
    let unsubscribe = null;
    function init() {
      if (!window.autorun || !window.rootStore) {
        return;
      }
      unsubscribe = window.autorun(() => {
        let processedChildrenForDisplay = children;
        let processedChildrenForCopy = children;
        for (const [key, value] of window.rootStore.variableStore.values.entries()) {
          const escapedKey = key.replaceAll(/[.*+?^${}()|[\]\\]/g, (String.raw)`\$&`);
          let displayValue = value;
          if (key === "{yourClientSecret}" && value !== "{yourClientSecret}") {
            displayValue = value.substring(0, 3) + "*****マスク済み*****";
          }
          processedChildrenForDisplay = processedChildrenForDisplay.replaceAll(new RegExp(escapedKey, "g"), displayValue);
          processedChildrenForCopy = processedChildrenForCopy.replaceAll(new RegExp(escapedKey, "g"), value);
        }
        setDisplayText(processedChildrenForDisplay);
        setCopyText(processedChildrenForCopy);
      });
    }
    if (window.rootStore) {
      init();
    } else {
      window.addEventListener("adu:storeReady", init);
    }
    return () => {
      window.removeEventListener("adu:storeReady", init);
      unsubscribe?.();
    };
  }, [children]);
  useEffect(() => {
    if (!wrapperRef.current) return;
    const originalWriteText = navigator.clipboard.writeText.bind(navigator.clipboard);
    let isOverriding = false;
    const handleClick = e => {
      const button = e.target.closest('[data-testid="copy-code-button"]');
      if (!button || !wrapperRef.current.contains(button)) return;
      isOverriding = true;
      navigator.clipboard.writeText = text => {
        if (isOverriding) {
          isOverriding = false;
          navigator.clipboard.writeText = originalWriteText;
          return originalWriteText(copyText);
        }
        return originalWriteText(text);
      };
      setTimeout(() => {
        if (isOverriding) {
          isOverriding = false;
          navigator.clipboard.writeText = originalWriteText;
        }
      }, 100);
    };
    const wrapper = wrapperRef.current;
    wrapper.addEventListener('click', handleClick, true);
    return () => {
      wrapper.removeEventListener('click', handleClick, true);
      if (navigator.clipboard.writeText !== originalWriteText) {
        navigator.clipboard.writeText = originalWriteText;
      }
    };
  }, [copyText]);
  return <div ref={wrapperRef}>
      <CodeBlock filename={filename} icon={icon} language={language} lines highlight={highlight}>
        {displayText}
      </CodeBlock>
    </div>;
};

export const AuthCodeGroup = ({children, dropdown}) => {
  const [processedChildren, setProcessedChildren] = useState(children);
  useEffect(() => {
    let unsubscribe = null;
    function init() {
      unsubscribe = window.autorun(() => {
        const processChildren = node => {
          if (typeof node === "string") {
            let processedNode = node;
            for (const [key, value] of window.rootStore.variableStore.values.entries()) {
              const escapedKey = key.replaceAll(/[.*+?^${}()|[\]\\]/g, (String.raw)`\$&`);
              processedNode = processedNode.replaceAll(new RegExp(escapedKey, "g"), value);
            }
            return processedNode;
          } else if (Array.isArray(node)) {
            return node.map(processChildren);
          } else if (node && node.props && node.props.children) {
            return {
              ...node,
              props: {
                ...node.props,
                children: processChildren(node.props.children)
              }
            };
          }
          return node;
        };
        setProcessedChildren(processChildren(children));
      });
    }
    if (window.rootStore) {
      init();
    } else {
      window.addEventListener("adu:storeReady", init);
    }
    return () => {
      window.removeEventListener("adu:storeReady", init);
      unsubscribe?.();
    };
  }, [children]);
  return <CodeGroup dropdown={dropdown}>{processedChildren}</CodeGroup>;
};

<HowToSchema />

<Accordion title="AI で Auth0 を統合する" icon="microchip-ai" iconType="solid" defaultOpen>
  Claude Code、Cursor、GitHub Copilot などの AI コーディングアシスタントを使用している場合は、[agent skills](https://agentskills.io/home) を使って数分で Auth0 認証を自動的に追加できます。

  **インストール:**

  ```bash theme={null}
  npx skills add auth0/agent-skills --skill auth0-quickstart --skill auth0-angular
  ```

  **次に、AI アシスタントに次のように依頼します。**

  ```text theme={null}
  Add Auth0 authentication to my Angular app
  ```

  AI アシスタントは、Auth0 アプリケーションを自動的に作成し、認証情報を取得して、`@auth0/auth0-angular` をインストールし、ルートガードと HTTP インターセプターを作成し、環境を設定します。[agent skills の完全なドキュメント →](/ja/docs/quickstart/agent-skills)
</Accordion>

<Note>
  **前提条件:** 始める前に、次のものがインストールされていることを確認してください。

  * **[Node.js](https://nodejs.org/en/download)** 20 LTS 以降
  * **[npm](https://docs.npmjs.com/downloading-and-installing-node-js-and-npm)** 10 以上、または **[yarn](https://classic.yarnpkg.com/lang/en/docs/install/)** 1.22 以上、または **[pnpm](https://pnpm.io/installation)** 8 以上
  * **[jq](https://jqlang.org/)** - Auth0 CLI のセットアップに必要

  インストールを確認します: `node --version && npm --version`

  **Angular のバージョン互換性:** このクイックスタートは、Angular CLI を使用する **Angular 19〜21** で動作します。`@auth0/auth0-angular` SDK は Angular 13 以降をサポートしています。
</Note>

<div id="get-started">
  ## はじめに
</div>

このクイックスタートでは、Angular アプリケーションに Auth0 認証を追加する方法を説明します。Angular の依存性注入システムと Auth0 Angular SDK を使用して、ログイン機能とログアウト機能を備えた安全なシングルページアプリケーションを構築します。

export const localEnvSnippet = `export const environment = {
  production: false,
  auth0: {
    domain: {yourDomain},
    clientId: {yourClientId}
  }
};`;

<Steps>
  <Step title="新規プロジェクトを作成する" stepNumber={1}>
    このクイックスタート用に新しい Angular プロジェクトを作成します

    ```shellscript theme={null}
    npx @angular/cli@latest new auth0-angular --routing=true --style=css
    ```

    プロジェクトを開く

    ```shellscript theme={null}
    cd auth0-angular
    ```
  </Step>

  <Step title="Auth0 Angular SDKをインストールする" stepNumber={2}>
    ```shellscript theme={null}
    npm install @auth0/auth0-angular && npm install
    ```
  </Step>

  <Step title="Auth0アプリの設定" stepNumber={3}>
    次に、Auth0テナントで新しいアプリを作成し、プロジェクトに環境変数を追加します。

    Auth0アプリをセットアップするには、次の3つの方法があります。クイックセットアップツールを使用する (推奨) 、CLIコマンドを実行する、またはDashboardから手動で設定する方法です。

    <Tabs>
      <Tab title="クイックセットアップ（推奨）">
        Auth0 アプリを作成し、適切な設定値があらかじめ入力された `.env` ファイルをコピーします。

        <CreateInteractiveApp placeholderText="Angular" appType="spa" allowedCallbackUrls={["http://localhost:4200"]} allowedLogoutUrls={["http://localhost:4200"]} allowedOriginUrls={["http://localhost:4200"]} />

        <AuthCodeBlock children={localEnvSnippet} language="typescript" filename="src/environments/environment.ts" />
      </Tab>

      <Tab title="CLI">
        Auth0 アプリを作成して環境ファイルを生成するには、プロジェクトのルートディレクトリで次のシェルコマンドを実行します。

        <AuthCodeGroup>
          ```shellscript Mac theme={null}
          AUTH0_APP_NAME="My Angular App" && brew tap auth0/auth0-cli && brew install auth0 && auth0 login --no-input && auth0 apps create -n "${AUTH0_APP_NAME}" -t spa -c http://localhost:4200 -l http://localhost:4200 -o http://localhost:4200 --json --metadata created_by="quickstart-docs-cli" > auth0-app-details.json && CLIENT_ID=$(jq -r '.client_id' auth0-app-details.json) && DOMAIN=$(auth0 tenants list --json | jq -r '.[] | select(.active == true) | .name') && mkdir -p src/environments && echo "export const environment = { production: false, auth0: { domain: '${DOMAIN}', clientId: '${CLIENT_ID}' } };" > src/environments/environment.ts && rm auth0-app-details.json && echo "Environment file created at src/environments/environment.ts with your Auth0 details:" && cat src/environments/environment.ts
          ```

          ```shellscript Windows theme={null}
          $AppName = "My Angular App"; winget install Auth0.CLI; auth0 login --no-input; auth0 apps create -n "$AppName" -t spa -c http://localhost:4200 -l http://localhost:4200 -o http://localhost:4200 --json --metadata created_by="quickstart-docs-cli" | Set-Content -Path auth0-app-details.json; $ClientId = (Get-Content -Raw auth0-app-details.json | ConvertFrom-Json).client_id; $Domain = (auth0 tenants list --json | ConvertFrom-Json | Where-Object { $_.active -eq $true }).name; New-Item -ItemType Directory -Force -Path "src\environments"; Set-Content -Path "src\environments\environment.ts" -Value "export const environment = { production: false, auth0: { domain: '$Domain', clientId: '$ClientId' } };"; Remove-Item auth0-app-details.json; Write-Output "Environment file created at src\environments\environment.ts with your Auth0 details:"; Get-Content "src\environments\environment.ts"
          ```
        </AuthCodeGroup>
      </Tab>

      <Tab title="Dashboard">
        始める前に、プロジェクト内に環境ファイルを作成してください

        ```typescript src/environments/environment.ts theme={null}
        export const environment = {
          production: false,
          auth0: {
            domain: 'YOUR_AUTH0_APP_DOMAIN',
            clientId: 'YOUR_AUTH0_APP_CLIENT_ID'
          }
        };
        ```

        1. [Auth0 Dashboard](https://manage.auth0.com/dashboard/)に移動します
        2. **Applications** > **Applications** > **Create Application** をクリックします
        3. ポップアップでアプリの名前を入力し、アプリの種類として `Single Page Web Application` を選択して、**Create** をクリックします
        4. Application Details ページで **Settings** タブに切り替えます
        5. `src/environments/environment.ts` ファイル内の `YOUR_AUTH0_APP_DOMAIN` と `YOUR_AUTH0_APP_CLIENT_ID` を、Dashboard の **ドメイン** と **クライアントID** の値に置き換えます

        最後に、Application Details ページの **Settings** タブで、以下の URL を設定します。

        **Allowed Callback URLs:**

        ```
        http://localhost:4200
        ```

        **許可済みログアウト URL:**

        ```
        http://localhost:4200
        ```

        **許可済みの Web オリジン:**

        ```
        http://localhost:4200
        ```

        <Info>
          **Allowed Callback URLs** は、認証後にユーザーを安全にアプリケーションへ戻すための重要なセキュリティ対策です。一致する URL がないとログイン処理は失敗し、ユーザーはアプリにアクセスできず、代わりに Auth0 のエラーページが表示されます。

          **Allowed Logout URLs** は、サインアウト時にシームレスなユーザー体験を提供するうえで不可欠です。一致する URL がないと、ユーザーはログアウト後にアプリケーションへリダイレクトされず、代わりに汎用的な Auth0 ページに留まることになります。

          **Allowed Web Origins** は、サイレント認証に不可欠です。これがないと、ユーザーはページを再読み込みしたときや、後でアプリに戻ったときにログアウトされます。
        </Info>
      </Tab>
    </Tabs>
  </Step>

  <Step title="Auth0 モジュールを設定する" stepNumber={4}>
    前の手順で環境ファイルを用意したら、`app.config.ts` の providers 配列に `provideAuth0` を追加します。

    ```typescript src/app/app.config.ts {3,4,5,11-17} lines theme={null}
    import { ApplicationConfig, provideZoneChangeDetection } from '@angular/core';
    import { provideRouter } from '@angular/router';
    import { provideAuth0 } from '@auth0/auth0-angular';
    import { environment } from '../environments/environment';
    import { routes } from './app.routes';

    export const appConfig: ApplicationConfig = {
      providers: [
        provideZoneChangeDetection({ eventCoalescing: true }),
        provideRouter(routes),
        provideAuth0({
          domain: environment.auth0.domain,
          clientId: environment.auth0.clientId,
          authorizationParams: {
            redirect_uri: window.location.origin
          }
        })
      ]
    };
    ```

    <Note>
      Auth0 アプリを Dashboard から手動で設定する場合は、Dashboard のドメインとクライアントIDを使用して `src/environments/environment.ts` を作成します。

      Angular 20+ のプロジェクトでは、このファイルに `provideBrowserGlobalErrorListeners()` も含まれます。`provideAuth0` と並べて配列内に残しておいてください。`main.ts` を変更する必要はありません。
    </Note>
  </Step>

  <Step title="Login、ログアウト、ユーザープロファイルの各コンポーネントを作成" stepNumber={5}>
    Angular CLIを使用してコンポーネントファイルを生成します：

    ```shellscript theme={null}
    ng generate component components/login-button --inline-template --inline-style --skip-tests && \
    ng generate component components/logout-button --inline-template --inline-style --skip-tests && \
    ng generate component components/profile --inline-template --inline-style --skip-tests
    ```

    各コンポーネントに以下のコードを追加します。

    <AuthCodeGroup>
      ```typescript src/app/components/login-button.component.ts expandable lines theme={null}
      import { Component, inject } from '@angular/core';
      import { AuthService } from '@auth0/auth0-angular';

      @Component({
        selector: 'app-login-button',
        standalone: true,
        template: `
          <button 
            (click)="loginWithRedirect()" 
            class="button login"
          >
            Log In
          </button>
        `
      })
      export class LoginButtonComponent {
        private auth = inject(AuthService);

        loginWithRedirect(): void {
          this.auth.loginWithRedirect();
        }
      }
      ```

      ```typescript src/app/components/logout-button.component.ts expandable lines theme={null}
      import { Component, inject } from '@angular/core';
      import { AuthService } from '@auth0/auth0-angular';

      @Component({
        selector: 'app-logout-button',
        standalone: true,
        template: `
          <button
            (click)="logout()"
            class="button logout"
          >
            Log Out
          </button>
        `
      })
      export class LogoutButtonComponent {
        private auth = inject(AuthService);

        logout(): void {
          this.auth.logout({ 
            logoutParams: { 
              returnTo: window.location.origin 
            } 
          });
        }
      }
      ```

      ```typescript src/app/components/profile.component.ts expandable lines theme={null}
      import { Component, inject } from '@angular/core';
      import { AuthService } from '@auth0/auth0-angular';
      import { CommonModule } from '@angular/common';

      @Component({
        selector: 'app-profile',
        standalone: true,
        imports: [CommonModule],
        template: `
          @if (auth.isLoading$ | async) {
            <div class="loading-text">Loading profile...</div>
          }
          
          @if ((auth.isAuthenticated$ | async) && (auth.user$ | async); as user) {
            <div style="display: flex; flex-direction: column; align-items: center; gap: 1rem;">
              @if (user.picture) {
                <img 
                  [src]="user.picture" 
                  [alt]="user.name || 'User'"
                  class="profile-picture"
                  style="
                    width: 110px; 
                    height: 110px; 
                    border-radius: 50%; 
                    object-fit: cover;
                    border: 3px solid #63b3ed;
                  "
                />
              }
              <div style="text-align: center;">
                <div 
                  class="profile-name" 
                  style="
                    font-size: 2rem; 
                    font-weight: 600; 
                    color: #f7fafc; 
                    margin-bottom: 0.5rem;
                  "
                >
                  {{ user.name }}
                </div>
                <div 
                  class="profile-email" 
                  style="
                    font-size: 1.15rem; 
                    color: #a0aec0;
                  "
                >
                  {{ user.email }}
                </div>
              </div>
            </div>
          }
        `
      })
      export class ProfileComponent {
        protected auth = inject(AuthService);
      }
      ```
    </AuthCodeGroup>

    次に、メインのAppコンポーネントを更新し、スタイルを追加します。

    <Tabs>
      <Tab title="App コンポーネント">
        アプリコンポーネントファイルの内容を置き換えます (Angular 20 以降では `src/app/app.ts`、Angular 19 では `src/app/app.component.ts`) :

        ```typescript src/app/app.ts expandable lines theme={null}
        import { Component, inject } from '@angular/core';
        import { AuthService } from '@auth0/auth0-angular';
        import { CommonModule } from '@angular/common';
        import { LoginButtonComponent } from './components/login-button.component';
        import { LogoutButtonComponent } from './components/logout-button.component';
        import { ProfileComponent } from './components/profile.component';

        @Component({
          selector: 'app-root',
          standalone: true,
          imports: [CommonModule, LoginButtonComponent, LogoutButtonComponent, ProfileComponent],
          template: `
            <div class="app-container">
              <!-- 読み込み中の状態 -->
              @if (auth.isLoading$ | async) {
                <div class="loading-state">
                  <div class="loading-text">Loading...</div>
                </div>
              }

              <!-- エラー状態 -->
              @if (auth.error$ | async; as error) {
                <div class="error-state">
                  <div class="error-title">Oops!</div>
                  <div class="error-message">Something went wrong</div>
                  <div class="error-sub-message">{{ error.message }}</div>
                </div>
              }

              <!-- メインコンテンツ -->
              @if (!(auth.isLoading$ | async) && !(auth.error$ | async)) {
                <div class="main-card-wrapper">
                  <img 
                    src="https://cdn.auth0.com/quantum-assets/dist/latest/logos/auth0/auth0-lockup-en-ondark.png" 
                    alt="Auth0 Logo" 
                    class="auth0-logo"
                  />
                  <h1 class="main-title">Welcome to Sample0</h1>
                  
                  <!-- 認証済み状態 -->
                  @if (auth.isAuthenticated$ | async) {
                    <div class="logged-in-section">
                      <div class="logged-in-message">✅ Successfully authenticated!</div>
                      <h2 class="profile-section-title">Your Profile</h2>
                      <div class="profile-card">
                        <app-profile />
                      </div>
                      <app-logout-button />
                    </div>
                  } @else {
                    <!-- 未認証状態 -->
                    <div class="action-card">
                      <p class="action-text">Get started by signing in to your account</p>
                      <app-login-button />
                    </div>
                  }
                </div>
              }
            </div>
          `,
          styles: []
        })
        export class App {
          protected auth = inject(AuthService);
        }
        ```
      </Tab>

      <Tab title="グローバルスタイル">
        `src/styles.css` にスタイルを追加します：

        ```css src/styles.css expandable lines theme={null}
        @import url('https://fonts.googleapis.com/css2?family=Inter:wght@300;400;500;600;700&display=swap');

        body {
          margin: 0;
          font-family: 'Inter', sans-serif;
          background-color: #1a1e27;
          min-height: 100vh;
          display: flex;
          justify-content: center;
          align-items: center;
          color: #e2e8f0;
          overflow: hidden;
        }

        #root {
          width: 100%;
          height: 100%;
          display: flex;
          justify-content: center;
          align-items: center;
        }

        .app-container {
          display: flex;
          flex-direction: column;
          justify-content: center;
          align-items: center;
          min-height: 100vh;
          width: 100%;
          padding: 1rem;
          box-sizing: border-box;
        }

        .loading-state, .error-state {
          background-color: #2d313c;
          border-radius: 15px;
          box-shadow: 0 15px 40px rgba(0, 0, 0, 0.4);
          padding: 3rem;
          text-align: center;
        }

        .loading-text {
          font-size: 1.8rem;
          font-weight: 500;
          color: #a0aec0;
          animation: pulse 1.5s infinite ease-in-out;
        }

        .error-state {
          background-color: #c53030;
          color: #fff;
        }

        .error-title {
          font-size: 2.8rem;
          font-weight: 700;
          margin-bottom: 0.5rem;
        }

        .error-message {
          font-size: 1.3rem;
          margin-bottom: 0.5rem;
        }

        .error-sub-message {
          font-size: 1rem;
          opacity: 0.8;
        }

        .main-card-wrapper {
          background-color: #262a33;
          border-radius: 20px;
          box-shadow: 0 20px 60px rgba(0, 0, 0, 0.6), 0 0 0 1px rgba(255, 255, 255, 0.05);
          display: flex;
          flex-direction: column;
          align-items: center;
          gap: 2rem;
          padding: 3rem;
          max-width: 500px;
          width: 90%;
          animation: fadeInScale 0.8s ease-out forwards;
        }

        .auth0-logo {
          width: 160px;
          margin-bottom: 1.5rem;
          opacity: 0;
          animation: slideInDown 1s ease-out forwards 0.2s;
        }

        .main-title {
          font-size: 2.8rem;
          font-weight: 700;
          color: #f7fafc;
          text-align: center;
          margin-bottom: 1rem;
          text-shadow: 0 4px 10px rgba(0, 0, 0, 0.3);
          opacity: 0;
          animation: fadeIn 1s ease-out forwards 0.4s;
        }

        .action-card {
          background-color: #2d313c;
          border-radius: 15px;
          box-shadow: inset 0 2px 5px rgba(0, 0, 0, 0.3), 0 5px 15px rgba(0, 0, 0, 0.3);
          padding: 2.5rem;
          display: flex;
          flex-direction: column;
          align-items: center;
          gap: 1.8rem;
          width: calc(100% - 2rem);
          opacity: 0;
          animation: fadeIn 1s ease-out forwards 0.6s;
        }

        .action-text {
          font-size: 1.25rem;
          color: #cbd5e0;
          text-align: center;
          line-height: 1.6;
          font-weight: 400;
        }

        .button {
          padding: 1.1rem 2.8rem;
          font-size: 1.2rem;
          font-weight: 600;
          border-radius: 10px;
          border: none;
          cursor: pointer;
          transition: all 0.3s cubic-bezier(0.25, 0.8, 0.25, 1);
          box-shadow: 0 8px 20px rgba(0, 0, 0, 0.4);
          text-transform: uppercase;
          letter-spacing: 0.08em;
          outline: none;
        }

        .button:focus {
          box-shadow: 0 0 0 4px rgba(99, 179, 237, 0.5);
        }

        .button.login {
          background-color: #63b3ed;
          color: #1a1e27;
        }

        .button.login:hover {
          background-color: #4299e1;
          transform: translateY(-5px) scale(1.03);
          box-shadow: 0 12px 25px rgba(0, 0, 0, 0.5);
        }

        .button.logout {
          background-color: #fc8181;
          color: #1a1e27;
        }

        .button.logout:hover {
          background-color: #e53e3e;
          transform: translateY(-5px) scale(1.03);
          box-shadow: 0 12px 25px rgba(0, 0, 0, 0.5);
        }

        .logged-in-section {
          display: flex;
          flex-direction: column;
          align-items: center;
          gap: 1.5rem;
          width: 100%;
        }

        .logged-in-message {
          font-size: 1.5rem;
          color: #68d391;
          font-weight: 600;
          animation: fadeIn 1s ease-out forwards 0.8s;
        }

        .profile-section-title {
          font-size: 2.2rem;
          animation: slideInUp 1s ease-out forwards 1s;
        }

        .profile-card {
          padding: 2.2rem;
          animation: scaleIn 0.8s ease-out forwards 1.2s;
        }

        .profile-picture {
          width: 110px;
          transition: transform 0.3s ease-in-out;
        }

        .profile-picture:hover {
          transform: scale(1.05);
        }

        .profile-name {
          font-size: 2rem;
          margin-top: 0.5rem;
        }

        .profile-email {
          font-size: 1.15rem;
          text-align: center;
        }

        @keyframes fadeIn {
          from { opacity: 0; }
          to { opacity: 1; }
        }

        @keyframes fadeInScale {
          from { opacity: 0; transform: scale(0.95); }
          to { opacity: 1; transform: scale(1); }
        }

        @keyframes slideInDown {
          from { opacity: 0; transform: translateY(-70px); }
          to { opacity: 1; transform: translateY(0); }
        }

        @keyframes slideInUp {
          from { opacity: 0; transform: translateY(50px); }
          to { opacity: 1; transform: translateY(0); }
        }

        @keyframes pulse {
          0%, 100% { opacity: 1; }
          50% { opacity: 0.6; }
        }

        @keyframes scaleIn {
          from { opacity: 0; transform: scale(0.8); }
          to { opacity: 1; transform: scale(1); }
        }

        @media (max-width: 600px) {
          .main-card-wrapper {
            padding: 2rem;
            margin: 1rem;
          }
          
          .main-title {
            font-size: 2.2rem;
          }
          
          .button {
            padding: 1rem 2rem;
            font-size: 1.1rem;
          }
          
          .auth0-logo {
            width: 120px;
          }
        }
        ```
      </Tab>
    </Tabs>
  </Step>

  <Step title="アプリを実行する" stepNumber={6}>
    ```shellscript theme={null}
    ng serve
    ```

    <Info>
      ポート4200がすでに使用されている場合は、`ng serve --port 4201` を実行し、Auth0 アプリのコールバックURLを `http://localhost:4201` に更新してください
    </Info>
  </Step>
</Steps>

<Check>
  **チェックポイント**

  [localhost](http://localhost:4200/) 上で Auth0 のログインページが問題なく動作しているはずです
</Check>

***

<div id="advanced-usage">
  ## 高度な使用方法
</div>

<Accordion title="従来の NgModule アプローチを使用する">
  `--standalone=false` でプロジェクトを作成した場合や、スタンドアロンコンポーネントではなく NgModule を使用したい場合は、Angular 20+ の CLI 生成プロジェクトで SDK を次のように設定します。

  ```typescript src/main.ts theme={null}
  import { platformBrowser } from '@angular/platform-browser';
  import { AppModule } from './app/app-module';

  platformBrowser().bootstrapModule(AppModule)
    .catch((err) => console.error(err));
  ```

  ```typescript src/app/app-module.ts theme={null}
  import { NgModule, provideBrowserGlobalErrorListeners } from '@angular/core';
  import { BrowserModule } from '@angular/platform-browser';
  import { AuthModule } from '@auth0/auth0-angular';
  import { environment } from '../environments/environment';

  import { AppRoutingModule } from './app-routing-module';
  import { App } from './app';

  @NgModule({
    declarations: [App],
    imports: [
      BrowserModule,
      AppRoutingModule,
      AuthModule.forRoot({
        domain: environment.auth0.domain,
        clientId: environment.auth0.clientId,
        authorizationParams: {
          redirect_uri: window.location.origin
        }
      })
    ],
    providers: [provideBrowserGlobalErrorListeners()],
    bootstrap: [App]
  })
  export class AppModule { }
  ```

  <Note>
    **Angular 19** を使用している場合、生成されるファイルは通常 `app.component.ts`、`app.module.ts`、`app-routing.module.ts` で、`main.ts` では `platformBrowser` ではなく `platformBrowserDynamic` を使用します。

    **Angular 20+** では `app.ts`、`app-module.ts`、`app-routing-module.ts` が生成されます (ファイル名にドットは含まれません) 。また、`main.ts` では上記のように `platformBrowser` を使用します。

    いずれの場合も、NgModule プロジェクトではメインのクイックスタートで示した `bootstrapApplication()` アプローチではなく、`bootstrapModule()` でブートストラップします。
  </Note>
</Accordion>

<Accordion title="AuthGuard でルートを保護する">
  認証が必要なルートを保護するには、最新の関数ベースのガードを使用します。

  ```typescript src/app/app.routes.ts theme={null}
  import { Routes } from '@angular/router';
  import { authGuardFn } from '@auth0/auth0-angular';
  import { ProfileComponent } from './components/profile.component';

  export const routes: Routes = [
    {
      path: 'profile',
      component: ProfileComponent,
      canActivate: [authGuardFn]
    },
    {
      path: '',
      redirectTo: '/profile',
      pathMatch: 'full'
    }
  ];
  ```

  次に、`app.config.ts` に `provideAuth0` とルーターを追加します (手順 4 でまだ追加していない場合) 。

  ```typescript src/app/app.config.ts theme={null}
  import { ApplicationConfig, provideZoneChangeDetection } from '@angular/core';
  import { provideRouter } from '@angular/router';
  import { provideAuth0 } from '@auth0/auth0-angular';
  import { environment } from '../environments/environment';
  import { routes } from './app.routes';

  export const appConfig: ApplicationConfig = {
    providers: [
      provideZoneChangeDetection({ eventCoalescing: true }),
      provideRouter(routes),
      provideAuth0({
        domain: environment.auth0.domain,
        clientId: environment.auth0.clientId,
        authorizationParams: {
          redirect_uri: window.location.origin
        }
      })
    ]
  };
  ```
</Accordion>

<Accordion title="保護された API を呼び出す">
  HTTP インターセプターを設定すると、API 呼び出しにトークンを自動的に付与できます。Auth0 インターセプターを含む `provideHttpClient` と `audience` を `app.config.ts` に追加します。

  ```typescript src/app/app.config.ts theme={null}
  import { ApplicationConfig, provideZoneChangeDetection } from '@angular/core';
  import { provideRouter } from '@angular/router';
  import { provideHttpClient, withInterceptors } from '@angular/common/http';
  import { provideAuth0, authHttpInterceptorFn } from '@auth0/auth0-angular';
  import { environment } from '../environments/environment';
  import { routes } from './app.routes';

  export const appConfig: ApplicationConfig = {
    providers: [
      provideZoneChangeDetection({ eventCoalescing: true }),
      provideRouter(routes),
      provideAuth0({
        domain: environment.auth0.domain,
        clientId: environment.auth0.clientId,
        authorizationParams: {
          redirect_uri: window.location.origin,
          audience: 'YOUR_API_IDENTIFIER'
        },
        httpInterceptor: {
          allowedList: [
            'http://localhost:3001/api/*'
          ]
        }
      }),
      provideHttpClient(
        withInterceptors([authHttpInterceptorFn])
      )
    ]
  };
  ```
</Accordion>
