Office.js Office.context.ui.messageParent not working in Excel - excel

We have an excel add-in hosted in azure where the Office.context.ui.messageParent API messages aren't being sent/received on the desktop. There's no errors, the message just doesn't get caught at the event listener.
We use the dialog for MFA and we have it working locally for both desktop and web but when we deploy to a stage add-in hosted in azure this issue occurs just on the desktop.
This works:
dialog url (https://localhost:3000/dist/callback.html)
taskpane url (https://localhost:3000/dist/taskpane.html)
This doesn't:
dialog url (https://ip-dev-custom-functions.azurewebsites.net/addin/callback.html)
taskpane url (https://ip-dev-custom-functions.azurewebsites.net/addin/taskpane.html)
Surprisingly the DialogEventReceived is being triggered but not the message DialogMessageReceived.
callback.html:
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8"/>
<meta http-equiv="X-UA-Compatible" content="IE=Edge"/>
<meta http-equiv="Expires" content="0"/>
<meta http-equiv="Cache-Control" content="private, no-cache, no-store"/>
<meta http-equiv="Pragma" content="no-cache"/>
<meta http-equiv="Expires" content="-1"/>
<title></title>
<script type="text/javascript" src="https://appsforoffice.microsoft.com/lib/1.1/hosted/office.js"></script>
</head>
<body>
<script>
if (window.opener)
window.opener.postMessage({type: 'interstitial', url: document.location.href}, "*");
Office.initialize = function (reason) {
console.log("Sending auth complete message through dialog: " + document.location.href);
Office.context.ui.messageParent(document.location.href);
}
</script>
</body>
</html>
snippet of the taskpane.html where's the event listener:
Office.context.ui.displayDialogAsync(url, {
height: dim.height,
width: dim.width,
promptBeforeOpen: false
}, async (result) => {
_loginDialog = result.value;
_loginDialog.addEventHandler(Office.EventType.DialogMessageReceived, processMessage);
_loginDialog.addEventHandler(Office.EventType.DialogEventReceived, (ev) => {
console.log("## EVENT RECEIVED ##", ev);
});
});
function processMessage(arg) {
console.log("Message received in processMessage: " + JSON.stringify(arg));
_lastCallback(arg.message);
_loginDialog?.close();
_loginDialog = null;
}

Based on Authenticate and authorize with the Office dialog API, the correct flow is:
The add-in should launch the page in the dialog in add-in's domain, then redirect to the sign-in page, and then redirect to another page with the same domain as the first page in dialog again.
Otherwise, the messageParent API won't work, because it only trust the domain of the page used in the displayDialogAsync() API. In your scenario, the sign-in page (launched first time in the dialog) and the callback page are in different domains, which causes the problem.
Thanks.

Related

WebChat - Direct Line Speech adapter error: WebSocket connection failed with 400

I have a Bot service running on Azure. I'm trying to voice enable the bot, however I keep getting Websocket connection error.
I have the echo-bot running on the server side. I followed this tutorial and I added a Direct Line Speech channel to the bot with a cognitive speech service on Azure portal.
As the client, I used the sample Direct Line Speech sample. Here is the full code:
<!DOCTYPE html>
<html lang="en-US">
<head>
<meta charset="utf-8" />
<meta name="viewport" content="width=device-width, initial-scale=1" />
<meta name="theme-color" content="#000000" />
<script
crossorigin="anonymous"
src="https://cdn.botframework.com/botframework-webchat/latest/webchat.js"></script>
<title>Speech Test</title>
<style>
html,
body {
height: 100%;
}
body {
margin: 0;
}
#webchat {
height: 100%;
width: 100%;
}
</style>
</head>
<body >
<div id="root"></div>
<div id="webchat" role="main"></div>
<script>
(async function () {
async function fetchCredentials(){
const res = await fetch(
"https://westeurope.api.cognitive.microsoft.com/sts/v1.0/issueToken",
{
method: "POST",
headers: {
"Ocp-Apim-Subscription-Key": `XXXXXXXXX`,
},
}
);
if (!res.ok) {
throw new Error("Failed to fetch authorization token and region.");
}
return { authorizationToken: await res.text(), region: "westeurope" };
};
const adapters = await window.WebChat.createDirectLineSpeechAdapters({
fetchCredentials
});
window.WebChat.renderWebChat(
{
...adapters
},
document.getElementById("webchat")
);
document.querySelector("#webchat > *").focus();
})().catch((err) => err);
</script>
</body>
</html>
What might be the reason for Websocket to fail? I appreciate any help.
Version
<meta name="botframework-directlinespeech:version" content="4.11.0">
<meta name="botframework-webchat:bundle:variant" content="full">
<meta name="botframework-webchat:bundle:version" content="4.11.0">
<meta name="botframework-webchat:core:version" content="4.11.0">
<meta name="botframework-webchat:ui:version" content="4.11.0">
Error
Firefox can’t establish a connection to the server at wss://westeurope.convai.speech.microsoft.com/api/v3?language=en-US&format=simple&Authorization=<XXXXXXXXXXXX>&X-ConnectionId=7047B4CE339E4DFDA5435664DEF8CE58
could you please check if following troubleshooting guidance works? https://learn.microsoft.com/en-us/azure/cognitive-services/Speech-Service/troubleshooting#error-websocket-upgrade-failed-with-an-authentication-error-403
It says Error: HTTP 400 Bad Request is an error usually occurs when the request body contains invalid audio data. Only WAV format is supported. Also, check the request's headers to make sure you specify appropriate values for Content-Type and Content-Length.

Service to Service authentication with Managed Identity in Azure

From my Azure Function I'm trying to access an API endpoint of another custom service that has been registered as an app in azure. I have Managed Identity enabled for my azure function. I use the following code to obtain a token:
var tokenIssuerAddress = #"uriOfServiceThatImTryingToConsume";
var tokenProvider = new AzureServiceTokenProvider("RunAs=App");
var accessToken = await tokenProvider.GetAccessTokenAsync(tokenIssuerAddress);
This seems to be fine since I'm getting a bearer token. But when I then try to call the service itself with the token:
using (var client = new HttpClient())
{
client.DefaultRequestHeaders.Authorization = new AuthenticationHeaderValue("Bearer", $"{accessToken}");
var response = await client.GetAsync($"{uriOfServiceThatImTryingToConsume}{path}");
}
I get a 200 OK but the response is a HTML page that starts with the following:
<!-- Copyright (C) Microsoft Corporation. All rights reserved. -->
<!DOCTYPE html>
<html dir="ltr" class="" lang="en">
<head>
<title>Sign in to your account</title>
<meta http-equiv="Content-Type" content="text/html; charset=UTF-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="width=device-width, initial-scale=1.0, maximum-scale=2.0, user-scalable=yes">
<meta http-equiv="Pragma" content="no-cache">
<meta http-equiv="Expires" content="-1">
<link rel="preconnect" href="https://aadcdn.msftauth.net" crossorigin>
<meta http-equiv="x-dns-prefetch-control" content="on">
<link rel="dns-prefetch" href="//aadcdn.msftauth.net">
<link rel="dns-prefetch" href="//aadcdn.msauth.net">
Why do I get a HTML login page as the response when I'm using the bearer token that I got? Am I missing a step?
You have done the right thing by registering the api as an app in Azure. You also have to add the Authentication middleware like
services.AddAuthentication(JwtBearerDefaults.AuthenticationScheme)
.AddJwtBearer(o =>
{
AuthenticationSettings settings = Configuration.GetSection("Authentication").Get<AuthenticationSettings>();
o.Authority = settings.Authority;
o.TokenValidationParameters = new TokenValidationParameters
{
ValidAudiences = new[]
{
settings.ClientId,
settings.ApplicationIdUri
}
};
});
Then add "UseAuthentication" in the pipleline. See if this helps.
Assuming that the bearer token you are getting is valid and what you expect (you can always decode it to see a look at its claims), then you need to provide more details about the specific service you are calling. It's possible that the service requires 2FA or has other authentication strength policy that your bearer token does not meet and is thereby redirecting to continue authentication.

How to expose a static html page from ionic

I have a static html page which intercept authorization message, I'd like to expose this on the domain. It looks like so:
<html lang="en">
<head>
<meta charset="utf-8">
<meta name="viewport" content="width=device-width, initial-scale=1, shrink-to-fit=no">
<title>JwtAuthDemo - Facebook Auth</title>
<meta name="viewport" content="width=device-width, initial-scale=1">
<script src="assets/util.js"></script>
</head>
<body>
<script>
// if we don't receive an access token then login failed and/or the user has not connected properly
var accessToken = getParameterByName("access_token");
var message = {};
if (accessToken) {
message.status = true;
message.accessToken = accessToken;
}
else
{
message.status = false;
message.error = getParameterByName("error");
message.errorDescription = getParameterByName("error_description");
}
window.opener.postMessage(JSON.stringify(message), "http://localhost:5000");
</script>
</body>
</html>
If I place this page next to the index.html page it is not exposed, however when I place it inside the assets folder it can be access. I'm guessing I have to explicitly expose the page in one of the json config files however I'm not to sure how to do so?
I'd prefer not to have my redirect url be www.mydomain.com/assets/oauth-response-parser.html. I'd like to keep this in my application seeing as it's part of the application.
How can I expose a static html page from Ionic as a sibling to the index.html page ?
You can automatically get files to your assets directory by specifying that you want to run a custom script during your ionic builds.
In your package.json you'd have a 'config' section where you can specify this script:
...
"config": {
"ionic_copy": "./config/customCopy.config.js"
},
...
and then your customCopy.config.js would contain an entry to copy over your html into assets:
module.exports = {
copyAssets: {
src: ['{{SRC}}/assets/**/*'],
dest: '{{WWW}}/assets'
}
}
More info on this process at the ionic app scripts page
I hope this steers you in the right direction.

Buildfire - Camera Service Returns Null for Image

In my index.html I have:
<head>
<script src="../../../scripts/buildfire.js"></script>
<script src="../../../scripts/buildfire/services/camera/camera.js"></script>
</head>
Then inside of my react app I have:
buildfire.services.camera.getPicture({}, (err, image) => {
if (err) {
console.log('error: ', err);
} else {
console.log('image: ', image);
}
})
When the function fires, half of the time the console.log of image will be null, half the time it will correctly launch the camera on my phone (testing inside of the previewer). I never run into issues where it errors. In order to get it to work I just keep restarting the previewer. It seems to be a 50/50 chance of working each time I load the previewer. Testing on an android galaxy s6.
If you create a bare bones version of the plugin, without React or any other custom code, can you reproduce the issue? If so, share the code for this bare bones example plugin. Thanks.
Update: I created a bare bones plugin that utilizes the camera, and it seems to work fine when I use it on the Previewer:
<!DOCTYPE html>
<html>
<head>
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<script src="../../../scripts/buildfire.js"></script>
<script src="../../../scripts/buildfire/services/camera/camera.js"></script>
</head>
<body>
<img id="pic">
</body>
<script>
buildfire.services.camera.getPicture({},
function (err, imageData) {
console.log('error', err);
console.log('imageData', imageData);
if (imageData) {
document.getElementById("pic").src = imageData;
}
}
);
</script>
</html>

How to catch error or fire callback when creating direct line connection BotFramework-WebChat

I'm using BotFramework-WebChat to create a direct line connection to my bot from a web page and I want to have the chat pop up when the conversation has started.
I have some variation of this code:
<!DOCTYPE html>
<html>
<head>
<link href="https://unpkg.com/botframework-webchat/botchat.css"
rel="stylesheet" />
</head>
<body>
<div id="bot"/>
<script src="https://unpkg.com/botframework-webchat/botchat.js">
</script>
<script>
BotChat.App({
directLine: { secret: direct_line_secret },
user: { id: 'userid' },
bot: { id: 'botid' },
resize: 'detect'
}, document.getElementById("bot"));
</script>
</body>
</html>
Right now I'm getting an error when the bot tries to connect and I want to catch that and not show the chat box.
Is there a way i can have a callback fire when either the conversation connects succssfully or there is an error?

Resources