ドキュメントAEMAEM チュートリアルAEM as a Cloud Service のチュートリアル

OAuth 単一ページアプリを使用した OpenAPI ベースの AEM API の呼び出し

最終更新日: 2025年6月18日
  • 適用対象:
  • Cloud Service
  • トピック:
  • 開発

作成対象:

  • 中級
  • 開発者
  • リーダー

OAuth 単一ページアプリ認証 ​を使用して、AEM as a Cloud Service で OpenAPI ベースの AEM API を呼び出す方法を学びます。これは、単一ページアプリケーション(SPA)でのユーザーベース認証の OAuth 2.0 PKCE(Proof Key for Code Exchange)フローに従います。

OAuth 単一ページアプリ認証は、ブラウザーで実行される JavaScript ベースのアプリケーションに最適です。バックエンドサーバーがない場合も、ユーザーの代わりに AEM API とやり取りするためにアクセストークンを取得する必要がある場合も同様です。

PKCE フローは、OAuth 2.0 authorization_code 付与タイプを拡張し、承認コードの傍受を防いでセキュリティを強化します。詳しくは、「OAuth サーバー間資格情報と、web アプリ資格情報と単一ページアプリ資格情報の違い」セクションを参照してください。

学習内容

このチュートリアルでは、以下の方法について説明します。

  • Adobe Developer Console(ADC)プロジェクトを設定し、OAuth 単一ページアプリ ​認証(一般的に OAuth 2.0 PKCE フロー ​と呼ばれる)を使用して、OpenAPI ベースの AEM API にアクセスします。

  • カスタム SPA で OAuth 単一ページアプリ認証フローを実装します。

    • IMS ユーザー認証とアプリ承認。
    • OAuth 2.0 PKCE フローを使用したアクセストークン取得。
    • アクセストークンを使用して、OpenAPI ベースの AEM API を呼び出します。

開始する前に、次を必ず確認します。

  • 「Adobe API へのアクセスと関連概念」セクション。
  • OpenAPI ベースの AEM API の設定記事。

WKND SPA の概要と機能フロー

WKND SPA とは何か、WKND SPA の構造、WKND SPA の機能を見てみましょう。

WKND SPA は、ユーザー固有のアクセストークンを安全に取得し、クライアントサイドから直接 AEM API とやり取りする方法を示す React ベースの単一ページアプリケーション ​です。Adobe IMS を通じた OAuth 2.0 PKCE 認証フローを実装し、次の 2 つの主要な AEM API と統合されています。

  1. Sites API:コンテンツフラグメントモデルへのアクセス用
  2. Assets API:DAM フォルダーの管理用

Adobe Developer Console(ADC)プロジェクトは、OAuth 単一ページアプリ認証を有効にするように設定されており、OAuth 2.0 PKCE フローを開始するために必要な client_id を提供します。

IMPORTANT
ADC プロジェクトは client_secret を提供しません。代わりに、SPA は code_verifier と code_challenge を生成して、アクセストークン ​の承認コードを安全に交換します。これにより、クライアント側でクライアントの秘密鍵を保存する必要がなくなり、セキュリティが強化されます。
https://video.tv.adobe.com/v/3456965?quality=12&learn=on&captions=jpn

次の図は、WKND SPA が OpenAPI ベースの AEM API を呼び出すためのユーザー固有のアクセストークンを取得する ​機能フローを示しています。

WKND SPA 認証フロー

  1. SPA が認証リクエスト経由で Adobe ID 管理システム(IMS)にユーザーを誘導することで認証フローを開始します。
  2. 認証リクエストの一環として、SPA は、OAuth 2.0 PKCE フローに従って、client_id、redirect_uri および code_challenge を IMS に送信します。SPA はランダムな code_verifier を生成し、SHA-256 を使用してハッシュ化し、Base64 がその結果をエンコードして code_challenge を作成します。
  3. IMS がユーザーを認証し、認証に成功すると、authorization_code を発行し、それが redirect_uri 経由で SPA に返されます。
  4. SPA は、IMS トークンエンドポイントに POST リクエストを送信することで、authorization_code と access token を交換します。これには、前に送信した code_challenge を検証するリクエストに code_verifier が含まれます。これにより、認証リクエスト(手順 2)とトークンリクエスト(手順 4)が同じ認証フローにリンクされ、傍受攻撃を防ぐことができます。
  5. IMS は code_verifier を検証し、ユーザー固有の​ アクセストークン ​を返します。
  6. SPA では、AEM に対する API リクエストに​ アクセストークン ​を含めて、ユーザー固有のコンテンツを認証および取得します。

WKND SPA は React ベースのアプリケーションであり、認証状態の管理には React コンテキストを使用し、ナビゲーションには React ルーターを使用します。

Angular、Vue、vanilla JavaScript などの他の SPA フレームワークを使用して、このチュートリアルで示すアプローチに従って Adobe API と統合する SPA を作成できます。

このチュートリアルの使用方法

このチュートリアルは次の 2 つの方法でアプローチできます。

  • SPA の主要なコードスニペットの確認:OAuth 単一ページアプリ認証フローを理解し、WKND SPA での主要な API 呼び出し実装について確認します。
  • SPA の設定と実行:段階的な手順に従って、ローカルマシンに WKND SPA を設定し実行します。

ニーズに合ったパスの選択

SPA の主要なコードスニペットの確認

次の方法を示す WKND SPA の主要なコードスニペットについて詳しく見てみましょう。

  • OAuth 単一ページアプリ認証フローを使用して、ユーザー固有のアクセストークンを取得します。

  • クライアント側から直接 OpenAPI ベースの AEM API を呼び出します。

これらのスニペットは、SPA 内の認証プロセスと API のインタラクションを理解するのに役立ちます。

SPA コードのダウンロード

  1. WKND SPA および AEM API - デモアプリzip ファイルをダウンロードして抽出します。

  2. 抽出したフォルダーに移動し、お気に入りのコードエディターで .env.example ファイルを開きます。必要な設定パラメーターを確認します。

    ########################################################################
    # Adobe IMS, Adobe Developer Console (ADC), and AEM as a Cloud Service Information
    ########################################################################
    # Adobe IMS OAuth endpoints
    REACT_APP_ADOBE_IMS_AUTHORIZATION_ENDPOINT=https://ims-na1.adobelogin.com/ims/authorize/v2
    REACT_APP_ADOBE_IMS_TOKEN_ENDPOINT=https://ims-na1.adobelogin.com/ims/token/v3
    
    # Adobe Developer Console (ADC) Project's OAuth Single-Page App credential
    REACT_APP_ADC_CLIENT_ID=<ADC Project OAuth Single-Page App credential ClientID>
    REACT_APP_ADC_SCOPES=<ADC Project OAuth Single-Page App credential Scopes>
    
    # AEM Assets Information
    REACT_APP_AEM_ASSET_HOSTNAME=<AEMCS Hostname, e.g., https://author-p63947-e1502138.adobeaemcloud.com/>
    
    ################################################
    # Single Page Application Information
    ################################################
    
    # Enable HTTPS for local development
    HTTPS=true
    PORT=3001
    
    # SSL Certificate and Key for local development
    SSL_CRT_FILE=./ssl/server.crt
    SSL_KEY_FILE=./ssl/server.key
    
    # The URL to which the user will be redirected after the OAuth flow is complete
    REACT_APP_REDIRECT_URI=https://localhost:3000/callback
    

    プレースホルダーを Adobe Developer Console(ADC)プロジェクトおよび AEM as a Cloud Service Assets インスタンスからの実際の値に置き換える必要があります。

IMS ユーザー認証と SPA 認証

IMS ユーザー認証と SPA 認証を処理するコードを確認しましょう。コンテンツフラグメントモデルと DAM フォルダーを取得するには、Adobe IMS で認証し、自分に代わって AEM API にアクセスするための WKND SPA 権限を付与する必要があります。

最初のログイン中に、ユーザーは同意を入力するように求められ、WKND SPA が必要なリソースに安全にアクセスできるようになります。

WKND SPA の最初のログインと同意

  1. src/context/IMSAuthContext.js ファイルでは、login 関数が IMS ユーザー認証およびアプリ承認フローを開始します。ランダムな code_verifier と code_challenge を生成して、code とアクセストークンを安全に交換します。code_verifier は、後で使用するためにローカルストレージに保存されます。前述のように、SPA は client_secret を保存または使用せず、その場で 1 つを生成し、authorize リクエストと token リクエストの 2 つの手順で使用します。

    ...
    const login = async () => {
        try {
            const codeVerifier = generateCodeVerifier();
            const codeChallenge = generateCodeChallenge(codeVerifier);
    
            localStorage.setItem(STORAGE_KEYS.CODE_VERIFIER, codeVerifier);
    
            const params = new URLSearchParams(
                getAuthParams(AUTH_METHODS.S256, codeChallenge, codeVerifier)
            );
    
            window.location.href = `${
                APP_CONFIG.adobe.ims.authorizationEndpoint //https://ims-na1.adobelogin.com/ims/authorize/v2
            }?${params.toString()}`;
        } catch (error) {
            console.error("Login initialization failed:", error);
            throw error;
        }
    };
    ...
    
    // Generate a random code verifier
    export function generateCodeVerifier() {
        const array = new Uint8Array(32);
        window.crypto.getRandomValues(array);
        const wordArray = CryptoJS.lib.WordArray.create(array);
        return base64URLEncode(wordArray);
    }
    
    // Generate code challenge using SHA-256
    export function generateCodeChallenge(codeVerifier) {
        const hash = CryptoJS.SHA256(codeVerifier);
        return base64URLEncode(hash);
    }
    
    // Get authorization URL parameters
    const getAuthParams = useCallback((method, codeChallenge, codeVerifier) => {
        const baseParams = {
            client_id: APP_CONFIG.adobe.adc.clientId, // ADC Project OAuth Single-Page App credential ClientID
            scope: APP_CONFIG.adobe.adc.scopes, // ADC Project OAuth Single-Page App credential Scopes
            response_type: "code",
            redirect_uri: APP_CONFIG.adobe.spa.redirectUri, // SPA redirect URI https://localhost:3000/callback
            code_challenge_method: method, // S256 or plain
        };
    
        return {
            ...baseParams,
            code_challenge:
                method === AUTH_METHODS.S256 ? codeChallenge : codeVerifier,
            };
    }, []);
    ...
    

    ユーザーが Adobe IMS に対して認証されていない場合は、ユーザーに認証を求める Adobe ID ログインページが表示されます。

    既に認証されている場合、ユーザーは authorization_code を使用して WKND SPA の指定された redirect_uri にリダイレクトされます。

OAuth 2.0 PKCE フローを使用したアクセストークン取得

WKND SPA は、client_id および code_verifier を使用して、ユーザー固有のアクセストークンのために、authorization_code を Adobe IMS と安全に交換します。

  1. src/context/IMSAuthContext.js ファイルでは、exchangeCodeForToken 関数は authorization_code をユーザー固有のアクセストークンと交換します。

    ...
    // Handle the callback from the Adobe IMS authorization endpoint
    const handleCallback = async (code) => {
        if (authState.isProcessingCallback) return;
    
        try {
            updateAuthState({ isProcessingCallback: true });
    
            const data = await exchangeCodeForToken(code);
    
            if (data.access_token) {
                handleStorageToken(data.access_token);
                localStorage.removeItem(STORAGE_KEYS.CODE_VERIFIER);
            }
        } catch (error) {
            console.error("Error exchanging code for token:", error);
            throw error;
        } finally {
            updateAuthState({ isProcessingCallback: false });
        }
    };
    
    ...
    // Exchange the authorization code for an access token
    const exchangeCodeForToken = useCallback(async (code) => {
        const codeVerifier = localStorage.getItem(STORAGE_KEYS.CODE_VERIFIER);
    
        if (!codeVerifier) {
            throw new Error("No code verifier found");
        }
    
        //https://ims-na1.adobelogin.com/ims/token/v3
        const response = await fetch(APP_CONFIG.adobe.ims.tokenEndpoint, {
            method: "POST",
            headers: { "Content-Type": "application/x-www-form-urlencoded" },
            body: new URLSearchParams({
                grant_type: "authorization_code",
                client_id: APP_CONFIG.adobe.adc.clientId, // ADC Project OAuth Single-Page App credential ClientID
                code_verifier: codeVerifier, // Code verifier generated during login
                code, // Authorization code received from the IMS
                redirect_uri: `${window.location.origin}/callback`,
            }),
        });
    
        if (!response.ok) {
            throw new Error("Token request failed");
        }
    
        return response.json();
    }, []);
    
    const handleStorageToken = useCallback(
        (token) => {
            if (token) {
                localStorage.setItem(STORAGE_KEYS.ACCESS_TOKEN, token);
                updateAuthState({ isLoggedIn: true, accessToken: token });
            }
        },
        [updateAuthState]
    );
    ...
    

    アクセストークンはブラウザーのローカルストレージに保存され、AEM API への後続の API 呼び出しで使用されます。

アクセストークンを使用した OpenAPI ベースの AEM API へのアクセス

WKND SPA では、ユーザー固有のアクセストークンを使用して、コンテンツフラグメントモデルと DAM フォルダー API エンドポイントを呼び出します。

src/components/InvokeAemApis.js ファイルでは、fetchContentFragmentModels 関数は、アクセストークンを使用して、OpenAPI ベースの AEM API をクライアント側から呼び出す方法を示します。

    ...
  // Fetch Content Fragment Models
  const fetchContentFragmentModels = useCallback(async () => {
    try {
      updateState({ isLoading: true, error: null });
      const data = await makeApiRequest({
        endpoint: `${API_PATHS.CF_MODELS}?cursor=0&limit=10&projection=summary`,
      });
      updateState({ cfModels: data.items });
    } catch (err) {
      updateState({ error: err.message });
      console.error("Error fetching CF models:", err);
    } finally {
      updateState({ isLoading: false });
    }
  }, [makeApiRequest, updateState]);

  // Common API request helper
  const makeApiRequest = useCallback(
    async ({ endpoint, method = "GET", passAPIKey = false, body = null }) => {

      // Get the access token from the local storage
      const token = localStorage.getItem("adobe_ims_access_token");
      if (!token) {
        throw new Error("No access token available. Please login again.");
      }

      const headers = {
        Authorization: `Bearer ${token}`,
        "Content-Type": "application/json",
        ...(passAPIKey && { "x-api-key": APP_CONFIG.adobe.adc.clientId }),
      };

      const response = await fetch(
        `${APP_CONFIG.adobe.aem.hostname}${endpoint}`,
        {
          method,
          headers,
          ...(body && { body: JSON.stringify(body) }),
        }
      );

      if (!response.ok) {
        throw new Error(`API request failed: ${response.statusText}`);
      }

      return method === "DELETE" ? null : response.json();
    },
    []
  );
  ...

SPA の設定と実行

OAuth 単一ページアプリ認証フローと API 呼び出しを理解するために、WKND SPA をローカルマシンで設定して実行しましょう。

前提条件

このチュートリアルを完了するには、次が必要になります。

  • 次を備えた AEM as a Cloud Service 環境の最新化

    • AEM リリース 2024.10.18459.20241031T210302Z 以降。
    • 新しいスタイルの製品プロファイル(2024年11月より前に環境が作成された場合)

    詳しくは、OpenAPI ベースの AEM API の設定を参照してください。

  • サンプル WKND Sites プロジェクトをそこにデプロイする必要があります。

  • Adobe Developer Console にアクセスします。

  • Node.js をローカルマシンにインストールして、サンプルの NodeJS アプリケーションを実行します。

開発手順

大まかな開発手順は次のとおりです。

  1. ADC プロジェクトの設定

    1. Assets API と Sites API を追加します。
    2. OAuth 単一ページアプリの資格情報を設定します。
  2. AEM インスタンスの設定

    1. ADC プロジェクト通信を有効にするには
    2. CORS を設定して SPA がAEM API にアクセスできるようにします。
  3. ローカルマシンでの WKND SPA の設定と実行

  4. エンドツーエンドフローの検証

ADC プロジェクトの設定

ADC プロジェクトの設定手順は、OpenAPI ベースの AEM API の設定から​ 反復 ​されます。Assets、Sites API を追加し、その認証方法を OAuth 単一ページアプリとして設定するためにそれが反復されます。

  1. Adobe Developer Console から目的のプロジェクトを開きます。

  2. AEM API を追加するには、「API を追加」ボタンをクリックします。

    API の追加

  3. API を追加 ​ダイアログで、Experience Cloud でフィルタリングし「AEM CS Sites コンテンツ管理」カードを選択して、「次へ」をクリックします。

    AEM API の追加

    TIP
    目的の AEM API カード が無効になっていて、無効になっている理由​ ライセンスが必要です ​というメッセージが表示されます。理由の 1 つ、AEM as a Cloud Service環境を最新化していないことが考えられます。詳しくは、AEM as a Cloud Service環境の最新化を参照してください。
  4. 次に、API を設定 ​ダイアログで「ユーザー認証」認証オプションを選択し、「次へ」をクリックします。

    AEM API の設定

  5. 次の API を設定 ​ダイアログで、「OAuth 単一ページアプリ」認証オプションを選択し、「次へ」をクリックします。

    OAuth 単一ページアプリの設定

  6. OAuth 単一ページアプリを設定 ​ダイアログで、次の詳細を入力し、「次へ」をクリックします。

    • 既定のリダイレクト URI:https://localhost:3001/callback
    • リダイレクト URI パターン:https://localhost:3001/callback

    OAuth 単一ページアプリの設定

  7. 使用可能なスコープを確認し、「設定済み API を保存」をクリックします。

    設定済み API の保存

  8. 上記の手順を繰り返して、AEM Assets オーサー API を追加します。

  9. AEM API と認証設定を確認します。

    AEM API 設定

    認証設定

ADC プロジェクト通信を有効にする AEM インスタンスの設定

OpenAPI ベースの AEM API の設定記事の手順に従って、AEM インスタンスを設定し、ADC プロジェクト通信を有効にします。

AEM CORS 設定

AEM as a Cloud Service のクロスオリジンリソース共有(CORS)は、AEM 以外の web プロパティが、AEM API に対してブラウザーベースのクライアントサイド呼び出しを行うのを容易にします。

  1. AEM プロジェクトで、/ui.config/src/main/content/jcr_root/apps/wknd/osgiconfig/config.author/ フォルダーから com.adobe.granite.cors.impl.CORSPolicyImpl~wknd-graphql.cfg.json ファイルを見つけるか作成します。

    CORS 設定ファイルの検索

  2. 次の設定をファイルに追加します。

    {
        "alloworigin":[
          ""
        ],
        "alloworiginregexp":[
          "https://localhost:.*",
          "http://localhost:.*"
        ],
        "allowedpaths": [
          "/adobe/sites/.*",
          "/graphql/execute.json.*",
          "/content/_cq_graphql/wknd-shared/endpoint.json",
          "/content/experience-fragments/.*"
        ],
        "supportedheaders": [
          "Origin",
          "Accept",
          "X-Requested-With",
          "Content-Type",
          "Access-Control-Request-Method",
          "Access-Control-Request-Headers",
          "Authorization"
        ],
        "supportedmethods":[
          "GET",
          "HEAD",
          "POST"
        ],
        "maxage:Integer": 1800,
        "supportscredentials": true,
        "exposedheaders":[ "" ]
    }
    
  3. 設定変更をコミットし、Cloud Manager パイプラインが接続されているリモート Git リポジトリに変更をプッシュします。

  4. Cloud Manager のフルスタックパイプラインを使用して、上記の変更をデプロイします。

SPA の設定と実行

  1. WKND SPA および AEM API - デモアプリzip ファイルをダウンロードして抽出します。

  2. 抽出したフォルダーに移動し、.env.example ファイルを .env にコピーします。

  3. Adobe Developer Console(ADC)プロジェクトおよび AEM as a Cloud Service 環境からの必要な設定パラメーターを使用して.env ファイルを更新します。例:

    ########################################################################
    # Adobe IMS, Adobe Developer Console (ADC), and AEM as a Cloud Service Information
    ########################################################################
    # Adobe IMS OAuth endpoints
    REACT_APP_ADOBE_IMS_AUTHORIZATION_ENDPOINT=https://ims-na1.adobelogin.com/ims/authorize/v2
    REACT_APP_ADOBE_IMS_TOKEN_ENDPOINT=https://ims-na1.adobelogin.com/ims/token/v3
    REACT_APP_ADOBE_IMS_USERINFO_ENDPOINT=https://ims-na1.adobelogin.com/ims/userinfo/v2
    
    # Adobe Developer Console (ADC) Project's OAuth Single-Page App credential
    REACT_APP_ADC_CLIENT_ID=ddsfs455a4a440c48c7474687c96945d
    REACT_APP_ADC_SCOPES=AdobeID,openid,aem.folders,aem.assets.author,aem.fragments.management
    
    # AEM Assets Information
    REACT_APP_AEM_ASSET_HOSTNAME=https://author-p69647-e1453424.adobeaemcloud.com/
    
    ################################################
    # Single Page Application Information
    ################################################
    
    # Enable HTTPS for local development
    HTTPS=true
    PORT=3001
    
    # SSL Certificate and Key for local development
    SSL_CRT_FILE=./ssl/server.crt
    SSL_KEY_FILE=./ssl/server.key
    
    # The URL to which the user will be redirected after the OAuth flow is complete
    REACT_APP_REDIRECT_URI=https://localhost:3000/callback
    
  4. ターミナルを開き、抽出したフォルダーに移動します。必要な依存関係をインストールし、次のコマンドを使用して WKND SPA を開始します。

    $ npm install
    $ npm start
    

エンドツーエンドフローの検証

  1. ブラウザーを開き、https://localhost:3001 に移動して WKND SPA にアクセスします。自己署名証明書の警告を受け入れます。

    WKND SPA ホーム

  2. 「Adobe IMS ログイン」ボタンをクリックして、OAuth 単一ページアプリ認証フローを開始します。

  3. Adobe IMS に対して認証を行い、WKND SPA がユーザーに代わってリソースにアクセスできるように同意します。

  4. 認証が成功すると、WKND SPA の /invoke-aem-apis ルートにリダイレクトされ、アクセストークンがブラウザーのローカルストレージに保存されます。

    WKND SPA による AEM API の呼び出し

  5. https://localhost:3001/invoke-aem-apis ルートで、「コンテンツフラグメントモデルを取得」ボタンをクリックして、コンテンツフラグメントモデル API を呼び出します。SPA にコンテンツフラグメントモデルのリストが表示されます。

    WKND SPA フェッチ CF モデル

  6. 同様に、「アセット - フォルダー API」タブでは、DAM フォルダーの一覧表示、作成および削除を行うことができます。

    WKND SPA Assets API

  7. ブラウザーの開発者ツールで、ネットワークリクエストと応答を調べて、API 呼び出しを理解できます。

    WKND SPA ネットワークリクエスト

IMPORTANT
認証済みユーザーが AEM リソースのリスト、作成または削除に必要な権限を持っていない場合、API 呼び出しは失敗し、403 Forbidden エラーが表示されます。これにより、ユーザーが認証され、有効な IMS アクセストークンを持っている場合でも、必要な権限がなければ AEM リソースにアクセスできなくなります。

SPA コードの確認

WKND SPA の全体的なコード構造と主なエントリポイントを確認してみましょう。SPA は、React フレームワークを使用して構築され、認証と状態管理に React コンテキスト API を使用します。

  1. src/App.js ファイルは、WKND SPA の主なエントリポイントです。アプリコンポーネントは、アプリケーション全体を含め、IMSAuthProvider コンテキストを初期化します。

  2. src/context/IMSAuthContext.js は、子コンポーネントに認証状態を提供する Adobe IMSAuthContext を作成します。OAuth 単一ページアプリ認証フローを開始するための login、logout、handleCallback 関数が含まれます。

  3. src/components フォルダーには、AEM API への API 呼び出しを示す様々なコンポーネントが含まれています。InvokeAemApis.js コンポーネントは、アクセストークンを使用して AEM API を呼び出す方法を示しています。

  4. src/config/config.js ファイルは、.env ファイルから環境変数を読み込み、アプリケーションで使用するために書き出します。

  5. src/utils/auth.js ファイルには、OAuth 2.0 PKCE フローのコードベリファイアおよびコードチャレンジを生成するユーティリティ関数が含まれています。

  6. ssl フォルダーには、ローカル SSL HTTP プロキシを実行するための自己署名証明書と主要なファイルが含まれています。

このチュートリアルに示すアプローチを使用して、既存の SPA を改良または Adobe API と統合することができます。

概要

このチュートリアルでは、OAuth 2.0 PKCE フロー経由で単一ページアプリ(SPA)からユーザーベースの認証を使用して、AEM as a Cloud Service で OpenAPI ベースの AEM API を呼び出す方法を学びました。

その他のリソース

  • Adobe Experience Manager as a Cloud Service API
  • ユーザー認証実装ガイド
  • リクエストの承認
  • アクセストークンの取得
recommendation-more-help
4859a77c-7971-4ac9-8f5c-4260823c6f69