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

# 認可コードフロー

> PKCE を使用した OAuth 2.0 認可コードフローにより、ABBYY Vantage でユーザーを認証し、Vantage API 用のアクセストークンを取得します。

export const VantageRegion = ({children}) => {
  const STORAGE_KEY = "abbyy.vantage.region";
  const REGIONS = [{
    code: "eu",
    label: "Europe"
  }, {
    code: "us",
    label: "North America"
  }, {
    code: "au",
    label: "Australia / Asia-Pacific"
  }];
  const [region, setRegion] = useState("eu");
  const [open, setOpen] = useState(false);
  const containerRef = useRef(null);
  const dropdownRef = useRef(null);
  function safeGetSavedRegion() {
    try {
      const saved = typeof window !== "undefined" ? window.localStorage.getItem(STORAGE_KEY) : null;
      const valid = new Set(REGIONS.map(r => r.code));
      return saved && valid.has(saved) ? saved : "eu";
    } catch {
      return "eu";
    }
  }
  function safeSetSavedRegion(code) {
    try {
      if (typeof window !== "undefined") {
        window.localStorage.setItem(STORAGE_KEY, code);
      }
    } catch {}
  }
  function dispatchRegionChanged(code) {
    try {
      if (typeof window !== "undefined") {
        window.dispatchEvent(new CustomEvent("abbyy:region-changed", {
          detail: {
            code
          }
        }));
      }
    } catch {}
  }
  function replaceRegionInString(text, currentRegion) {
    if (typeof text !== "string") return text;
    const hostRegex = /https:\/\/vantage-(eu|us|au)\.abbyy\.com/gi;
    const tokenRegex = /https:\/\/vantage-\[region\]\.abbyy\.com/gi;
    const bareHostRegex = /vantage-(eu|us|au)\.abbyy\.com/gi;
    const bareTokenRegex = /vantage-\[region\]\.abbyy\.com/gi;
    return text.replace(hostRegex, `https://vantage-${currentRegion}.abbyy.com`).replace(tokenRegex, `https://vantage-${currentRegion}.abbyy.com`).replace(bareHostRegex, `vantage-${currentRegion}.abbyy.com`).replace(bareTokenRegex, `vantage-${currentRegion}.abbyy.com`);
  }
  useEffect(() => {
    setRegion(safeGetSavedRegion());
  }, []);
  useEffect(() => {
    const root = containerRef.current;
    if (!root) return;
    const anchors = root.querySelectorAll("a[href]");
    anchors.forEach(a => {
      const href = a.getAttribute("href");
      if (href) {
        const next = replaceRegionInString(href, region);
        if (next !== href) {
          a.setAttribute("href", next);
        }
      }
    });
    const walker = typeof document !== "undefined" ? document.createTreeWalker(root, NodeFilter.SHOW_TEXT, null) : null;
    if (walker) {
      const toUpdate = [];
      let node = walker.nextNode();
      while (node) {
        const updated = replaceRegionInString(node.nodeValue, region);
        if (updated !== node.nodeValue) {
          toUpdate.push([node, updated]);
        }
        node = walker.nextNode();
      }
      toUpdate.forEach(([textNode, value]) => {
        textNode.nodeValue = value;
      });
    }
  }, [region, children]);
  useEffect(() => {
    const root = containerRef.current;
    if (!root) return;
    const anchors = root.querySelectorAll("a[href]");
    anchors.forEach(a => {
      a.style.fontWeight = "600";
      a.style.textDecoration = "underline";
      a.style.textUnderlineOffset = "2px";
    });
  }, [region, children]);
  useEffect(() => {
    const onRegionChanged = evt => {
      const code = evt && evt.detail && evt.detail.code;
      if (!code || code === region) return;
      setRegion(code);
    };
    if (typeof window !== "undefined") {
      window.addEventListener("abbyy:region-changed", onRegionChanged);
    }
    return () => {
      if (typeof window !== "undefined") {
        window.removeEventListener("abbyy:region-changed", onRegionChanged);
      }
    };
  }, [region]);
  useEffect(() => {
    const onClickAway = e => {
      if (!dropdownRef.current) return;
      if (!dropdownRef.current.contains(e.target)) {
        setOpen(false);
      }
    };
    document.addEventListener("click", onClickAway, true);
    return () => document.removeEventListener("click", onClickAway, true);
  }, []);
  const onSelect = code => {
    setRegion(code);
    safeSetSavedRegion(code);
    dispatchRegionChanged(code);
    setOpen(false);
  };
  const current = REGIONS.find(r => r.code === region) || REGIONS[0];
  return <div className="not-prose">
      <div className="mb-4 inline-flex items-center gap-2" ref={dropdownRef}>
        <span className="text-sm text-zinc-950/80 dark:text-white/80">Tenant Region:</span>
        <div className="relative">
          <button type="button" aria-haspopup="listbox" aria-expanded={open ? "true" : "false"} onClick={() => setOpen(v => !v)} className="h-8 px-3 rounded-md bg-zinc-900/5 dark:bg-white/5 border border-zinc-950/20 dark:border-white/20 text-sm inline-flex items-center gap-2">
            <span className="text-zinc-950 dark:text-white">{current.label}</span>
            <svg width="8" height="24" viewBox="0 -9 3 24" className="transition-transform text-gray-400 overflow-visible dark:text-gray-600 ml-auto" style={{
    transform: open ? "rotate(270deg)" : "rotate(90deg)"
  }}>
              <path d="M0 0L3 3L0 6" fill="none" stroke="currentColor" strokeWidth="1.5" strokeLinecap="round"></path>
            </svg>
          </button>
          {open ? <div role="listbox" className="absolute z-50 left-full top-0 ml-2 w-56 rounded-xl border border-zinc-950/20 dark:border-white/20 bg-white dark:bg-zinc-900 shadow-lg p-2">
              {REGIONS.map(r => {
    const selected = r.code === region;
    return <button key={r.code} type="button" role="option" aria-selected={selected ? "true" : "false"} onClick={() => onSelect(r.code)} className={"w-full flex items-center justify-between px-3 py-2 rounded-lg text-sm hover:bg-zinc-950/5 dark:hover:bg-white/10 text-zinc-950 dark:text-white" + (selected ? " font-medium" : "")}>
                    <span style={selected ? {
      color: "#ff2038"
    } : undefined}>{r.label}</span>
                    <span className={selected ? "ml-3" : "ml-3 invisible"} style={selected ? {
      color: "#ff2038"
    } : undefined}>✓</span>
                  </button>;
  })}
            </div> : null}
        </div>
      </div>
      <div ref={containerRef} className="prose dark:prose-invert max-w-none">{children}</div>
    </div>;
};

この認証方式は最も安全と考えられます。認証リクエストをユーザーに直接送るのではなく、アプリケーションが Vantage の認可サーバーに送信するためです。認可サーバーがユーザーを認証し、クライアントに認可コードを返します。

認可コードの傍受を防ぐために、この認証シナリオでは PKCE (Proof Key for Code Exchange) というセキュリティ拡張を使用します。動作は次のとおりです。各認可リクエストで暗号学的に安全な乱数を生成して **code\_verifier** に保存し、それを用いて **code\_challenge** に格納する暗号学的に導出された値を生成します。続いてこの値を認可サーバーへ送信し、認可コードを取得します。

PKCE の詳細については [こちら](https://datatracker.ietf.org/doc/html/rfc7636)をご覧ください。

<div id="getting-the-authorization-code">
  #### 認可コードの取得
</div>

認証プロセスを開始するには、以下のパラメーターを渡してユーザーを authorize エンドポイントにリダイレクトします。

| Parameter                                      | 説明                                                                                                                              |
| ---------------------------------------------- | ------------------------------------------------------------------------------------------------------------------------------- |
| client\_id                                     | アプリケーション識別子。Vantage API Client (**client\_id** と **client\_secret**) の作成方法については、「Managing Tenant Vantage API Clients」を参照してください。 |
| redirect\_uri                                  | アクセス権限が付与された後にブラウザーをリダイレクトするために使用する、アプリケーションまたは Web サイトの URL。                                                                   |
| response\_type=code                            | 認可コードのレスポンスタイプを使用することを指定します。                                                                                                    |
| scope=openid permissions global.wildcard       | 権限スコープを指定します。                                                                                                                   |
| state                                          | レスポンスに認可結果を含めるための任意の文字列値。                                                                                                       |
| code\_challenge                                | **code\_verifier** コードのデジタル署名値 (**code\_challenge\_method** メソッドを使用) 。                                                          |
| code\_challenge\_method                        | **code\_verifier** コードのデジタル署名方式 (S256) 。                                                                                        |
| productId=a8548c9b-cb90-4c66-8567-d7372bb9b963 | Vantage の識別子。                                                                                                                   |

<Warning>
  response\_type、scope、productId の値は、必ず上記のとおり正確に指定してください。response\_type を除くこれらのキーは変更される可能性があります。設定で管理することをお勧めします。
</Warning>

サンプルリクエスト

<VantageRegion>
  ```shell wrap theme={null}
  https://vantage-us.abbyy.com/auth2/connect/authorize?client_id=client_id&redirect_uri=https%3A%2F%2Fvantage-us.abbyy.com%2Flogin-callback&response_type=code&scope=openid%20permissions%20global.wildcard&state=ef30939211cc4ecb9a7a349b855c6a10&code_challenge=tA6ayQ5VUjLX2tufAKaHh-9bTAQ4hQQY5VZAoB2kG9o&code_challenge_method=S256&productId=a8548c9b-cb90-4c66-8567-d7372bb9b963
  ```
</VantageRegion>

リソースの識別子を含む redirect\_uri パラメーターは OAuth 2.0 で使用されます。これにより、Vantage は認可コードをリソースに送信し、そのコードをアクセストークンに交換できます。アクセストークンは、以降のすべての API call での認証に必要です。この認証方法を使用するには、redirect\_uri パラメーターの値を ABBYY technical support に提供し、管理者によってホワイトリストに登録してもらう必要があります。

scope パラメーターで要求したアクセス権限の付与が確認されると、ブラウザーは Vantage server で設定された専用の Web ページにリダイレクトされます。この Web ページには、アカウントを使用して認可を行うためのダイアログウィンドウが表示されます。このページは、ページ URL と接続の SSL 証明書の状態を確認できるよう、アドレスバーが表示されるブラウザーで開く必要があります。

メールアドレスが異なるテナント内の複数のアカウントに関連付けられている場合は、メールアドレスを入力した後、テナントの選択と password の入力を求められます。次のいずれかのリソースを使用して、テナント identifier (tokenId パラメーター) を直接渡すこともできます。

<VantageRegion>
  ```shell wrap theme={null}
  https://vantage-us.abbyy.com/auth2/{tenantId}/connect/authorize?client_id=client_id&redirect_uri=external_app_redirect_uri&response_type=code
  &scope=openid%20permissions%20global.wildcard&state=state&code_challenge=code_challenge
  &code_challenge_method=S256&productId=a8548c9b-cb90-4c66-8567-d7372bb9b963
  ```
</VantageRegion>

または

<VantageRegion>
  ```shell wrap theme={null}
  https://vantage-us.abbyy.com/auth2/connect/authorize?client_id=client_id&redirect_uri=external_app_redirect_uri&response_type=code
  &scope=openid%20permissions%20global.wildcard&state=state&code_challenge=code_challenge
  &code_challenge_method=S256&productId=a8548c9b-cb90-4c66-8567-d7372bb9b963&tenantId=tenantId
  ```
</VantageRegion>

テナント アカウントの password を入力する必要があります。認証情報を入力すると、認可はサーバー側で完了し、アプリケーションに Vantage API へのアクセスが付与され、request へのレスポンスで認可コードが返されます。

この認証タイプをサイトまたはアプリケーションが使用する場合、Vantage ユーザーは、自分に代わって、許可されたリダイレクト URL のリストに追加するそのサイトまたはアプリに Vantage API へのアクセス権を与えることになります。サイトまたはアプリにアクセス権を与えるために、ユーザーは自分のログイン名とパスワードを使って Vantage にログインして認証を行うよう求められます。ユーザーが認証されると、そのサイトまたはアプリには次の権限が付与されます。

* ユーザーに代わって Vantage テナント内のデータ カタログを管理すること
* ユーザーに代わって Vantage テナント内の Skill にアクセスすること
* ユーザーに代わって Vantage トランザクションを作成および参照すること

そのサイトまたはアプリは、ユーザーのパスワードを変更したり、Vantage テナントのユーザー一覧を変更したり、Skill を編集したりすることはできません。許可されるのは Vantage API へのアクセスのみです。一度アクセス権が付与されると、ユーザーがそのアクセス権を取り消すことはできません。

<div id="getting-the-authorization-token">
  #### 認可トークンの取得
</div>

認可コードを取得したら、1分以内にアクセストークンへ交換します。`application/x-www-form-urlencoded` データを使用して、トークンエンドポイントに POST リクエストを送信します。

リクエスト本文のパラメーター:

| Parameter                                                | 説明                                                               |
| -------------------------------------------------------- | ---------------------------------------------------------------- |
| code\_verifier                                           | 生成したコード。認可リクエストの開始を確認するために必要です。                                  |
| client\_id                                               | アプリケーションの識別子。                                                    |
| client\_secret                                           | アプリケーションのセキュアキー。                                                 |
| code                                                     | サーバーから取得した認可コード。                                                 |
| redirect\_uri                                            | 認可ステップで使用したリダイレクト URL。                                           |
| grant\_type=authorization\_code                          | 認可コードのグラントタイプを使用することを指定します。                                      |
| scope=openid permissions global.wildcard offline\_access | 権限スコープを指定します。リフレッシュトークンを取得するには、スコープに **offline\_access** を追加します。 |

サンプルリクエスト:

Windows の場合:

<VantageRegion>
  ```shell theme={null}
  curl --location --request POST "https://vantage-eu.abbyy.com/auth2/connect/token" \
    --data-urlencode "code_verifier=code_verifier" \
    --data-urlencode "client_id=client_id" \
    --data-urlencode "client_secret=client_secret" \
    --data-urlencode "code=authorization code" \
    --data-urlencode "redirect_uri=external app redirect uri" \
    --data-urlencode "grant_type=authorization_code"
  ```
</VantageRegion>

Linux の場合:

<VantageRegion>
  ```shell theme={null}
  curl --location --request POST 'https://vantage-eu.abbyy.com/auth2/connect/token' \
    --data-urlencode 'code_verifier=code_verifier' \
    --data-urlencode 'client_id=client_id' \
    --data-urlencode 'client_secret=client_secret' \
    --data-urlencode 'code=authorization code' \
    --data-urlencode 'redirect_uri=external app redirect uri' \
    --data-urlencode 'grant_type=authorization_code'
  ```
</VantageRegion>

サーバーからの応答にはアクセス トークンが含まれます。

```json theme={null}
{
  "id_token": "<redacted>",
  "access_token": "<redacted>",
  "expires_in": 86400,
  "token_type": "Bearer",
  "refresh_token": "<redacted>",
  "scope": "openid permissions global.wildcard offline_access legacy.client"
}
```

認可コードフローの詳細は、[こちら](https://datatracker.ietf.org/doc/html/rfc6749#section-4.1)をご覧ください。

各フローのレスポンスでは、**access\_token** キーにトークンが含まれ、**expires\_in** キーにトークンの有効期限までの残り時間 (秒) が指定されます。デフォルトのアクセストークンの有効期間は 24 時間です (詳細は [Token lifetimes](#token-lifetimes) を参照してください) 。すべてのリクエストに次の Authorization ヘッダーを追加し、`token` を取得したトークンの値に置き換えてください。

```shell theme={null}
-H "Authorization: Bearer token"
```

同じアカウントでも複数のトークンを取得できることに注意してください。認可トークンの詳細については、[こちらのリンク](https://datatracker.ietf.org/doc/html/rfc6749#section-7.1)をご覧ください。

<div id="getting-the-refresh-token">
  #### リフレッシュトークンの取得
</div>

Vantage API クライアントを設定する際に `Allow issuing refresh tokens to refresh access tokens` オプションを有効にし、アクセストークンを取得するためのリクエストに `scope=openid permissions global.wildcard offline_access` パラメーターが含まれていた場合、レスポンスとして追加のリフレッシュトークンも受け取ります。リフレッシュトークンを取得したら、次のパラメーターを指定してトークンエンドポイントに POST リクエストを送信することで、アクセストークンを更新できます。

| Parameter                  | Description                      |
| -------------------------- | -------------------------------- |
| client\_id                 | アプリケーション識別子。                     |
| client\_secret             | アプリケーション用の機密キー。                  |
| refresh\_token             | サーバーから取得したリフレッシュトークン。            |
| grant\_type=refresh\_token | リフレッシュトークンのグラントタイプを使用することを指定します。 |

サンプルリクエスト:

Windows の場合:

<VantageRegion>
  ```shell theme={null}
  curl --location --request POST "https://vantage-eu.abbyy.com/auth2/connect/token" \
    --data-urlencode "client_id=client_id" \
    --data-urlencode "client_secret=client_secret" \
    --data-urlencode "refresh_token=refresh_token" \
    --data-urlencode "grant_type=refresh_token"
  ```
</VantageRegion>

Linux の場合:

<VantageRegion>
  ```shell theme={null}
  curl --location --request POST 'https://vantage-eu.abbyy.com/auth2/connect/token' \
    --data-urlencode 'client_id=client_id' \
    --data-urlencode 'client_secret=client_secret' \
    --data-urlencode 'refresh_token=refresh_token' \
    --data-urlencode 'grant_type=refresh_token'
  ```
</VantageRegion>

<div id="token-lifetimes">
  #### トークンの有効期間
</div>

Access トークンと Refresh トークンには、次の有効期間が設定されています。

* **Access トークンの有効期間:** 発行された Access トークンを使用してユーザーが Vantage にアクセスできる期間を定義します。Access トークンの既定の有効期間は 24 時間です。
* **Refresh トークンの有効期間:** 最初の Access トークンが発行された時点から起算される固定の期間を定義し、その期間中は発行された Refresh トークンを使用して Access トークンを更新できます。Refresh トークンの既定の有効期間は 30 日です。
