이 문서에서는 Azure Communication Services SDK에서 사용자 액세스 토큰 을 관리하는 모범 사례를 제공합니다. 이 지침에 따라 애플리케이션에서 사용하는 리소스를 최적화하고 Azure Communication Identity API에 대한 왕복 횟수를 줄입니다.
통신 토큰 자격 증명
통신 토큰 자격 증명(자격 증명)은 사용자 액세스 토큰을 래핑하는 인증 기본 형식입니다. 채팅 또는 통화와 같은 Communication Services에서 사용자를 인증하는 데 사용됩니다. 또한 개발자의 편의를 위해 기본 제공 토큰 새로 고침 기능을 제공합니다.
세션 수명 선택
시나리오에 따라 애플리케이션에 대해 발급된 토큰의 수명을 조정할 수 있습니다. 다음 모범 사례 또는 조합은 시나리오에 가장 적합한 솔루션을 달성하는 데 도움이 될 수 있습니다.
- 특정 요구 사항에 맞게 토큰 만료 시간을 사용자 지정합니다.
- 일회성 채팅 메시지 또는 시간 제한 통화 세션에 대한 정적 토큰 을 사용하여 자격 증명을 초기화합니다.
- 더 오랜 기간 동안 애플리케이션을 사용하는 에이전트에 콜백 함수 를 사용합니다.
사용자 지정 토큰 만료 시간 설정
새 토큰을 요청할 때 일회성 채팅 메시지 또는 시간 제한 통화 세션에 짧은 수명 토큰을 사용하는 것이 좋습니다. 더 긴 기간 동안 애플리케이션을 사용하는 에이전트에 대해 더 긴 수명 토큰을 사용하는 것이 좋습니다. 기본 토큰 만료 시간은 24시간입니다. 다음과 같이 선택적 매개 변수에 1시간에서 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>")
});
토큰 새로 고침
토큰 새로 고침 콜백을 올바르게 구현하려면 코드에서 유효한 JWT(JSON Web Token)가 있는 문자열을 반환해야 합니다. 반환된 토큰은 항상 유효해야 하며 만료 날짜는 나중에 설정되어야 합니다. JavaScript 및 .NET과 같은 일부 플랫폼은 새로 고침 작업을 중단할 수 있는 방법을 제공하며, AbortSignal 또는 CancellationToken을 사용하여 함수에 전달할 수 있습니다. 이러한 개체를 수락하고, 활용하거나, 다른 곳에 전달하는 것이 좋습니다.
예제 1: 통신 사용자에 대한 토큰 새로 고침
이름으로 지정된 사용자에 대해 유효한 새 토큰을 가져올 수 있도록 엔드포인트를 사용하여 Express /getToken 에서 빌드된 Node.js 애플리케이션이 있다고 가정해 보겠습니다.
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 });
});
다음으로, 클라이언트 애플리케이션에서 토큰 새로 고침 콜백을 구현하여 래핑되지 않은 JWT 문자열을 제대로 활용 AbortSignal 하고 반환해야 합니다.
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 사용자에 대한 토큰 새로 고침
Teams 사용자의 Microsoft Entra 액세스 토큰을 일치하는 만료 시간을 가진 새로운 Communication Identity 액세스 토큰으로 교환할 수 있도록 /getTokenForTeamsUser 엔드포인트를 사용하여 Express에서 구축된 Node.js 애플리케이션이 있다고 가정해 보겠습니다.
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 });
});
다음으로, 다음을 담당하는 클라이언트 애플리케이션에서 토큰 새로 고침 콜백을 구현해야 합니다.
- Teams 사용자의 Microsoft Entra 액세스 토큰을 새로 고칩니다.
- Teams 사용자의 Microsoft Entra 액세스 토큰을 통신 ID 액세스 토큰으로 교환합니다.
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;
}
}
이 예제에서는 MSAL(Microsoft 인증 라이브러리)을 사용하여 Microsoft Entra 액세스 토큰을 새로 고칩니다. API를 호출하는 Microsoft Entra 토큰을 획득하는 가이드에 따라 먼저 사용자의 상호 작용 없이 토큰을 가져오려고 합니다. 가능하지 않은 경우 대화형 흐름 중 하나를 트리거합니다.
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 Communication Identity API에 대한 왕복 횟수를 최소화하려면 교환 을 위해 전달하는 Microsoft Entra 토큰에 충분한 유효 기간(> 10분)이 있는지 확인합니다. MSAL이 더 짧은 유효성으로 캐시된 토큰을 반환하는 경우 캐시를 바이패스하는 다음 옵션이 있습니다.
- 토큰을 강제로 새로 고칩니다.
- 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...');
});
후속 새로 고침 작업을 취소하려면 자격 증명 개체를 삭제 합니다.
리소스 정리
자격 증명 개체를 여러 채팅 또는 호출 클라이언트 인스턴스에 전달할 수 있으므로 SDK는 수명에 대한 가정을 하지 않으며 개발자에게 해당 삭제의 책임을 맡깁니다. 더 이상 필요하지 않은 경우 Credential 인스턴스를 삭제하는 것은 Communication Services 애플리케이션에 달려 있습니다. 자격 증명을 삭제하면 사전 새로 고침을 사용하도록 설정하면 예약된 새로 고침 작업도 취소됩니다.
.dispose() 함수를 호출합니다.
const tokenCredential = new AzureCommunicationTokenCredential("<token>");
// Use the credential for Calling or Chat
const chatClient = new ChatClient("<endpoint-url>", tokenCredential);
// ...
tokenCredential.dispose()
로그아웃 처리
시나리오에 따라 하나 이상의 서비스에서 사용자를 로그아웃할 수 있습니다.
- 단일 서비스에서 사용자를 로그아웃하려면 자격 증명 개체를 삭제 합니다.
- 여러 서비스에서 사용자를 로그아웃하려면 모든 서비스에 자격 증명 개체를 삭제 하도록 알리고 지정된 ID에 대한 모든 액세스 토큰을 해지하는 신호 메커니즘을 구현합니다.
다음 단계
이 문서에서는 다음 방법을 설명했습니다.
- 자격 증명 개체를 올바르게 초기화 및 삭제
- 토큰 새로 고침 콜백 구현
- 토큰 새로 고침 논리 최적화