共用方式為


認證最佳做法

本文提供在 Azure 通訊服務 SDK 中管理 使用者存取令牌 的最佳做法。 請遵循此指引,將應用程式所使用的資源優化,並減少往返 Azure 通訊身分識別 API 的數目。

通訊權杖認證

通訊令牌認證 (Credential) 是包裝使用者存取令牌的驗證基本類型。 它用來驗證通訊服務中的使用者,例如聊天或通話。 此外,它也提供內建令牌重新整理功能,方便開發人員使用。

選擇工作階段存留期

視您的案例而定,您可能想要調整為應用程序發行之令牌的生命週期。 下列最佳做法或其組合可協助您為案例達成最佳解決方案:

設定自定義令牌到期時間

要求新的令牌時,建議針對一次性聊天訊息或限時通話會話使用簡短的存留期令牌。 我們建議對於使用應用程式時間較長的代理,使用較長有效期的令牌。 默認令牌到期時間為24小時。 您可以將一小時到 24 小時之間的值提供給選擇性參數,以自訂到期時間,如下所示:

const tokenOptions = { tokenExpiresInMinutes: 60 };
const user = { communicationUserId: userId };
const scopes = ["chat"];
let communicationIdentityToken = await identityClient.getToken(user, scopes, tokenOptions);

靜態令牌

若為短期用戶端,請使用靜態令牌初始化認證。 這種方法適用於傳送一次性聊天訊息或限時通話會話等案例。

let communicationIdentityToken = await identityClient.getToken({ communicationUserId: userId }, ["chat", "voip"]);
const tokenCredential = new AzureCommunicationTokenCredential(communicationIdentityToken.token);

回呼函式

對於長期運行的用戶端,請使用回呼函數初始化認證憑證,以確保在通訊期間保持持續的驗證狀態。 例如,此方法適用於長時間通話會話。

const tokenCredential = new AzureCommunicationTokenCredential({
            tokenRefresher: async (abortSignal) => fetchTokenFromMyServerForUser(abortSignal, "<user_name>")
        });

權杖重新整理

若要正確實作權杖重新整理器回呼,程式碼必須傳回包含有效 JSON Web 權杖 (JWT) 的字串。 傳回的令牌必須一律有效,且其到期日會在未來設定。 某些平臺,例如 JavaScript 和 .NET,提供中止重新整理作業的方式,並將 AbortSignalCancellationToken 傳遞至您的函式。 建議您接受這些物件、使用這些物件,或進一步傳遞這些物件。

範例 1:重新整理通訊使用者的權杖

假設我們在 Express 上建置了一個 Node.js 應用程式,其 /getToken 端點允許根據名稱擷取使用者的新有效令牌。

app.post('/getToken', async (req, res) => {
    // Custom logic to determine the communication user id
    let userId = await getCommunicationUserIdFromDb(req.body.username);
    // Get a fresh token
    const identityClient = new CommunicationIdentityClient("<COMMUNICATION_SERVICES_CONNECTION_STRING>");
    let communicationIdentityToken = await identityClient.getToken({ communicationUserId: userId }, ["chat", "voip"]);
    res.json({ communicationIdentityToken: communicationIdentityToken.token });
});

接下來,我們需要在用戶端應用程式中實作權杖重新整理器回呼,恰當利用 AbortSignal 並傳回未包裝的 JWT 字串。

const fetchTokenFromMyServerForUser = async function (abortSignal, username) {
    const response = await fetch(`${HOST_URI}/getToken`,
        {
            method: "POST",
            body: JSON.stringify({ username: username }),
            signal: abortSignal,
            headers: { 'Content-Type': 'application/json' }
        });

    if (response.ok) {
        const data = await response.json();
        return data.communicationIdentityToken;
    }
};

範例 2:為 Teams 使用者刷新令牌

假設我們有一個建置在 Express /getTokenForTeamsUser 上的 Node.js 應用程式,其端點允許使用相符的到期時間交換 Teams 使用者的 Microsoft Entra 存取令牌,以取得新的通訊身分識別存取令牌。

app.post('/getTokenForTeamsUser', async (req, res) => {
    const identityClient = new CommunicationIdentityClient("<COMMUNICATION_SERVICES_CONNECTION_STRING>");
    let communicationIdentityToken = await identityClient.getTokenForTeamsUser(req.body.teamsToken, '<AAD_CLIENT_ID>', '<TEAMS_USER_OBJECT_ID>');
    res.json({ communicationIdentityToken: communicationIdentityToken.token });
});

接下來,我們需要在用戶端應用程式中實作權杖重新整理器回呼,其責任是:

  1. 重新整理 Teams 使用者的 Microsoft Entra 存取令牌。
  2. 交換 Teams 使用者的 Microsoft Entra 存取令牌,以取得通訊身分識別存取令牌。
const fetchTokenFromMyServerForUser = async function (abortSignal, username) {
    // 1. Refresh the Azure AD access token of the Teams User
    let teamsTokenResponse = await refreshAadToken(abortSignal, username);

    // 2. Exchange the Azure AD access token of the Teams User for a Communication Identity access token
    const response = await fetch(`${HOST_URI}/getTokenForTeamsUser`,
        {
            method: "POST",
            body: JSON.stringify({ teamsToken: teamsTokenResponse.accessToken }),
            signal: abortSignal,
            headers: { 'Content-Type': 'application/json' }
        });

    if (response.ok) {
        const data = await response.json();
        return data.communicationIdentityToken;
    }
}

在此範例中,我們使用 Microsoft 驗證連結庫 (MSAL) 來重新整理 Microsoft Entra 存取令牌。 遵循 取得Microsoft Entra 令牌以呼叫 API 的指南之後,我們先嘗試取得令牌,而不需用戶互動。 如果不可能,則觸發其中一個互動式流程。

const refreshAadToken = async function (abortSignal, username) {
    if (abortSignal.aborted === true) throw new Error("Operation canceled");

    // MSAL.js v2 exposes several account APIs; the logic to determine which account to use is the responsibility of the developer. 
    // In this case, we'll use an account from the cache.    
    let account = (await publicClientApplication.getTokenCache().getAllAccounts()).find(u => u.username === username);

    const renewRequest = {
        scopes: [
            "https://auth.msft.communication.azure.com/Teams.ManageCalls",
            "https://auth.msft.communication.azure.com/Teams.ManageChats"
        ],
        account: account,
        forceRefresh: forceRefresh
    };
    let tokenResponse = null;
    // Try to get the token silently without the user's interaction    
    await publicClientApplication.acquireTokenSilent(renewRequest).then(renewResponse => {
        tokenResponse = renewResponse;
    }).catch(async (error) => {
        // In case of an InteractionRequired error, send the same request in an interactive call
        if (error instanceof InteractionRequiredAuthError) {
            // You can choose the popup or redirect experience (`acquireTokenPopup` or `acquireTokenRedirect` respectively)
            publicClientApplication.acquireTokenPopup(renewRequest).then(function (renewInteractiveResponse) {
                tokenResponse = renewInteractiveResponse;
            }).catch(function (interactiveError) {
                console.log(interactiveError);
            });
        }
    });
    return tokenResponse;
}

提供初始權杖

若要進一步優化程序代碼,您可以在應用程式的啟動時擷取令牌,並將它直接傳遞至認證。 提供初始權杖會略過第一次呼叫重新整理器回呼函式,但保留所有後續的呼叫。

const tokenCredential = new AzureCommunicationTokenCredential({
            tokenRefresher: async () => fetchTokenFromMyServerForUser("<user_id>"),
            token: "<initial_token>"
        });

主動式權杖重新整理

使用主動刷新消除在按需擷取令牌時可能出現的任何延遲。 主動式重新整理會在權杖存留期結束時,在背景中更新權杖。 當權杖即將到期時,在有效性結束的前 10 分鐘,認證會開始嘗試擷取權杖。 它會越來越頻繁地觸發重新整理器回呼,直到成功並擷取有效性夠長的權杖為止。

const tokenCredential = new AzureCommunicationTokenCredential({
            tokenRefresher: async () => fetchTokenFromMyServerForUser("<user_id>"),
            refreshProactively: true
        });

如果要取消排程的重新整理工作,請處置認證物件。

主動重新整理 Teams 使用者的權杖

若要最大程度减少往返 Azure 通訊識別 API 的次數,請確定您為 交換 傳遞的 Microsoft Entra 令牌的有效期限至少為 10 分鐘。 如果 MSAL 傳回具有較短有效性的快取令牌,您有下列選項可以略過快取:

  1. 強制重新整理權杖
  2. 將 MSAL 的權杖更新時段延長到 10 分鐘以上

選項 1:觸發令牌擷取流程,並將 AuthenticationParameters.forceRefresh 設定為 true

// Extend the `refreshAadToken` function 
const refreshAadToken = async function (abortSignal, username) {

    // ... existing refresh logic

    // Make sure the token has at least 10-minute lifetime and if not, force-renew it
    if (tokenResponse.expiresOn < (Date.now() + (10 * 60 * 1000))) {
        const renewRequest = {
            scopes: [
                "https://auth.msft.communication.azure.com/Teams.ManageCalls",
                "https://auth.msft.communication.azure.com/Teams.ManageChats"
            ],
            account: account,
            forceRefresh: true // Force-refresh the token
        };        
        
        await publicClientApplication.acquireTokenSilent(renewRequest).then(renewResponse => {
            tokenResponse = renewResponse;
        });
    }
}

選項 2:使用自訂 SystemOptions.tokenRenewalOffsetSeconds 來具現化 PublicClientApplication,以初始化 MSAL 驗證內容。

const publicClientApplication = new PublicClientApplication({
    system: {
        tokenRenewalOffsetSeconds: 900 // 15 minutes (by default 5 minutes)
    });

取消重新整理

若要讓通訊用戶端能夠取消進行中的重新整理工作,必須將取消物件傳遞給重新整理器回呼。 此模式僅適用於 JavaScript 和 .NET。

var controller = new AbortController();

var joinChatBtn = document.querySelector('.joinChat');
var leaveChatBtn = document.querySelector('.leaveChat');

joinChatBtn.addEventListener('click', function () {
    // Wrong:
    const tokenCredentialWrong = new AzureCommunicationTokenCredential({
        tokenRefresher: async () => fetchTokenFromMyServerForUser("<user_name>")
    });

    // Correct: Pass abortSignal through the arrow function
    const tokenCredential = new AzureCommunicationTokenCredential({
        tokenRefresher: async (abortSignal) => fetchTokenFromMyServerForUser(abortSignal, "<user_name>")
    });

    // ChatClient is now able to abort token refresh tasks
    const chatClient = new ChatClient("<endpoint-url>", tokenCredential);

    // Pass the abortSignal to the chat client through options
    const createChatThreadResult = await chatClient.createChatThread(
        { topic: "Hello, World!" },
        {
            // ...
            abortSignal: controller.signal
        }
    );

    // ...
});

leaveChatBtn.addEventListener('click', function() {
    controller.abort();
    console.log('Leaving chat...');
});

如果您想要取消後續的重新整理工作,清理 Credential 物件。

清除資源

由於 Credential 物件可以傳遞至多個聊天或通話用戶端實例,因此 SDK 不會對其存留期做出任何假設,並將其處置責任留給開發人員。 通訊服務應用程式負責處置不再需要的認證執行個體。 如果已啟用主動式重新整理,則處置認證也會取消排程的重新整理動作。

呼叫 .dispose() 函式。

const tokenCredential = new AzureCommunicationTokenCredential("<token>");
// Use the credential for Calling or Chat
const chatClient = new ChatClient("<endpoint-url>", tokenCredential);
// ...
tokenCredential.dispose()

處理登出

視您的案例而定,您可能想要從一或多個服務註銷使用者:

  • 若要從單一服務註銷使用者,捨棄 Credential 物件。
  • 若要將使用者登出多個服務,請實作訊號機制以通知所有服務處置認證物件,此外,對特定身分識別撤銷所有存取權杖

後續步驟

本文說明如何:

  • 正確初始化和處置認證物件
  • 實作權杖重新整理器回呼
  • 最佳化權杖重新整理邏輯