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

> Auth0 SDKが複数のカスタムドメインで動作するように設定する方法を説明します。

# 複数のカスタムドメイン向けSDK設定

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>;
};

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) + "*****MASKED*****";
          }
          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>;
};

[複数のカスタムドメイン](/docs/ja-jp/customize/custom-domains/multiple-custom-domains)を使用する場合は、認証に適したカスタムドメインを使用するように Auth0 SDK を設定する必要があります。このガイドでは、さまざまなプラットフォームやシナリオにおける SDK の設定について説明します。

<div id="key-concepts">
  ## 基本概念
</div>

<div id="domain-parameter">
  ### ドメインパラメーター
</div>

すべてのAuth0 SDKでは、認証に使用するAuth0ドメインを指定する `domain` パラメーターが必要です。カスタムドメインを使用する場合は、このパラメーターにAuth0の正規ドメインではなく、カスタムドメインを設定します。

**カスタムドメインを使用しない場合:**

```
domain: 'tenant.auth0.com'
```

**カスタムドメインの場合:**

```
domain: 'login.example.com'
```

<div id="token-issuer">
  ### トークン発行者
</div>

カスタムドメインを使用する場合、トークンの`iss` (発行者) クレームには、カスタムドメインが設定されます：

```json theme={null}
{
  "iss": "https://login.example.com/",
  "sub": "auth0|123456",
  "aud": "your-client-id"
}
```

トークンの検証で、カスタムドメインを有効な発行元として受け入れるように設定する必要があります。

<div id="authentication-sdks">
  ## 認証 SDK
</div>

<Callout icon="file-lines" color="#0EA5E9" iconType="regular">
  MCD を使用する場合、すべてのカスタムドメインの提供と検証は顧客の責任となります。SDK でドメインリゾルバー関数を使用してテナントのカスタムドメインを解決するよう設定する場合は、解決されたすべてのドメインが信頼できるものであることを確認する責任があります。ドメインリゾルバーの設定を誤ると、Relying Party 側で認証が回避されたり、アプリケーションがサーバーサイドリクエストフォージェリにさらされたりするおそれがあります。ドメインとプロキシサーバーを適切に設定しないと、重大なセキュリティ脆弱性が生じる可能性があり、Okta はそれに対して責任を負いません。
</Callout>

<div id="auth0-spa-sdk-javascript">
  ### Auth0 SPA SDK (JavaScript)
</div>

[Auth0 SPA SDK](https://github.com/auth0/auth0-spa-js) を使用するシングルページアプリケーションの場合:

<AuthCodeGroup>
  ```javascript 静的設定 theme={null}
  import { createAuth0Client } from '@auth0/auth0-spa-js';

  const auth0 = await createAuth0Client({
    domain: 'login.example.com',  // ご利用のカスタムドメイン
    clientId: 'YOUR_CLIENT_ID',
    authorizationParams: {
      redirect_uri: window.location.origin
    }
  });
  ```

  ```javascript 動的設定 theme={null}
  import { createAuth0Client } from '@auth0/auth0-spa-js';

  // アプリケーションのコンテキストに応じてドメインを判定
  function getAuth0Domain() {
    const hostname = window.location.hostname;

    // アプリケーションのドメインを Auth0 のカスタムドメインにマッピング
    if (hostname.includes('app1.example.com')) {
      return 'login.brand1.com';
    } else if (hostname.includes('app2.example.com')) {
      return 'login.brand2.com';
    }

    // デフォルトのカスタムドメイン
    return 'login.example.com';
  }

  const auth0 = await createAuth0Client({
    domain: getAuth0Domain(),
    clientId: 'YOUR_CLIENT_ID',
    authorizationParams: {
      redirect_uri: window.location.origin
    }
  });
  ```

  ```javascript 環境変数 theme={null}
  import { createAuth0Client } from '@auth0/auth0-spa-js';

  const auth0 = await createAuth0Client({
    domain: process.env.REACT_APP_AUTH0_DOMAIN,  // .env ファイルから取得
    clientId: process.env.REACT_APP_AUTH0_CLIENT_ID,
    authorizationParams: {
      redirect_uri: window.location.origin
    }
  });
  ```
</AuthCodeGroup>

<div id="nextjs">
  ### Next.js
</div>

[Auth0 Next.js SDK](https://github.com/auth0/nextjs-auth0) (v4+) を使用する Next.js アプリケーションの場合:

<AuthCodeGroup>
  ```typescript 静的構成 theme={null}
  import { Auth0Client } from '@auth0/nextjs-auth0/server';

  const auth0 = new Auth0Client({
    domain: 'login.example.com',        // 使用するカスタムドメイン
    clientId: process.env.AUTH0_CLIENT_ID,
    clientSecret: process.env.AUTH0_CLIENT_SECRET,
    appBaseUrl: process.env.APP_BASE_URL,
    secret: process.env.AUTH0_SECRET
  });
  ```

  ```typescript 動的構成（マルチブランド） theme={null}
  import { Auth0Client } from '@auth0/nextjs-auth0/server';

  const auth0 = new Auth0Client({
    domain: ({ headers }) => {
      // アプリケーションのホストを Auth0 のカスタムドメインに対応付ける
      const host = headers.get('host') || '';

      if (host.includes('brand1.example.com')) {
        return 'login.brand1.com';
      } else if (host.includes('brand2.example.com')) {
        return 'login.brand2.com';
      }

      return 'login.example.com';  // デフォルトドメイン
    },
    clientId: process.env.AUTH0_CLIENT_ID,
    clientSecret: process.env.AUTH0_CLIENT_SECRET,
    appBaseUrl: process.env.APP_BASE_URL,
    secret: process.env.AUTH0_SECRET
  });
  ```

  ```typescript 動的構成（B2B SaaS） theme={null}
  import { Auth0Client } from '@auth0/nextjs-auth0/server';

  // テナント識別子をカスタムドメインに対応付ける（データベース、キャッシュなどから）
  async function resolveTenantDomain(host: string): Promise<string> {
    // サブドメインからテナント識別子を抽出する
    const subdomain = host.split('.')[0];

    // このテナントのカスタムドメインを取得する（例: データベースから）
    const tenantConfig = await getTenantConfig(subdomain);

    return tenantConfig?.customDomain || 'login.example.com';
  }

  const auth0 = new Auth0Client({
    domain: async ({ headers }) => {
      const host = headers.get('host') || '';
      return resolveTenantDomain(host);
    },
    clientId: process.env.AUTH0_CLIENT_ID,
    clientSecret: process.env.AUTH0_CLIENT_SECRET,
    appBaseUrl: process.env.APP_BASE_URL,
    secret: process.env.AUTH0_SECRET
  });
  ```

  ```bash 環境変数 theme={null}
  # .env.local

  # Auth0 の設定（単一テナントで複数のカスタムドメインを使用）
  AUTH0_DOMAIN=login.example.com           # デフォルトまたはフォールバック用のカスタムドメイン
  AUTH0_CLIENT_ID=your_client_id
  AUTH0_CLIENT_SECRET=your_client_secret
  APP_BASE_URL=http://localhost:3000
  AUTH0_SECRET=your-long-random-secret
  ```
</AuthCodeGroup>

**Next.js で MCD を使用する際の重要なポイント:**

* **単一の Auth0 テナント、複数のドメイン**: すべてのカスタムドメインは同じ Auth0 テナントに属しているため、同じ `clientId` と `clientSecret` を共有します。
* **DomainResolver 関数**: `domain` パラメータには、`(config: { headers: Headers; url?: URL }) => Promise<string> | string` という関数を渡せます。これにより、受信したリクエストヘッダーに基づいて、リクエストごとに動的にドメインを解決できます。
* **インスタンスのキャッシュ**: SDK はパフォーマンス向上のため、`Auth0Client` インスタンスをドメインごとに上限付きの LRU キャッシュ (最大 100 エントリ) で自動的にキャッシュします。
* **セッションの分離**: あるカスタムドメイン経由で作成されたセッションはそのドメインに限定され、別のドメインのセッションと相互に使い回すことはできません。
* **URL パラメータ**: リゾルバー内の `url` パラメータは、Server Components と Server Actions では `undefined` です。利用できるのは middleware または API ルート内のみです。
* **Discovery キャッシュの調整**: `discoveryCache` オプションを使用して OIDC メタデータのキャッシュを設定します。
  ```typescript theme={null}
  const auth0 = new Auth0Client({
    // ... 他の設定
    discoveryCache: {
      ttl: 600,      // 10 分間キャッシュ（デフォルト）
      maxEntries: 100  // キャッシュする issuer の最大数（デフォルト: 100、LRU による削除）
    }
  });
  ```

<div id="auth0-react-sdk">
  ### Auth0 React SDK
</div>

React アプリケーションで [Auth0 React SDK](https://github.com/auth0/auth0-react) を使用する場合:

```jsx theme={null}
import { Auth0Provider } from '@auth0/auth0-react';

function App() {
  return (
    <Auth0Provider
      domain="login.example.com"
      clientId="YOUR_CLIENT_ID"
      authorizationParams={{
        redirect_uri: window.location.origin
      }}
    >
      <MyApp />
    </Auth0Provider>
  );
}
```

複数ドメインの場合:

```jsx theme={null}
import { Auth0Provider } from '@auth0/auth0-react';

function App() {
  // 環境またはコンテキストに基づいてカスタムドメインを決定する
  const auth0Domain = process.env.REACT_APP_AUTH0_DOMAIN || 'login.example.com';

  return (
    <Auth0Provider
      domain={auth0Domain}
      clientId={process.env.REACT_APP_AUTH0_CLIENT_ID}
      authorizationParams={{
        redirect_uri: window.location.origin
      }}
    >
      <MyApp />
    </Auth0Provider>
  );
}
```

<div id="auth0js">
  ### Auth0.js
</div>

[Auth0.js](https://github.com/auth0/auth0.js) を使用するアプリケーション向け:

```javascript theme={null}
const webAuth = new auth0.WebAuth({
  domain: 'login.example.com',
  clientID: 'YOUR_CLIENT_ID',
  redirectUri: window.location.origin + '/callback',
  responseType: 'code',
  scope: 'openid profile email'
});

// ログインを開始
webAuth.authorize();
```

<div id="nodejs-express">
  ### Node.js (Express)
</div>

[express-openid-connect](https://github.com/auth0/express-openid-connect) を使用している Node.js アプリケーションの場合:

```javascript theme={null}
const { auth } = require('express-openid-connect');

app.use(
  auth({
    authRequired: false,
    auth0Logout: true,
    issuerBaseURL: 'https://login.example.com',  // カスタムドメイン
    baseURL: 'http://localhost:3000',
    clientID: 'YOUR_CLIENT_ID',
    secret: 'YOUR_CLIENT_SECRET'
  })
);
```

マルチテナント環境の場合：

```javascript theme={null}
const { auth } = require('express-openid-connect');

// リクエストごとにカスタムドメインを決定するミドルウェア
app.use((req, res, next) => {
  // サブドメイン、パス、またはヘッダーからテナント識別子を取得する
  const tenant = req.subdomains[0] || 'default';

  // テナントをカスタムドメインにマッピングする
  const domainMap = {
    'customer1': 'login.customer1.com',
    'customer2': 'login.customer2.com',
    'default': 'login.example.com'
  };

  req.auth0Domain = domainMap[tenant] || domainMap.default;
  next();
});

// 動的な認証設定
app.use((req, res, next) => {
  auth({
    authRequired: false,
    auth0Logout: true,
    issuerBaseURL: `https://${req.auth0Domain}`,
    baseURL: req.protocol + '://' + req.get('host'),
    clientID: process.env.AUTH0_CLIENT_ID,
    secret: process.env.AUTH0_CLIENT_SECRET
  })(req, res, next);
});
```

<div id="mobile-sdks">
  ### モバイル SDK
</div>

<div id="ios-swift">
  #### iOS (Swift)
</div>

[Auth0.swift](https://github.com/auth0/Auth0.swift) を使用する場合：

```swift theme={null}
import Auth0

let auth0 = Auth0
    .webAuth(clientId: "YOUR_CLIENT_ID", domain: "login.example.com")

auth0
    .scope("openid profile email")
    .start { result in
        switch result {
        case .success(let credentials):
            print("Obtained credentials: \(credentials)")
        case .failure(let error):
            print("Failed with: \(error)")
        }
    }
```

ドメインを動的に選択する場合:

```swift theme={null}
import Auth0

class AuthService {
    private let clientId = "YOUR_CLIENT_ID"

    func getAuth0Domain() -> String {
        // アプリの設定に基づいてドメインを決定する
        if let savedDomain = UserDefaults.standard.string(forKey: "auth0Domain") {
            return savedDomain
        }
        return "login.example.com" // デフォルト
    }

    func login(completion: @escaping (Result<Credentials, Error>) -> Void) {
        Auth0
            .webAuth(clientId: clientId, domain: getAuth0Domain())
            .scope("openid profile email")
            .start { result in
                completion(result)
            }
    }
}
```

<div id="android-kotlin">
  #### Android (Kotlin)
</div>

[Auth0.Android](https://github.com/auth0/Auth0.Android) を使用するには:

```kotlin theme={null}
import com.auth0.android.Auth0
import com.auth0.android.authentication.AuthenticationAPIClient
import com.auth0.android.provider.WebAuthProvider
import com.auth0.android.result.Credentials

val account = Auth0.getInstance(
    "YOUR_CLIENT_ID",
    "login.example.com"  // カスタムドメイン
)

WebAuthProvider.login(account)
    .withScheme("demo")
    .withScope("openid profile email")
    .start(this, object : Callback<Credentials, AuthenticationException> {
        override fun onSuccess(credentials: Credentials) {
            // 成功時の処理
        }

        override fun onFailure(exception: AuthenticationException) {
            // 失敗時の処理
        }
    })
```

複数ドメイン対応の場合:

```kotlin theme={null}
class AuthManager(private val context: Context) {
    private val clientId = "YOUR_CLIENT_ID"

    private fun getAuth0Domain(): String {
        // 共有プリファレンスまたはアプリ設定から取得
        val prefs = context.getSharedPreferences("auth", Context.MODE_PRIVATE)
        return prefs.getString("auth0_domain", "login.example.com") ?: "login.example.com"
    }

    fun login(callback: Callback<Credentials, AuthenticationException>) {
        val account = Auth0.getInstance(clientId, getAuth0Domain())

        WebAuthProvider.login(account)
            .withScheme("demo")
            .withScope("openid profile email")
            .start(context, callback)
    }
}
```

<div id="react-native">
  #### React Native
</div>

[react-native-auth0](https://github.com/auth0/react-native-auth0) を使用する場合:

```javascript theme={null}
import Auth0 from 'react-native-auth0';

const auth0 = new Auth0({
  domain: 'login.example.com',
  clientId: 'YOUR_CLIENT_ID'
});

// ログイン
auth0.webAuth
  .authorize({
    scope: 'openid profile email'
  })
  .then(credentials => {
    console.log('Logged in!');
  })
  .catch(error => {
    console.log(error);
  });
```

<div id="flutter">
  ### Flutter
</div>

[flutter\_auth0](https://github.com/auth0/auth0-flutter) を使用する場合：

```dart theme={null}
import 'package:auth0_flutter/auth0_flutter.dart';

final auth0 = Auth0(
  'login.example.com',
  'YOUR_CLIENT_ID'
);

// ログイン
try {
  final credentials = await auth0.webAuthentication().login();
  print('Logged in successfully');
} catch (e) {
  print('Login failed: $e');
}
```

<div id="management-sdks">
  ## Management SDKs
</div>

Management SDKs は、Auth0 Management API を操作するために使用されます。カスタムドメインを使用する場合は、`auth0-custom-domain` ヘッダーを含めるか、[デフォルトドメイン](/docs/ja-jp/customize/custom-domains/multiple-custom-domains/default-domain) を使用する必要があることがあります。

<div id="nodejs-management-sdk">
  ### Node.js 管理用 SDK
</div>

```javascript theme={null}
import { ManagementClient, CustomDomainHeader } from "auth0";

// グローバルカスタムドメイン（ホワイトリストに登録されたすべてのリクエストで送信）
const management = new ManagementClient({
  domain: 'tenant.auth0.com',
  clientId: 'YOUR_M2M_CLIENT_ID',
  clientSecret: 'YOUR_M2M_CLIENT_SECRET',
  withCustomDomainHeader: 'login.example.com', 
});

// ユーザー一覧取得（ホワイトリストに登録されたエンドポイント - ヘッダーは自動送信）
const users = await management.users.getAll();

// リクエスト単位の上書き（グローバル設定より優先）
const reqOptions = {
  ...CustomDomainHeader("specific-user-request.exampleco.com"),
};
const response = await management.users.getAll({}, reqOptions);
```

<div id="python-management-sdk">
  ### Python向け管理SDK
</div>

```python theme={null}
from auth0.management import ManagementClient, CustomDomainHeader

# グローバルカスタムドメイン（ホワイトリストに登録されたすべてのリクエストで送信）
client = ManagementClient(
    domain='tenant.auth0.com',
    client_id='YOUR_M2M_CLIENT_ID',
    client_secret='YOUR_M2M_CLIENT_SECRET',
    custom_domain='login.example.com',
)

# ユーザー一覧取得（ホワイトリストに登録されたエンドポイント - ヘッダーは自動的に送信）
users = client.users.list()

# リクエストごとの上書き（グローバル設定より優先される）
client.users.create(
    connection='Username-Password-Authentication',
    email='user@example.com',
    password='SecurePass123!',
    request_options=CustomDomainHeader('login.brand2.com'),
)
```

<div id="go-management-sdk">
  ### Go向け管理SDK
</div>

```go theme={null}
import (
    "context"
    management "github.com/auth0/go-auth0/v2/management/client"
    "github.com/auth0/go-auth0/v2/management/option"
)

// クライアントレベル: ホワイトリストに登録されたエンドポイントにカスタムドメインヘッダーを自動適用
mgmt, err := management.New(
    "{yourDomain}",
    option.WithClientCredentials("{yourClientId}", "{yourClientSecret}"),
    option.WithCustomDomainHeader("login.example.com"),
)
if err != nil {
    // エラー処理
}

// ユーザー一覧取得（ホワイトリストに登録されたエンドポイント - ヘッダーは自動送信）
userList, err := mgmt.Users.List(context.Background(), nil)

// リクエストごとの上書き（クライアントレベルより優先）
userList, err := mgmt.Users.List(
    context.Background(),
    nil,
    option.WithCustomDomainHeader("specific-request.exampleco.com"),
)
```

<div id="token-validation">
  ## トークンの検証
</div>

カスタムドメインを使用する場合は、トークンの検証で発行者としてカスタムドメインを受け入れるよう更新してください。

<div id="nodejs-express">
  ### Node.js (Express)
</div>

[express-jwt](https://github.com/auth0/express-jwt) または [jose](https://github.com/panva/jose) を使用する場合:

```javascript theme={null}
const { expressjwt } = require('express-jwt');
const { expressJwtSecret } = require('jwks-rsa');

app.use(
  expressjwt({
    secret: expressJwtSecret({
      cache: true,
      rateLimit: true,
      jwksUri: 'https://login.example.com/.well-known/jwks.json'  // カスタムドメイン
    }),
    audience: 'YOUR_API_IDENTIFIER',
    issuer: 'https://login.example.com/',  // 発行者としてのカスタムドメイン
    algorithms: ['RS256']
  })
);
```

複数のカスタムドメインを使用する場合:

```javascript theme={null}
const validIssuers = [
  'https://login.brand1.com/',
  'https://login.brand2.com/',
  'https://login.example.com/'
];

app.use(
  expressjwt({
    secret: expressJwtSecret({
      cache: true,
      rateLimit: true,
      jwksRequestsPerMinute: 5,
      jwksUri: (req) => {
        // tokenから発行者を抽出してJWKS URIを決定する
        const token = req.headers.authorization?.split(' ')[1];
        if (token) {
          const payload = JSON.parse(Buffer.from(token.split('.')[1], 'base64').toString());
          return `${payload.iss}.well-known/jwks.json`;
        }
        return 'https://login.example.com/.well-known/jwks.json';
      }
    }),
    audience: 'YOUR_API_IDENTIFIER',
    issuer: validIssuers,  // 複数の発行者を許可する
    algorithms: ['RS256']
  })
);
```

<div id="python-flask">
  ### Python (Flask)
</div>

[python-jose](https://github.com/mpdavis/python-jose) を使用する場合：

```python theme={null}
from jose import jwt
from functools import wraps
from flask import request, jsonify

def get_token_auth_header():
    auth = request.headers.get('Authorization', None)
    if not auth:
        raise Exception('Authorization header is expected')

    parts = auth.split()
    if parts[0].lower() != 'bearer':
        raise Exception('Authorization header must start with Bearer')
    elif len(parts) == 1:
        raise Exception('Token not found')
    elif len(parts) > 2:
        raise Exception('Authorization header must be Bearer token')

    return parts[1]

def requires_auth(f):
    @wraps(f)
    def decorated(*args, **kwargs):
        token = get_token_auth_header()

        # 複数のカスタムドメインをサポート
        valid_issuers = [
            'https://login.brand1.com/',
            'https://login.brand2.com/',
            'https://login.example.com/'
        ]

        try:
            # カスタムドメインからJWKSを取得
            unverified = jwt.get_unverified_header(token)
            issuer = jwt.get_unverified_claims(token)['iss']

            if issuer not in valid_issuers:
                raise Exception('Invalid issuer')

            jwks_uri = f"{issuer}.well-known/jwks.json"
            jwks = requests.get(jwks_uri).json()

            payload = jwt.decode(
                token,
                jwks,
                algorithms=['RS256'],
                audience='YOUR_API_IDENTIFIER',
                issuer=valid_issuers
            )
        except Exception as e:
            return jsonify({'error': str(e)}), 401

        return f(*args, **kwargs)

    return decorated
```

<div id="java-spring-boot">
  ### Java (Spring Boot)
</div>

Spring Security を使用する場合：

```java theme={null}
@Configuration
@EnableWebSecurity
public class SecurityConfig extends WebSecurityConfigurerAdapter {

    @Value("${auth0.audience}")
    private String audience;

    @Value("${auth0.custom-domain}")
    private String customDomain;

    @Override
    protected void configure(HttpSecurity http) throws Exception {
        http
            .authorizeRequests()
            .mvcMatchers("/api/public").permitAll()
            .mvcMatchers("/api/private").authenticated()
            .and()
            .oauth2ResourceServer()
            .jwt()
            .decoder(jwtDecoder());
    }

    @Bean
    JwtDecoder jwtDecoder() {
        String issuerUri = "https://" + customDomain + "/";

        NimbusJwtDecoder jwtDecoder = JwtDecoders.fromIssuerLocation(issuerUri);

        // audienceを検証する
        OAuth2TokenValidator<Jwt> audienceValidator = new AudienceValidator(audience);
        OAuth2TokenValidator<Jwt> withIssuer = JwtValidators.createDefaultWithIssuer(issuerUri);
        OAuth2TokenValidator<Jwt> withAudience = new DelegatingOAuth2TokenValidator<>(withIssuer, audienceValidator);

        jwtDecoder.setJwtValidator(withAudience);

        return jwtDecoder;
    }
}
```

<div id="environment-specific-configuration">
  ## 環境ごとの設定
</div>

環境変数を使用して、環境ごとにカスタムドメインを管理します。

<div id="env-file-structure">
  ### .env ファイルの構造
</div>

```bash theme={null}
# 開発環境
AUTH0_DOMAIN=dev.example.com
AUTH0_CLIENT_ID=dev_client_id
AUTH0_CLIENT_SECRET=dev_client_secret

# ステージング環境
# AUTH0_DOMAIN=staging.example.com
# AUTH0_CLIENT_ID=staging_client_id
# AUTH0_CLIENT_SECRET=staging_client_secret

# 本番環境
# AUTH0_DOMAIN=login.example.com
# AUTH0_CLIENT_ID=prod_client_id
# AUTH0_CLIENT_SECRET=prod_client_secret
```

<div id="loading-configuration">
  ### 設定の読み込み
</div>

```javascript theme={null}
require('dotenv').config();

const auth0Config = {
  domain: process.env.AUTH0_DOMAIN,
  clientId: process.env.AUTH0_CLIENT_ID,
  clientSecret: process.env.AUTH0_CLIENT_SECRET
};
```

<div id="troubleshooting">
  ## トラブルシューティング
</div>

<div id="common-issues">
  ### よくある問題
</div>

<table class="table">
  <thead>
    <tr>
      <th><strong>問題</strong></th>
      <th><strong>原因</strong></th>
      <th><strong>解決策</strong></th>
    </tr>
  </thead>

  <tbody>
    <tr>
      <td>発行者 が無効というエラー</td>
      <td>token の検証では正規ドメインを想定しているが、カスタムドメインを受け取っている</td>
      <td>発行者 としてカスタムドメインを受け入れるよう、token の検証を更新する</td>
    </tr>

    <tr>
      <td>JWKS の取得に失敗する</td>
      <td>JWKS URI が正規ドメインを指している</td>
      <td>JWKS URI がカスタムドメインを使用するように更新する: `https://custom-domain/.well-known/jwks.json`</td>
    </tr>

    <tr>
      <td>リダイレクト URI の不一致</td>
      <td>callback URL が設定済みのリダイレクト URI と一致していない</td>
      <td>カスタムドメインの callback URL をアプリケーション設定に追加する</td>
    </tr>

    <tr>
      <td>クロスオリジン エラー (CORS)</td>
      <td>カスタムドメインが許可済みオリジンに含まれていない</td>
      <td>アプリケーション設定の Allowed Web Origins にカスタムドメインを追加する</td>
    </tr>

    <tr>
      <td>Lock の読み込みに失敗する</td>
      <td>`configurationBaseUrl` が指定されていない</td>
      <td>リージョンに対応する CDN URL を指定して `configurationBaseUrl` パラメーターを追加する</td>
    </tr>
  </tbody>
</table>

<div id="best-practices">
  ## ベストプラクティス
</div>

1. **環境変数を使用する**: カスタムドメインは、環境ごとの設定ファイルに保存します
2. **複数の発行者を検証する**: 複数のカスタムドメインを使用する場合は、トークンの検証でそれらすべてを有効な発行者として受け入れるように設定します
3. **コールバックURLを更新する**: すべてのカスタムドメインが、アプリケーション設定のAllowed Callback URLsに追加されていることを確認します
4. **十分にテストする**: 本番環境に移行する前に、各カスタムドメイン経由での認証を十分にテストします
5. **トークン発行者を監視する**: 正しいカスタムドメインが使用されていることを確認するため、トークン内の`iss`クレームを記録し、監視します
6. **ドメインの対応関係を文書化する**: どのアプリケーションがどのカスタムドメインを使用しているかを、わかりやすく文書化します
7. **障害を適切に処理する**: 認証の失敗に対する適切なエラー処理を実装します
8. **JWKSをキャッシュする**: パフォーマンスを向上させ、リクエスト数を減らすために、JWKSデータをキャッシュします

<div id="learn-more">
  ## 詳細はこちら
</div>

* [複数のカスタムドメイン](/docs/ja-jp/customize/custom-domains/multiple-custom-domains)
* [デフォルトのカスタムドメイン](/docs/ja-jp/customize/custom-domains/multiple-custom-domains/default-domain)
* [カスタムドメインを使用する機能を設定する](/docs/ja-jp/customize/custom-domains/configure-features-to-use-custom-domains)
* [Auth0 SDK](/docs/ja-jp/troubleshoot/customer-support/product-support-matrix#auth0-sdks)
