共用方式為


通訊 SDK 中的認證

本文提供在 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,提供中止重新整理作業的方式,並將 或 CancellationToken 傳遞AbortSignal至您的函式。 建議您接受這些物件、利用這些物件或進一步傳遞這些物件。

範例 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 上的Node.js應用程式,其 /getTokenForTeamsUser 端點允許將 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
        });

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

主動重新整理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 物件
  • 實作令牌重新整理器回呼
  • 優化令牌重新整理邏輯

若要深入瞭解,您可能想要探索下列快速入門指南: