本文內容
Copilot Studio 支援單一登入 (SSO)。 如果客戶已登入部署副手的頁面或應用程式,則 SSO 可讓您網站上的副手登入客戶。
例如,副手是託管在公司內部網站或在使用者已經登入的應用程式中。
為 Copilot Studio 設定 SSO 有四個主要步驟:
在 Microsoft Entra ID 中建立自訂畫布的應用程式註冊。
為您的副手定義自訂範圍。
在 Copilot Studio 中設定驗證以啟用 SSO。
設定您的自訂畫布 HTML 代碼以啟用 SSO。
先決條件
支援的管道
下表詳細描述目前支援 SSO 的管道 。 您可以在 Microsoft Copilot Studio 構想論壇中 ,建議支援其他管道。
1 如果您也已啟用 Teams 頻道,則需要遵循使用 Microsoft Entra ID 為 Microsoft Teams 中的副手設定單一登入 文件上的指示。 如果無法根據該頁面上的指示設定 Teams SSO 設定,會導致您的使用者在使用 Teams 管道時一直無法通過驗證。
2 只支援即時聊天管道。 如需其他資訊,請參閱設定移交至 Dynamics 365 Customer Service 。
重要
當副手處於以下任一狀態時,目前不支援 SSO:
但是,已作為 SPFx 元件 發佈到 SharePoint 網站的副手支援 SSO。
建立自訂網站的應用程式註冊
若要啟用 SSO,您需要建立兩個不同的應用程式註冊:
驗證應用程式註冊 ,可為您的副手啟用 Microsoft Entra ID 使用者驗證
畫布應用程式註冊 ,這會為自訂網頁啟用 SSO
出於安全原因,不建議您的副手和自訂網站重複使用相同的應用程式註冊。
依照使用 Microsoft Entra ID 設定使用者驗證中的說明建立驗證應用程式註冊。
再次按照建立驗證應用程式註冊說明建立第二個應用程式註冊,用作畫布應用程式註冊。
將畫布應用程式註冊識別碼新增至驗證應用程式註冊。
新增權杖交換 URL
若要更新 Copilot Studio 中的 Microsoft Entra ID 驗證設定,您需要新增權杖交換 URL,以允許您的應用程式和 Copilot Studio 共用資訊。
在驗證應用程式註冊側邊欄標籤上的 Azure 入口網站中,前往公開 API 。
在範圍 下,選擇複製到剪貼簿 圖示。
在 Copilot Studio 導覽功能表的設定 底下,選取安全性 ,然後選取驗證 圖標。
在權杖交換 URL (SSO 的必要項) 中,將先前複製的範圍貼上。
選取儲存 。
建立畫布應用程式註冊之後,移至驗證 ,然後選取新增平台 。
在平台 設定底下,選取新增平台 ,然後選取 Web 。
在重新導向 URL 底下,輸入網頁的 URL;例如,http://contoso.com/index.html
。
在隱含授與和混合流程 區段中,開啟存取權杖 (用於隱含流程) 和識別碼權杖 (用於隱含和混合流程) 。
選取設定 。
尋找副手的權杖端點 URL
在 Copilot Studio 中,打開副手,然後選擇頻道 。
選取行動裝置應用程式 。
在權杖端點 底下,選取複製 。
在網頁中設定 SSO
使用 Copilot Studio GitHub 存放庫 提供的程式碼,建立重新導向 URL 的網頁。 從 GitHub 儲存庫複製代碼,並使用以下說明對其進行修改。
注意
GitHub 存放庫中的程式碼要求使用者選擇登錄按鈕或從其他網站登錄。 若要啟用自動登入,請將以下程式碼新增至 aysnc function main()
的開頭:
(async function main() {
if (clientApplication.getAccount() == null) {
await clientApplication.loginPopup(requestObj).then(onSignin).catch(function (error) {console.log(error) });
}
// Add your BOT ID below
var theURL =
前往 Azure 入口網站中的概觀 頁面,並複製畫布應用程式註冊中的應用程式 (用戶端) 識別碼 和目錄 (租用戶) 識別碼 。
若要設定 Microsoft 驗證庫 (MSAL):
指派 clientId
給您的應用程式 (用戶端) 識別碼 。
指派 authority
至 https://login.microsoftonline.com/
並將目錄 (租用戶) 識別碼新增 至結尾。
例如:
var clientApplication;
(function (){
var msalConfig = {
auth: {
clientId: '00001111-aaaa-2222-bbbb-3333cccc4444',
authority: 'https://login.microsoftonline.com/7ef988bf-xxxx-51af-01ab-2d7fd011db47'
},
將 theURL
變數設定為先前複製的權杖端點 URL。 例如:
(async function main() {
var theURL = "https://<token endpoint URL>"
編輯 userId
的值以加入自訂首碼。 例如:
var userId = clientApplication.account?.accountIdentifier != null ?
("My-custom-prefix" + clientApplication.account.accountIdentifier).substr(0, 64)
: (Math.random().toString() + Date.now().toString()).substr(0,64);
儲存您的變更。
使用您的網頁測試副手
在瀏覽器中打開網頁。
選取登入 。
注意
如果您的瀏覽器封鎖快顯視窗,或者您使用的是無痕瀏覽或私密瀏覽視窗,系統會提示您登入。 否則,將使用驗證碼完成登入。
新的瀏覽器索引標籤會開啟。
切換至新的索引標籤,並複製驗證代碼。
切換回副手索引標籤,然後將驗證碼貼到副手對話中。
相關內容
技術概觀
下列圖解顯示使用者如何登入,而不會在 Copilot Studio 中看到登入提示 (SSO):
副手使用者輸入觸發登入主題 的字詞。 登入主題的設計目的是為了讓使用者登入,並使用使用者的已驗證權杖 (User.AccessToken
變數) 。
Copilot Studio 傳送登入提示,允許使用者以其設定的識別提供者登入。
副手的自訂畫布 攔截登入提示,並要求 Microsoft Entra ID 的代理者 (OBO) 權杖。 畫布將權杖傳送至副手。
在收到 OBO 權杖時,副手會用 OBO 權杖交換「存取權杖」,並使用存取權杖的值來填入 AuthToken
變數。 此時也會設定 IsLoggedIn
變數。
在 Microsoft Entra ID 中建立自訂畫布的應用程式註冊
若要啟用 SSO,您需要兩個不同的應用程式註冊:
重要
您無法為副手的使用者驗證和自訂畫布重複使用相同的應用程式註冊。
為副手畫布建立應用程式註冊
登入 Azure 入口網站 。
若要移至應用程式註冊 ,請選取圖示或在頂端搜尋欄中搜尋。
選取新增註冊 。
輸入註冊的名稱。 使用要註冊之畫布的副手名稱,並加入「canvas」以協助將它與應用程式註冊相分離以進行驗證,這會非常實用。
例如,如果您的副手名為「Contoso 銷售説明」,您可以將應用程式註冊命名為「ContosoSalesCanvas」或類似的名稱。
在支援的帳戶類型 中選取任何組織用戶端中的帳戶 (任何 Microsoft Entra ID 目錄 - 多用戶端) 和個人 Microsoft 帳戶 (例如 Skype、Xbox) 。
目前將重新導向 URI 區段保留空白,將在後續步驟中輸入該資訊。 選取註冊 。
註冊完成後,會開啟概觀 頁面。 移至清單 。 確認 accessTokenAcceptedVersion
設定為 2
。 如果不是,請將它變更為 2
,然後選取儲存 。
新增重新導向 URL
在註冊開放後,前往驗證 然後選取新增平臺 。
在設定平台 刀鋒視窗中,選取 Web 。
在重新導向 URI 下,將完整 URL 新增至您的聊天畫布被託管的頁面。 在隱含授與 區段中,選取 識別碼權杖 和存取權杖 核取方塊。
選取設定 以確認變更。
移至 API 權限 。 選取代表 <您的用戶名稱> 授與管理員同意 然後是 。
重要
若要避免使用者同意每個應用程式,全域系統管理員、應用程式系統管理員或雲端應用程式管理員必須將用戶範圍的同意 授與您的應用程式註冊。
為您的副手定義自訂範圍
透過為驗證應用程式註冊中的畫布應用程式註冊公開 API,以定義自訂範圍。 範圍 可讓您判斷使用者和管理角色以及存取權限。
此步驟會在驗證應用程式註冊和自訂畫布的應用程式註冊之間建立信任關係。
開啟您在設定驗證時 所建立的應用程式註冊。
移至 API 權限 ,並確定已為您的副手新增正確的權限。 選取代表 <您的用戶名稱> 授與管理員同意 然後是 。
重要
若要避免使用者同意每個應用程式,全域系統管理員、應用程式系統管理員或雲端應用程式管理員必須將用戶範圍的同意 授與您的應用程式註冊。
移至公開 API 並選取新增範圍 。
輸入範圍的名稱,以及在使用者進入 SSO 畫面時,應該向使用者顯示的資訊。 選取新增範圍 。
選取新增用戶端應用程式 。
從畫布應用程式註冊的概觀 頁面,將應用程式 (用戶端) 識別碼 輸入至用戶端識別碼 欄位。 選取所建立之列出範圍的核取方塊。
選取新增應用程式 。
Copilot Studio 驗證設定頁面中的權杖交換 URL 用於透過機器人框架,交換 OBO 權杖和要求的存取權杖。
Copilot Studio 呼叫 Microsoft Entra ID 以執行實際的交換。
登入 Copilot Studio。
確認您已透過選取頂端功能表上的副手圖示,然後選擇正確的副手,來選取要啟用驗證的副手。
在導覽功能表的設定 底下,選取安全性 。 接著選取驗證 卡片。
在代用文字交換 URL 欄位中,從副手驗證應用程式註冊的接觸 API 刀鋒視窗中輸入完整範圍的 URI。 URI 的格式為 api://1234-4567/scope.name
。
選取儲存 然後發佈副手內容。
更新副手所在的自訂畫布頁面,以攔截登入卡片要求,並交換 OBO 權杖。
將下列程式碼新增至 <head> 區段的 <script> 標記中,即可設定 Microsoft 驗證程式庫 (MSAL)。
使用畫布應用程式註冊的應用程式 (用戶端) 識別碼 更新 clientId
。 將 <Directory ID>
取代為目錄 (租用戶) 識別碼 。 您可以從畫布應用程式註冊的概觀 頁面取得這些識別碼。
<head>
<script>
var clientApplication;
(function () {
var msalConfig = {
auth: {
clientId: '<Client ID [CanvasClientId]>',
authority: 'https://login.microsoftonline.com/<Directory ID>'
},
cache: {
cacheLocation: 'localStorage',
storeAuthStateInCookie: false
}
};
if (!clientApplication) {
clientApplication = new Msal.UserAgentApplication(msalConfig);
}
} ());
</script>
</head>
在 <body> 區段中插入下列 <script>。 此指令碼會呼叫方法,擷取 resourceUrl
並將您目前的權杖交換為 OAuth 提示要求的權杖。
<script>
function getOAuthCardResourceUri(activity) {
if (activity &&
activity.attachments &&
activity.attachments[0] &&
activity.attachments[0].contentType === 'application/vnd.microsoft.card.oauth' &&
activity.attachments[0].content.tokenExchangeResource) {
// asking for token exchange with Microsoft Entra ID
return activity.attachments[0].content.tokenExchangeResource.uri;
}
}
function exchangeTokenAsync(resourceUri) {
let user = clientApplication.getAccount();
if (user) {
let requestObj = {
scopes: [resourceUri]
};
return clientApplication.acquireTokenSilent(requestObj)
.then(function (tokenResponse) {
return tokenResponse.accessToken;
})
.catch(function (error) {
console.log(error);
});
}
else {
return Promise.resolve(null);
}
}
</script>
在 <body> 區段中插入下列 <script>。 在 main
方法中,此代碼會新增帶有副手唯一識別碼的條件式到您的 store
。 它也會產生唯一的識別碼做為您的 userId
變數。
使用您的副手識別碼更新 <COPILOT ID>
。 您可以前往所使用副手的管道 索引標籤,然後在 Copilot Studio 入口網站上選取行動裝置應用程式 ,以查看您的副手識別碼。
<script>
(async function main() {
// Add your COPILOT ID below
var BOT_ID = "<BOT ID>";
var theURL = "https://powerva.microsoft.com/api/botmanagement/v1/directline/directlinetoken?botId=" + BOT_ID;
const {
token
} = await fetchJSON(theURL);
var directline = await fetchJSON(regionalChannelSettingsURL).then(res=> res.channelUrlsById.directline);
const directLine = window.WebChat.createDirectLine({
domain: `${directline}v3/directline`,
token
});
var userID = clientApplication.account?.accountIdentifier != null ?
("Your-customized-prefix-max-20-characters" + clientApplication.account.accountIdentifier).substr(0, 64) :
(Math.random().toString() + Date.now().toString()).substr(0, 64); // Make sure this will not exceed 64 characters
const store = WebChat.createStore({}, ({
dispatch
}) => next => action => {
const {
type
} = action;
if (action.type === 'DIRECT_LINE/CONNECT_FULFILLED') {
dispatch({
type: 'WEB_CHAT/SEND_EVENT',
payload: {
name: 'startConversation',
type: 'event',
value: {
text: "hello"
}
}
});
return next(action);
}
if (action.type === 'DIRECT_LINE/INCOMING_ACTIVITY') {
const activity = action.payload.activity;
let resourceUri;
if (activity.from && activity.from.role === 'bot' &&
(resourceUri = getOAuthCardResourceUri(activity))) {
exchangeTokenAsync(resourceUri).then(function(token) {
if (token) {
directLine.postActivity({
type: 'invoke',
name: 'signin/tokenExchange',
value: {
id: activity.attachments[0].content.tokenExchangeResource.id,
connectionName: activity.attachments[0].content.connectionName,
token,
},
"from": {
id: userID,
name: clientApplication.account.name,
role: "user"
}
}).subscribe(
id => {
if (id === 'retry') {
// copilot was not able to handle the invoke, so display the oauthCard
return next(action);
}
// else: tokenexchange successful and we do not display the oauthCard
},
error => {
// an error occurred to display the oauthCard
return next(action);
}
);
return;
} else
return next(action);
});
} else
return next(action);
} else
return next(action);
});
const styleOptions = {
// Add styleOptions to customize Web Chat canvas
hideUploadButton: true
};
window.WebChat.renderWebChat({
directLine: directLine,
store,
userID: userID,
styleOptions
},
document.getElementById('webchat')
);
})().catch(err => console.error("An error occurred: " + err));
</script>
完整範例程式碼
如需更多資訊,您可以在我們的 GitHub 存放庫 找到完整的範例程式碼,以及 MSAL 及儲存條件指令碼。