> ## 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.

# Ionic Vue と Capacitor アプリケーションにログインを追加する

> このガイドでは、Auth0 Vue SDK を使用して、Ionic Vue と Capacitor のアプリケーションに 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 の認証を自動的に追加できます。
  まず、Auth0 agent skills をインストールします。

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

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

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

  AI アシスタントは、Auth0 アプリケーションの作成、認証情報の取得、Auth0 Vue SDK と Capacitor プラグインのインストール、ディープリンクの設定、ネイティブブラウザー統合によるログイン/ログアウト フローの実装を自動的に行います。詳しくは、[Auth0 agent skills](/ja/docs/quickstart/agent-skills) を参照してください。
</Accordion>

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

このクイックスタートでは、Capacitor を使用して iOS と Android 上で動作する Ionic Vue アプリケーションに Auth0 の認証を追加する方法を説明します。Auth0 Vue SDK と Capacitor のネイティブブラウザープラグインを使用して、ログイン、ログアウト、ユーザープロフィール機能を備えた安全なモバイルアプリを構築します。

<Steps>
  <Step title="新しいプロジェクトを作成する" stepNumber={1}>
    Capacitor を使用して新しい Ionic Vue プロジェクトを作成する

    ```shellscript theme={null}
    npx ionic start auth0-ionic-vue blank --type=vue --capacitor
    ```

    プロジェクトを開く

    ```shellscript theme={null}
    cd auth0-ionic-vue
    ```

    <Warning>
      `@ionic/cli` パッケージを使用していることを確認してください (非推奨の `ionic` パッケージではありません) 。プロジェクト作成時に `--npm-client` に関するエラーが表示される場合は、CLI を更新してください。

      ```shellscript theme={null}
      npm uninstall -g ionic
      npm i -g @ionic/cli
      ```
    </Warning>
  </Step>

  <Step title="Auth0 Vue SDKとCapacitorプラグインをインストールする" stepNumber={2}>
    ```shellscript theme={null}
    npm install @auth0/auth0-vue @capacitor/browser @capacitor/app
    ```

    <Info>
      **[`@capacitor/browser`](https://capacitorjs.com/docs/apis/browser)** は、安全に認証できるよう、Auth0 Universal Login ページをデバイスのシステムブラウザー (iOS では SFSafariViewController、Android では Chrome Custom Tabs) で開きます。

      **[`@capacitor/app`](https://capacitorjs.com/docs/apis/app)** はディープリンクイベントを監視し、ログイン後に Auth0 がリダイレクトでアプリに戻した際の OAuth callback をアプリで処理できるようにします。
    </Info>
  </Step>

  <Step title="Auth0アプリを設定する" stepNumber={3}>
    次に、Auth0 テナントに新しいアプリを作成する必要があります。Ionic Capacitor アプリでは、カスタム URL スキームのコールバックを使用する **Native** アプリケーションタイプを使用します。

    Auth0 アプリを設定する方法は 3 つあります。Quick Setup ツール (推奨) を使用する方法、CLI コマンドを実行する方法、または Dashboard で手動設定する方法です。

    <Tabs>
      <Tab title="Quick Setup（推奨）">
        Auth0 の **Native** アプリケーションを作成し、認証情報を取得します。

        <CreateInteractiveApp placeholderText="Ionic Vue" appType="native" allowedCallbackUrls={[]} allowedLogoutUrls={[]} allowedOriginUrls={["capacitor://localhost", "http://localhost"]} />

        アプリを作成したら、次の手順で使用する **ドメイン** と **クライアントID** の値を控えておきます。

        <Note>
          Dashboard でコールバック URL とログアウト URL も設定する必要があります。以下の URL 設定を参照してください。
        </Note>
      </Tab>

      <Tab title="CLI">
        プロジェクトのルートディレクトリで次のコマンドを実行して、Auth0 アプリを作成します。

        <CodeGroup>
          ```shellscript Mac theme={null}
          # Auth0 CLI をインストールします（まだインストールしていない場合）
          brew tap auth0/auth0-cli && brew install auth0

          # ログインして Native アプリケーションを作成します
          auth0 login && auth0 apps create \
            -n "My Ionic Vue App" \
            -t native \
            -o "capacitor://localhost,http://localhost" \
            --no-input
          ```

          ```powershell Windows theme={null}
          # Auth0 CLI をインストールします（まだインストールしていない場合）
          scoop bucket add auth0 https://github.com/auth0/scoop-auth0-cli.git
          scoop install auth0

          # ログインして Native アプリケーションを作成します
          auth0 login; auth0 apps create `
            -n "My Ionic Vue App" `
            -t native `
            -o "capacitor://localhost,http://localhost" `
            --no-input
          ```
        </CodeGroup>

        <Note>
          このコマンドでは、次の処理が行われます。

          1. Auth0 で認証します (必要に応じてログインを求められます)
          2. 許可されたオリジンを設定した **Native** アプリケーションを作成します
          3. プロジェクトで使用する **ドメイン** と **クライアントID** を出力します

          コールバック URL とログアウト URL は、引き続き Dashboard で設定する必要があります。詳しくは以下を参照してください。
        </Note>
      </Tab>

      <Tab title="Dashboard">
        1. [Auth0 Dashboard](https://manage.auth0.com/dashboard/) に移動します
        2. **Applications** > **Applications** > **Create Application** をクリックします
        3. アプリの名前 (例: `Ionic Vue App`) を入力し、アプリケーションタイプとして **Native** を選択して **Create** をクリックします
        4. **Settings** タブに切り替え、**ドメイン** と **クライアントID** の値を控えます

        <Info>
          このガイドでは、`YOUR_PACKAGE_ID` はアプリケーションのパッケージ ID を指します。これは `capacitor.config.ts` ファイルの `appId` フィールドで確認および設定できます。詳細については、[Capacitor の Config schema](https://capacitorjs.com/docs/config#schema) を参照してください。
        </Info>

        Application Details ページの **Settings** タブで、次の URL を設定します。

        **Allowed Callback URLs:**

        ```text theme={null}
        YOUR_PACKAGE_ID://{yourDomain}/capacitor/YOUR_PACKAGE_ID/callback
        ```

        **Allowed Logout URLs:**

        ```text theme={null}
        YOUR_PACKAGE_ID://{yourDomain}/capacitor/YOUR_PACKAGE_ID/callback
        ```

        **Allowed Origins:**

        ```text theme={null}
        capacitor://localhost, http://localhost
        ```

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

          **Allowed Logout URLs** は、サインアウト時にシームレスなユーザー体験を提供するために重要です。一致する URL がない場合、ログアウト後にユーザーはアプリケーションへリダイレクトされず、Auth0 の汎用ページに遷移します。

          **Allowed Origins** には、ネイティブアプリから Auth0 へのリクエストのために `capacitor://localhost` (iOS) と `http://localhost` (Android) を含める必要があります。
        </Info>
      </Tab>
    </Tabs>

    <Tip>
      **Quick Setup** または **CLI** を使用した場合は、[Auth0 Dashboard](https://manage.auth0.com/#/applications) で対象アプリの **Settings** タブに移動し、上記の Dashboard タブに記載されているコールバック URL とログアウト URL を設定してください。
    </Tip>
  </Step>

  <Step title="Auth0 プラグインを設定する" stepNumber={4}>
    ```typescript src/main.ts {5,6,8,9,14,15,16,17,18,19,20,21,22,23} lines theme={null}
    import { createApp } from 'vue'
    import App from './App.vue'
    import router from './router'
    import { IonicVue } from '@ionic/vue'
    import { createAuth0 } from '@auth0/auth0-vue'
    import config from '../capacitor.config'

    // アプリのパッケージIDを使用してcallback URLを構築する
    const redirect_uri = `${config.appId}://{yourDomain}/capacitor/${config.appId}/callback`

    const app = createApp(App)
      .use(IonicVue)
      .use(router)

    app.use(
      createAuth0({
        domain: '{yourDomain}',
        clientId: '{yourClientId}',
        useRefreshTokens: true,
        useRefreshTokensFallback: false,
        authorizationParams: {
          redirect_uri,
        },
      })
    )

    router.isReady().then(() => {
      app.mount('#app')
    })
    ```

    <Info>
      * **`useRefreshTokens: true`** — モバイルブラウザーではサードパーティ Cookie がブロックされるため、iframe ベースのサイレント認証は機能しません。リフレッシュトークンは `/oauth/token` エンドポイントを直接呼び出します。
      * **`useRefreshTokensFallback: false`** — モバイルでは利用できない iframe ベースのフォールバックを SDK が試行しないようにします。
      * **`router.isReady().then(...)`** — SDK が OAuth コールバックを処理する前に Vue Router が完全に初期化されていることを確認し、競合状態を防ぎます。
    </Info>

    <Warning>
      アプリケーションを閉じて再度開いた後も認証状態を維持するには、`cacheLocation` を `localstorage` に設定することを検討してください。ただし、[localstorage にトークンを保存するリスク](https://auth0.com/docs/libraries/auth0-single-page-app-sdk#change-storage-options)がある点に注意してください。また、Capacitor アプリではデータが予期せず復元される可能性があるため、localstorage は **一時的** なものとして扱う必要があります。[Capacitor ドキュメントのストレージに関するガイダンス](https://capacitorjs.com/docs/guides/storage#why-cant-i-just-use-localstorage-or-indexeddb)も参照してください。
    </Warning>
  </Step>

  <Step title="認証コンポーネントを作成" stepNumber={5}>
    コンポーネントファイルを作成する

    <CodeGroup>
      ```shellscript Mac/Linux theme={null}
      mkdir -p src/components && touch src/components/LoginButton.vue && touch src/components/LogoutButton.vue && touch src/components/UserProfile.vue
      ```

      ```powershell Windows theme={null}
      New-Item -ItemType Directory -Force -Path src/components
      New-Item -ItemType File -Path src/components/LoginButton.vue
      New-Item -ItemType File -Path src/components/LogoutButton.vue
      New-Item -ItemType File -Path src/components/UserProfile.vue
      ```
    </CodeGroup>

    以下のコードを新しいコンポーネントに追加し、既存の`App.vue`と`HomePage.vue`を更新してください。

    <AuthCodeGroup>
      ```vue src/components/LoginButton.vue lines theme={null}
      <template>
        <ion-button @click="login" expand="block">Log in</ion-button>
      </template>

      <script setup lang="ts">
      import { useAuth0 } from '@auth0/auth0-vue'
      import { Browser } from '@capacitor/browser'
      import { IonButton } from '@ionic/vue'

      const { loginWithRedirect } = useAuth0()

      const login = async () => {
        await loginWithRedirect({
          openUrl: (url: string) =>
            Browser.open({ url, windowName: '_self' }),
        })
      }
      </script>
      ```

      ```vue src/components/LogoutButton.vue lines theme={null}
      <template>
        <ion-button @click="onLogout" expand="block" color="danger">
          Log out
        </ion-button>
      </template>

      <script setup lang="ts">
      import { useAuth0 } from '@auth0/auth0-vue'
      import { Browser } from '@capacitor/browser'
      import { IonButton } from '@ionic/vue'
      import config from '../../capacitor.config'

      const returnTo = `${config.appId}://{yourDomain}/capacitor/${config.appId}/callback`

      const { logout } = useAuth0()

      const onLogout = async () => {
        await logout({
          logoutParams: { returnTo },
          openUrl: (url: string) =>
            Browser.open({ url, windowName: '_self' }),
        })
      }
      </script>
      ```

      ```vue src/components/UserProfile.vue expandable lines theme={null}
      <template>
        <div v-if="isLoading" class="ion-text-center">
          <p>Loading profile...</p>
        </div>
        <div v-else-if="isAuthenticated && user" class="ion-text-center">
          <ion-avatar style="margin: 0 auto; width: 80px; height: 80px;">
            <img :src="user.picture" :alt="user.name" />
          </ion-avatar>
          <h2>{{ user.name }}</h2>
          <p>{{ user.email }}</p>
        </div>
      </template>

      <script setup lang="ts">
      import { useAuth0 } from '@auth0/auth0-vue'
      import { IonAvatar } from '@ionic/vue'

      const { user, isAuthenticated, isLoading } = useAuth0()
      </script>
      ```

      ```vue src/views/HomePage.vue expandable lines theme={null}
      <template>
        <ion-page>
          <ion-header>
            <ion-toolbar>
              <ion-title>Auth0 + Ionic Vue</ion-title>
            </ion-toolbar>
          </ion-header>

          <ion-content class="ion-padding ion-text-center">
            <div v-if="isLoading">
              <ion-spinner />
              <p>Loading...</p>
            </div>

            <div v-else-if="error">
              <h2>Oops!</h2>
              <p>{{ error.message }}</p>
            </div>

            <div v-else-if="isAuthenticated">
              <div style="margin-bottom: 1rem;">
                <UserProfile />
              </div>
              <LogoutButton />
            </div>

            <div v-else>
              <h2>Welcome</h2>
              <p>Get started by signing in to your account</p>
              <LoginButton />
            </div>
          </ion-content>
        </ion-page>
      </template>

      <script setup lang="ts">
      import {
        IonPage,
        IonHeader,
        IonToolbar,
        IonTitle,
        IonContent,
        IonSpinner,
      } from '@ionic/vue'
      import { useAuth0 } from '@auth0/auth0-vue'
      import LoginButton from '../components/LoginButton.vue'
      import LogoutButton from '../components/LogoutButton.vue'
      import UserProfile from '../components/UserProfile.vue'

      const { isAuthenticated, isLoading, error } = useAuth0()
      </script>
      ```

      ```vue src/App.vue expandable lines theme={null}
      <template>
        <ion-app>
          <ion-router-outlet />
        </ion-app>
      </template>

      <script setup lang="ts">
      import { IonApp, IonRouterOutlet } from '@ionic/vue'
      import { useAuth0 } from '@auth0/auth0-vue'
      import { App as CapApp } from '@capacitor/app'
      import { Browser } from '@capacitor/browser'

      const { handleRedirectCallback } = useAuth0()

      CapApp.addListener('appUrlOpen', async ({ url }) => {
        if (url.includes('state') && (url.includes('code') || url.includes('error'))) {
          await handleRedirectCallback(url)
        }
        await Browser.close()
      })
      </script>
      ```
    </AuthCodeGroup>
  </Step>

  <Step title="アプリを実行する" stepNumber={6}>
    まずはブラウザーでテストしてください:

    ```shellscript theme={null}
    ionic serve
    ```

    <Warning>
      ブラウザで `ionic serve` を実行する場合、ブラウザではカスタム URL スキームを処理できないため、カスタム URL スキームのリダイレクトは機能しません。ブラウザでテストする場合は、一時的に `src/main.ts` の `redirect_uri` を `http://localhost:8100` に変更し、`http://localhost:8100` を Auth0 アプリの Dashboard にある **Allowed Callback URLs** と **Allowed Logout URLs** に追加してください。ネイティブ向けにビルドする前に、必ずこの変更を元に戻してください。
    </Warning>

    デバイスまたはシミュレータで実行するには、まずネイティブプラットフォームを追加します。

    ```shellscript theme={null}
    npx cap add ios
    npx cap add android
    ```

    次に、ビルド、同期、実行を行います。

    <CodeGroup>
      ```shellscript iOS theme={null}
      ionic build && npx cap sync && npx cap run ios
      ```

      ```shellscript Android theme={null}
      ionic build && npx cap sync && npx cap run android
      ```
    </CodeGroup>

    <Info>
      実行するには、事前に `npx cap add` でネイティブプラットフォームを追加しておく必要があります。これは各プラットフォームごとに一度だけ行えばかまいません。先に、お使いのプラットフォームの[カスタム URL スキーム](#custom-url-scheme-setup)を登録しておいてください。
    </Info>
  </Step>
</Steps>

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

  これで、Ionic Vue アプリケーションで、ログイン、ログアウト、ユーザープロフィール情報を含む Auth0 のログイン機能が完全に動作するようになっているはずです。
</Check>

***

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

<Accordion title="カスタムURLスキームの設定">
  認証後に OAuth コールバックをアプリに戻せるよう、OS がルーティングできるように、アプリのカスタム URL スキームを登録します。

  ### iOS

  `ios/App/App/Info.plist` にカスタム URL スキームを登録します。

  ```xml theme={null}
  <key>CFBundleURLTypes</key>
  <array>
    <dict>
      <key>CFBundleURLSchemes</key>
      <array>
        <string>YOUR_PACKAGE_ID</string>
      </array>
    </dict>
  </array>
  ```

  `YOUR_PACKAGE_ID` は、`capacitor.config.ts` の `appId` に置き換えてください。詳細は、[Defining a Custom URL Scheme](https://developer.apple.com/documentation/xcode/defining-a-custom-url-scheme-for-your-app) を参照してください。

  ### Android

  メインの `<activity>` タグ内にある `android/app/src/main/AndroidManifest.xml` に intent filter を追加します。

  ```xml theme={null}
  <intent-filter>
    <action android:name="android.intent.action.VIEW" />
    <category android:name="android.intent.category.DEFAULT" />
    <category android:name="android.intent.category.BROWSABLE" />
    <data android:scheme="YOUR_PACKAGE_ID" />
  </intent-filter>
  ```

  詳細は、[Create Deep Links to App Content](https://developer.android.com/training/app-links/deep-linking) を参照してください。

  <Tip>
    ネイティブプロジェクトのファイルを変更した後は、変更を確実に反映するために `npx cap sync` を実行してください。
  </Tip>
</Accordion>

<Accordion title="Vue Router でルートを保護する">
  認証が必要なルートを保護するには、Auth0 Vue SDK に組み込まれている `authGuard` を使用します。

  ```typescript src/router/index.ts theme={null}
  import { createRouter, createWebHistory } from '@ionic/vue-router'
  import { authGuard } from '@auth0/auth0-vue'
  import HomePage from '../views/HomePage.vue'
  import ProfilePage from '../views/ProfilePage.vue'

  const routes = [
    {
      path: '/',
      name: 'Home',
      component: HomePage,
    },
    {
      path: '/profile',
      name: 'Profile',
      component: ProfilePage,
      beforeEnter: authGuard,
    },
  ]

  const router = createRouter({
    history: createWebHistory(import.meta.env.BASE_URL),
    routes,
  })

  export default router
  ```

  `authGuard` は、未認証のユーザーを Auth0 の Universal Login ページに自動的にリダイレクトします。ログイン後は、最初に要求したルートに戻ります。
</Accordion>

<Accordion title="保護されたAPIの呼び出し">
  API 用のアクセストークンをリクエストするには、`createAuth0` の設定で `audience` パラメーターを構成します。

  ```typescript src/main.ts theme={null}
  app.use(
    createAuth0({
      domain: '{yourDomain}',
      clientId: '{yourClientId}',
      useRefreshTokens: true,
      useRefreshTokensFallback: false,
      authorizationParams: {
        redirect_uri,
        audience: 'YOUR_API_IDENTIFIER',
      },
    })
  )
  ```

  次に、コンポーネントで `getAccessTokenSilently` を使用してトークンを取得し、認証済みの API 呼び出しを行います。

  ```vue src/components/ApiCall.vue theme={null}
  <template>
    <ion-button @click="callApi" :disabled="loading">
      {{ loading ? 'Calling...' : 'Call Protected API' }}
    </ion-button>
    <pre v-if="response">{{ JSON.stringify(response, null, 2) }}</pre>
  </template>

  <script setup lang="ts">
  import { ref } from 'vue'
  import { useAuth0 } from '@auth0/auth0-vue'
  import { IonButton } from '@ionic/vue'

  const { getAccessTokenSilently } = useAuth0()
  const response = ref(null)
  const loading = ref(false)

  const callApi = async () => {
    try {
      loading.value = true
      const token = await getAccessTokenSilently()

      const res = await fetch('https://your-api.com/endpoint', {
        headers: {
          Authorization: `Bearer ${token}`,
        },
      })

      response.value = await res.json()
    } catch (error) {
      console.error('API call failed:', error)
    } finally {
      loading.value = false
    }
  }
  </script>
  ```

  <Tip>
    `getAccessTokenSilently` メソッドは、キャッシュからトークンを取得するか、必要に応じてリフレッシュトークンを使って自動的に更新します。
  </Tip>
</Accordion>

<Accordion title="defineComponent パターンを使用する">
  `<script setup>` ではなく、明示的な `defineComponent` パターンを使いたい場合は、LoginButton コンポーネントは次のようになります。

  ```vue src/components/LoginButton.vue theme={null}
  <template>
    <ion-button @click="login" expand="block">Log in</ion-button>
  </template>

  <script lang="ts">
  import { defineComponent } from 'vue'
  import { useAuth0 } from '@auth0/auth0-vue'
  import { Browser } from '@capacitor/browser'
  import { IonButton } from '@ionic/vue'

  export default defineComponent({
    components: { IonButton },
    setup() {
      const { loginWithRedirect } = useAuth0()

      const login = async () => {
        await loginWithRedirect({
          openUrl: (url: string) =>
            Browser.open({ url, windowName: '_self' }),
        })
      }

      return { login }
    },
  })
  </script>
  ```

  `defineComponent` パターンでは、子コンポーネントを明示的に登録し、`setup()` 関数から値を返す必要があります。どちらのパターンも Auth0 Vue SDK で完全にサポートされています。
</Accordion>

<Accordion title="よくある問題と解決策">
  ### Callback URL の不一致エラー

  **解決策:** Auth0 Dashboard の callback URL が、アプリケーションで生成される URL と完全に一致していることを確認してください。`YOUR_PACKAGE_ID` が `capacitor.config.ts` の `appId` フィールドと一致していることも確認してください。

  ### 「PKCE not allowed」エラー

  **修正方法:**

  1. Auth0 Dashboard > Applications > Your Application に移動します
  2. **Application Type** を **Native** に変更します
  3. **Token Endpoint Authentication Method** を `None` に設定します
  4. 変更を保存して、再度試します

  ### iOS で SSO が機能しない

  Capacitor の Browser プラグインは `SFSafariViewController` を使用しており、iOS 11 以降では Safari と Cookie を共有しません。SSO が必要な場合は、[ASWebAuthenticationSession](https://developer.apple.com/documentation/authenticationservices/aswebauthenticationsession) を使用する互換性のあるプラグインを使用してください。

  ### ログインは成功するが、アプリを再起動するとユーザーが未認証のままになる

  アプリの再起動後もトークンを保持するには、`createAuth0` の設定で `cacheLocation: 'localstorage'` を有効にしてください。[セキュリティ上の影響](https://auth0.com/docs/libraries/auth0-single-page-app-sdk#change-storage-options)がある点に注意してください。SDK は、より安全に保存するための[カスタムキャッシュ実装](https://github.com/auth0/auth0-spa-js/blob/master/EXAMPLES.md#creating-a-custom-cache)もサポートしています。

  ### ログイン後にブラウザーが閉じない

  `App.vue` コンポーネントの `appUrlOpen` イベントリスナー内で `Browser.close()` を呼び出していることを確認してください。Android では `Browser.close()` は no-op のため、ブラウザーは自動的に閉じます。

  ### ログインページがデバイスの既定のブラウザーアプリで開く

  ログインページがアプリ内ブラウザーのオーバーレイではなく Safari や Chrome で開く場合は、`openUrl` コールバックを `loginWithRedirect` と `logout` に渡していることを確認してください。これを渡さないと、SDK はデフォルトで `window.location.href` を使用するため、アプリ全体が別の場所に遷移します。
</Accordion>
