我正在尝试使用下面的代码配置SSO。
<!DOCTYPE html>
<html>
<head>
<title>Contoso Sample Web Chat</title>
<script src="https://cdn.botframework.com/botframework-webchat/latest/webchat.js"></script>
<script type="text/javascript" src="https://alcdn.msauth.net/lib/1.2.0/js/msal.js"></script>
<script src="https://unpkg.com/@azure/storage-blob@10.3.0/browser/azure-storage.blob.min.js"
integrity="sha384-fsfhtLyVQo3L3Bh73qgQoRR328xEeXnRGdoi53kjo1uectCfAHFfavrBBN2Nkbdf"
crossorigin="anonymous">
</script>
<script type="text/javascript">
if (typeof Msal === 'undefined') document.write(unescape("%3Cscript src='https://alcdn.msauth.net/lib/1.2.0/js/msal.js' type='text/javascript' %3E%3C/script%3E"));
</script>
<style>
html,
body {
height: 100%;
}
body {
margin: 0;
}
.modal {
display: none; /* Hidden by default */
position: fixed; /* Stay in place */
z-index: 1; /* Sit on top */
padding-top: 100px; /* Location of the box */
left: 0;
top: 0;
width: 100%; /* Full width */
height: 100%; /* Full height */
overflow: auto; /* Enable scroll if needed */
background-color: rgb(0, 0, 0); /* Fallback color */
background-color: rgba(0, 0, 0, 0.4); /* Black w/ opacity */
}
.modal-content {
background-color: #fefefe;
margin: auto;
padding: 10px;
border: 1px solid #888;
width: 500px;
height: 575px;
}
.close {
color: black;
float: right;
font-size: 28px;
font-weight: bold;
}
.close:hover,
.close:focus {
color: #000;
text-decoration: none;
cursor: pointer;
}
.main {
margin: 18px;
border-radius: 4px;
}
div[role="form"] {
background-color: #3392ff;
}
#webchat {
position: center;
height: 530px;
width: 100%;
top: 60px;
overflow: hidden;
}
#heading {
padding-bottom: 5px;
}
h1 {
font-size: 14px;
font-family: Segoe UI;
font-style: normal;
font-weight: 600;
font-size: 14px;
line-height: 20px;
color: #f3f2f1;
letter-spacing: 0.005em;
display: table-cell;
vertical-align: middle;
padding: 13px 0px 0px 20px;
}
/*#chatwindow
{
height: 530px;
width: 100%;
overflow: hidden;
top: 60px;
position: center;
}*/
#login {
position: fixed;
margin-left: 150px;
}
.span {
font-weight: bold;
}
#myBtn {
position: fixed;
float: right;
outline: none;
width: 60px;
height: 80px;
margin: auto auto auto 10px;
}
button:hover {
background-color: transparent;
}
</style>
</head>
<body>
<button id="myBtn" type="button">Power Virtual Agent</button>
<div id="myModal" class="modal">
<div class="modal-content" style="background-color: #ffd933">
<span class="close">×</span>
<div id="chatwindow">
<div id="heading">
<div><span>SSO Test Bot</span></div>
</div>
<!-- <div style="z-index: 100;position: absolute;margin-top: 50px;width: 100%;">
<div>
<label id="userName" name="userName" style="width:75%;height:15px;border-color: Transparent;">Not logged in.</label>
<button id="login" name="login" onclick="onSignInClick()">Log In</button>
</div>
</div> -->
<div id="webchat"></div>
</div>
</div>
</div>
<script>
//Button code begins here
// Get the modal
var modal = document.getElementById("myModal");
// Get the button that opens the modal
var btn = document.getElementById("myBtn");
// Get the <span> element that closes the modal
var span = document.getElementsByClassName("close")[0];
// When the user clicks the button, open the modal
btn.onclick = function () {
modal.style.display = "block";
};
// When the user clicks on <span> (x), close the modal
span.onclick = function () {
modal.style.display = "none";
};
// When the user clicks anywhere outside of the modal, close it
window.onclick = function (event) {
if (event.target == modal) {
modal.style.display = "none";
}
};
//Button code ends here
</script>
<script>
function onSignin(idToken)
{
alert("KMT - Inside onSignin: " + idToken);
let user = clientApplication.getAccount();
alert("KMT - user.name: " + user.name);
document.getElementById("userName").innerHTML = "Currently logged in as " + user.name;
let requestObj1 = {
scopes: ["user.read", 'openid', 'profile']
};
}
function onSignInClick()
{
//console.log("Inside onSignInClick");
let requestObj = {
scopes: ["user.read", 'openid', 'profile']
};
clientApplication.loginPopup(requestObj).then(onSignin).catch(function (error) {console.log(error) });
}
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 AAD
return activity.attachments[0].content.tokenExchangeResource.uri;
}
}
function exchangeTokenAsync(resourceUri) {
let user = clientApplication.getAccount();
if (user) {
let requestObj = {
scopes: ["user.read", 'openid', 'profile']
};
return clientApplication.acquireTokenSilent(requestObj).then(function (tokenResponse) {
return tokenResponse.accessToken;
})
.catch(function (error) {
console.log(error);
});
}
else {
return Promise.resolve(null);
}
}
async function fetchJSON(url, options = {}) {
const res = await fetch(url, {
...options,
headers: {
...options.headers,
accept: 'application/json'
}});
if (!res.ok)
{
throw new Error(`Failed to fetch JSON due to ${res.status}`);
}
return await res.json();
}
</script>
<script>
var clientApplication;
(function ()
{
var msalConfig = {
auth:{
clientId: '7dd5c894-17f5-4fd1-be79-cb4900590418',
authority: 'https://login.microsoftonline.com/e7ee4711-c0b1-4311-b500-b80d89e5b298'
},
cache:{
cacheLocation: 'localStorage',
storeAuthStateInCookie: true
}};
if (!clientApplication)
{
clientApplication = new Msal.UserAgentApplication(msalConfig);
}
} ());
(async function main() {
// Add your BOT ID below
var BOT_ID = "ec06d968-e213-4d13-90c7-fe78bcadbfa6";
var theURL = "https://powerva.microsoft.com/api/botmanagement/v1/directline/directlinetoken?botId=" + BOT_ID;
var userId = clientApplication.account?.accountIdentifier != null ?
("You-customized-prefix" + clientApplication.account.accountIdentifier).substr(0, 64)
: (Math.random().toString() + Date.now().toString()).substr(0,64);
const { token } = await fetchJSON(theURL);
//console.log("Token inside main: " + JSON.parse(token));
const directLine = window.WebChat.createDirectLine({ token });
//console.log("directLine inside main: " + directLine);
const store = WebChat.createStore({}, ({ dispatch }) => next => action => {const { type } = action;
//console.log("store inside main: " + JSON.parse(store));
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)
{
//console.log("Inside if token: " + 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')
{ // bot 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>
</body>
</html>
每次我点击电源虚拟代理按钮,我都会在bot中看到登录OAuth卡。
聊天机器人OAuth卡图片
我要找的是我不希望登录OAuth卡被显示。相反,我想直接登录,因为我已经登录到我的SharePoint网站了。
根据Microsoft
提供的文档,我在Azure AAD中创建了2个应用程序注册1个用于身份验证,另一个用于SSO不幸的是,SharePoint的SSO与bot服务集成是不可能的SharePoint Online,这是更多的限制。没有办法从SharePoint获得AAD令牌并将其发送到bot以供使用。
作为一种解决方案,你只能将登录用户的UserID传递给bot,这样它就可以通过调用graph API/SharePoint API来获取访问令牌来使用资源,再次获取新的访问令牌来使用bot中的App权限访问用户/SP详细信息。
但是这种方法存在安全问题,任何拥有Bot直线令牌的人都可以传递任何有效的电子邮件id,并且它将使用Bot中的AAD应用程序权限获取该用户(提供的电子邮件id)详细信息的访问令牌。但最终这些用户和电子邮件id只能来自您组织的租户。我不能说它是安全的/耐脾气的。
这只是一个解决方案,因为SharePoint与bot服务的SSO集成不可能与SharePoint在线。如果您不希望用户使用AAD重新进行身份验证,那么这是目前唯一可用的选项。