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

> SPA + API アーキテクチャ シナリオ向けの API と SPA の設定

# API と SPA の設定（SPAs + API）

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

このセクションでは、このシナリオ向けの API を実装する方法を見ていきます。

<Callout icon="file-lines" color="#0EA5E9" iconType="regular">
  簡潔にするため、この実装では認証と認可のみに絞ります。サンプルで示すとおり、入力するタイムシートエントリはハードコードされており、API でそのタイムシートエントリが永続化されることはありません。代わりに、その情報の一部をそのまま返すだけです。
</Callout>

<div id="define-the-api-endpoints">
  ## API エンドポイントを定義する
</div>

まず、API のエンドポイントを定義する必要があります。

<Card title="API エンドポイントとは">
  **API エンドポイント**は、オブジェクトを表す一意の URL です。このオブジェクトとやり取りするには、アプリケーションからその URL を指定する必要があります。たとえば、注文または顧客を返す API がある場合は、`/orders` と `/customers` という 2 つのエンドポイントを設定できます。アプリケーションは異なる HTTP メソッドを使用してこれらのエンドポイントとやり取りします。たとえば、`POST /orders` で新しい注文を作成し、`GET /orders` で 1 件以上の注文データを取得できます。
</Card>

この実装では、定義するエンドポイントは 2 つだけです。1 つは従業員のすべてのタイムシートの一覧を取得するためのもので、もう 1 つは従業員が新しいタイムシートエントリを作成できるようにするものです。

`/timesheets` エンドポイントへの `HTTP GET` リクエストにより、ユーザーは自分のタイムシートを取得できます。また、`/timesheets` エンドポイントへの `HTTP POST` リクエストにより、ユーザーは新しいタイムシートを追加できます。

**実装は** [**Node.js**](/ja/docs/get-started/architecture-scenarios/spa-api/api-implementation-nodejs#1-define-the-api-endpoints) **で確認してください。**

<div id="secure-the-endpoints">
  ### エンドポイントを保護する
</div>

API がヘッダーにベアラー <Tooltip tip="アクセストークン: API へのアクセスに使用される、不透明な文字列または JWT 形式の認可資格情報。" cta="用語集を見る" href="/ja/docs/glossary?term=Access+Token">アクセストークン</Tooltip> を含むリクエストを受け取った場合、最初に行うべきことはそのトークンを検証することです。これには一連の手順があり、そのいずれかに失敗した場合は、呼び出し元アプリケーションに `Missing or invalid token` エラーメッセージを返して、リクエストを拒否する必要があります。

API が実行すべき検証は次のとおりです。

* <Tooltip tip="JSON Web Token (JWT): 2 者間でクレームを安全に表現するために使用される標準的な IDトークン形式（および多くの場合はアクセストークン形式）。" cta="用語集を見る" href="/ja/docs/glossary?term=JWT">JWT</Tooltip> の形式が正しいことを確認する
* 署名を確認する
* 標準クレームを検証する

<Callout icon="file-lines" color="#0EA5E9" iconType="regular">
  [JWT.io](https://jwt.io/) では、JWT の解析、署名の検証、クレームの検証など、この作業の大半を実行できるライブラリの一覧を提供しています。
</Callout>

検証プロセスの一環として、アプリケーションの権限 (スコープ) も確認する必要がありますが、これについてはこのドキュメントの次の段落で個別に説明します。

アクセストークンの検証の詳細については、[アクセストークンを検証する](/ja/docs/secure/tokens/access-tokens/validate-access-tokens) を参照してください。

**実装例については** [**Node.js**](/ja/docs/get-started/architecture-scenarios/spa-api/api-implementation-nodejs#2-secure-the-api-endpoints) **を参照してください。**

<div id="check-the-applications-permissions">
  ### アプリケーションの権限を確認する
</div>

ここまでで、JWT が有効であることを検証できました。最後に、保護されたリソースへのアクセスに必要な権限をアプリケーションが持っていることを確認します。

そのため、API はデコードされた JWT の[スコープ](/ja/docs/get-started/apis/scopes)を確認する必要があります。このクレームはペイロードの一部で、スペース区切りの文字列リストです。

**実装については** [**Node.js**](/ja/docs/get-started/architecture-scenarios/spa-api/api-implementation-nodejs#3-check-the-client-permissions) **を参照してください。**

<div id="determine-user-identity">
  ### ユーザーの識別方法
</div>

どちらのエンドポイント (タイムシート一覧の取得と新しいタイムシートの追加) でも、ユーザーの識別が必要です。

タイムシート一覧を取得する場合は、リクエストを行ったユーザーに属するタイムシートだけを返すためです。新しいタイムシートを追加する場合は、そのタイムシートをリクエストを行ったユーザーに関連付けるためです。

標準的な JWT クレームの 1 つに、クレームの対象となる主体を識別する `sub` クレームがあります。Implicit Grant フローでは、このクレームにユーザーの識別情報が含まれます。これは Auth0 ユーザーの一意の識別子です。これを使うと、外部システム内の任意の情報を特定のユーザーに関連付けることができます。

また、カスタムクレームを使用して、ユーザーの別の属性 (メールアドレスなど) をアクセストークンに追加し、それを使ってユーザーを一意に識別することもできます。

**実装については** [**Node.js**](/ja/docs/get-started/architecture-scenarios/spa-api/api-implementation-nodejs#4-determine-the-user-identity) **を参照してください。**

<div id="implement-the-spa">
  ## SPA を実装する
</div>

このセクションでは、このシナリオで SPA を実装する方法を見ていきます。

<div id="authorize-the-user">
  ### ユーザーを認可する
</div>

ユーザーを認可するには、[auth0.js ライブラリ](/ja/docs/libraries/auth0js) を使用します。Auth0 アプリケーションの新しいインスタンスは、次のように初期化できます。

export const codeExample = `var auth0 = new auth0.WebAuth({
  clientID: '{yourClientId}',
  domain: '{yourDomain}',
  responseType: 'token id_token',
  audience: 'YOUR_API_IDENTIFIER',
  redirectUri: '{https://yourApp/callback}',
  scope: 'openid profile read:timesheets create:timesheets'
});`;

<AuthCodeBlock children={codeExample} language="javascript" />

次の設定値を渡す必要があります。

* **clientID**: Auth0 の <Tooltip tip="クライアントID: Auth0 から登録済みのリソースに付与される識別値です。" cta="用語集を見る" href="/ja/docs/glossary?term=Client+Id">クライアントID</Tooltip> の値です。[Dashboard](https://manage.auth0.com/#/applications%7D) の Application の Settings から取得できます。
* **domain**: Auth0 のドメインの値です。[Dashboard](https://manage.auth0.com/#/applications%7D) の Application の Settings から取得できます。
* **responseType**: 使用する認証フローを指定します。**Implicit Flow** を使用する SPA の場合は、`token id_token` に設定する必要があります。`token` の部分は、フローが URL フラグメントでアクセストークンを返すようにし、`id_token` の部分は、フローが <Tooltip tip="IDトークン: リソースにアクセスするためではなく、クライアント自身のための資格情報です。" cta="用語集を見る" href="/ja/docs/glossary?term=ID+Token">IDトークン</Tooltip> も返すようにします。
* **<Tooltip tip="対象者: 発行されたトークンの対象者を一意に識別する値です。トークン内では aud という名前で、IDトークン の場合はアプリケーション（クライアントID）、アクセストークン の場合は API（API Identifier）の ID がその値に含まれます。" cta="用語集を見る" href="/ja/docs/glossary?term=audience">対象者</Tooltip>**: API Identifier の値です。[Dashboard の API の Settings](https://manage.auth0.com/#/apis%7D) から取得できます。
* **redirectUri**: ユーザーの認証後に Auth0 がリダイレクトする先の URL です。
* **scope**: IDトークン とアクセストークンで返される情報を決定する [スコープ](/ja/docs/get-started/apis/scopes) です。`openid profile` スコープを指定すると、IDトークン にすべてのユーザープロファイル情報が返されます。また、API を呼び出すために必要なスコープ (この場合は `read:timesheets create:timesheets`) もリクエストする必要があります。これにより、アクセストークンにこれらのスコープが含まれることが保証されます。

認証フローを開始するには、`authorize()` メソッドを呼び出します。

```js lines theme={null}
auth0.authorize();
```

認証後、Auth0 は、Auth0 アプリケーションの新しいインスタンスの設定時に指定した **redirectUri** にリダイレクトします。この時点で、URL のハッシュフラグメントを解析して Auth0 の認証レスポンス結果を取得する `parseHash()` メソッドを呼び出す必要があります。

parseHash が返す authResult オブジェクトの内容は、使用した認証パラメーターによって異なります。次の項目が含まれる場合があります。

* **idToken**: ユーザープロファイル情報を含む IDトークン JWT
* **accessToken**: **対象者** で指定した API のアクセストークン。
* **expiresIn**: アクセストークンの有効期限 (秒単位) を表す文字列。

トークンの[保存場所](/ja/docs/secure/security-guidance/data-security/token-storage)を決定してください。シングルページアプリにバックエンドサーバーがある場合は、トークンを [Authorization Code Flow](/ja/docs/get-started/authentication-and-authorization-flow/authorization-code-flow) または [Authorization Code Flow with Proof Key for Code Exchange (PKCE)](/ja/docs/get-started/authentication-and-authorization-flow/authorization-code-flow-with-pkce) を使用してサーバー側で処理する必要があります。

対応するバックエンドサーバーがないシングルページアプリ (SPA) の場合、SPA はログイン時に新しいトークンをリクエストし、永続化せずにメモリ内に保存する必要があります。API 呼び出しを行う際は、SPA はそのメモリ内のトークンのコピーを使用します。

SPA でセッションを処理する方法の例については、[JavaScript Single-Page App Quickstart](/ja/docs/quickstart/spa/vanillajs) の [Handle Authentication Tokens](/ja/docs/quickstart/spa/vanillajs#handle-authentication-tokens) セクションを参照してください。

**実装例は** [**Angular 2**](/ja/docs/get-started/architecture-scenarios/spa-api/spa-implementation-angular2#2-authorize-the-user) **を参照してください**。

<div id="get-the-user-profile">
  ### ユーザープロファイルを取得する
</div>

<Card title="トークンから情報を取得する">
  このセクションでは、アクセストークンと [/userinfo エンドポイント](https://auth0.com/docs/api/authentication#get-user-info) を使用してユーザー情報を取得する方法を説明します。この API 呼び出しを避けるには、代わりに IDトークン を [ライブラリを使用して](https://jwt.io/#libraries-io) デコードすることもできます (先に必ず検証してください) 。追加のユーザー情報が必要な場合は、バックエンドから [Management API](https://auth0.com/docs/api/management/v2#!/Users/get_users_by_id) を使用することを検討してください。
</Card>

`client.userInfo` メソッドは、返された `authResult.accessToken` を渡して呼び出すことで、ユーザーのプロファイル情報を取得できます。これにより [/userinfo エンドポイント](https://auth0.com/docs/api/authentication#get-user-info) にリクエストが送信され、ユーザー情報を含む `user` オブジェクトが返されます。以下の例のような内容です。

```json lines theme={null}
{
    "email_verified": "false",
    "email": "test@example.com",
    "clientID": "AAAABBBBCCCCDDDDEEEEFFFFGGGGHHHH",
    "updated_at": "2017-02-07T20:50:33.563Z",
    "name": "tester9@example.com",
    "picture": "https://gravatar.com/avatar/example.png",
    "user_id": "auth0|123456789012345678901234",
    "nickname": "tester9",
    "created_at": "2017-01-20T20:06:05.008Z",
    "sub": "auth0|123456789012345678901234"
}
```

`userInfo` 関数の呼び出し時に渡されるコールバック関数では、これらのプロパティのいずれにもアクセスできます。

```javascript lines theme={null}
const accessToken = authResult.accessToken;

auth0.client.userInfo(accessToken, (err, profile) => {
  if (profile) {
    // ユーザーのニックネームとプロフィール画像を取得する
    var nickname = profile.nickname;
    var picture = profile.picture;
  }
});
```

**実装例については** [**Angular 2**](/ja/docs/get-started/architecture-scenarios/spa-api/spa-implementation-angular2#3-get-the-user-profile) **を参照してください。**

<div id="display-ui-elements-conditionally-based-on-scope">
  ### スコープに基づいて UI 要素を条件付きで表示する
</div>

ユーザーの`scope`に応じて、特定の UI 要素を表示または非表示にできます。ユーザーに発行されたスコープを確認するには、認可プロセスで最初にリクエストしたスコープを保存しておく必要があります。ユーザーが認可されると、`scope`は`authResult`でも返されます。

`authResult`内の`scope`が空の場合は、リクエストしたすべてのスコープが付与されたことを意味します。`authResult`内の`scope`が空でない場合は、付与されたスコープのセットが異なることを意味するため、`authResult.scope`に含まれる値を使用してください。

**実装例については** [**Angular 2**](/ja/docs/get-started/architecture-scenarios/spa-api/spa-implementation-angular2#4-display-ui-elements-conditionally-based-on-scope) **を参照してください。**

<div id="call-the-api">
  ### API を呼び出す
</div>

API の保護されたリソースにアクセスするには、認証済みユーザーのアクセストークンを、その API に送信するリクエストに含める必要があります。これを行うには、`Bearer` スキームを使用して `Authorization` ヘッダーにアクセストークンを設定します。

**実装例については** [**Angular 2**](/ja/docs/get-started/architecture-scenarios/spa-api/spa-implementation-angular2#5-call-the-api) **を参照してください。**

<div id="renew-the-access-token">
  ### アクセストークンを更新する
</div>

セキュリティ対策として、ユーザーのアクセストークンの有効期間は短く設定しておくことを推奨します。<Tooltip tip="Auth0 Dashboard: サービスを構成するための Auth0 の主要製品です。" cta="用語集を見る" href="/ja/docs/glossary?term=Auth0+dashboard">Auth0 Dashboard</Tooltip> で API を作成すると、デフォルトの有効期間は `7200` 秒 (2 時間) ですが、API ごとに設定できます。

有効期限が切れると、アクセストークンは API へのアクセスに使用できなくなります。再度アクセスするには、新しいアクセストークンを取得する必要があります。

新しいアクセストークンを取得するには、最初のアクセストークンを取得したときと同じ認証フローをもう一度実行します。ただし SPA では、認証フローを再度完了するためにユーザーを現在の作業からリダイレクトしたくない場合があるため、これは理想的ではありません。

このような場合は、[サイレント認証](/ja/docs/authenticate/login/configure-silent-authentication) を利用できます。サイレント認証では、Auth0 はリダイレクトのみを返し、ログインページは表示しない認証フローを実行できます。ただし、この方法を使用するには、ユーザーが事前に [シングルサインオン (SSO) ](/ja/docs/authenticate/single-sign-on) でログインしている必要があります。

**実装例は** [**Angular 2**](/ja/docs/get-started/architecture-scenarios/spa-api/spa-implementation-angular2#6-renew-the-access-token) **を参照してください。**
