Integrate the approval center into a model driven app/Dynamics 365 CE

Power Automate provides a standard solution to manage approvals in a Dataverse environment. Approval requests can be easily generated from cloud flows. Users have the ability to view and respond to approval requests from email, teams or the approval center which is accessible from the power automate website only.

Today, I will present a way to respond to approvals from a model driven app directly. Also, the approval requester will be able to cancel his request from the MDA in one click.


The approver can approve, reject, or assign the approval to a new approver in the organization:

The approval requestor can cancel his or her own approval request:


To implement this, we just need to use the widgets provided by the MsFlow js SDK. Yes, there is an SDK that allows us to generate some cool widgets. The approval center is one of them. Please refer to the documentation.

As the image above shows, I managed to integrate the approval center that is accessible from the power automate web app into a model driven app dashboard. Practically, my dashboard contains an HTML web resource. On this web page, I use the msflow js sdk to display my widget. This is the source code used

<html>
<head>
<title>Flow JS SDK Sample</title>
<style>
.flowContainer iframe {
border: none;
width: 100%;
height: 100%;
}
</style>
<script type="text/javascript" src="https://alcdn.msauth.net/browser/2.3.0/js/msal-browser.js"></script>
<script src="https://flow.microsoft.com/Content/msflowsdk-1.1.js"></script>
</head>
<body onfocusout="parent.setEmailRange();" style="overflow-wrap: break-word;">
<!–<button id="signOut" onclick="signOut()">SignOut</button>–>
<div id="flowsDiv" class="flowContainer"></div>
<script>
const msalConfig = {
auth: {
clientId: "e7c3a75a-a8ef-4406-b235-eb794e0d2c1c",
authority: "https://login.microsoftonline.com/4b63d104-ee12-4f0a-bb30-7a94a90f3103&quot;,
redirectUri: "https://yourOrg.crm4.dynamics.com/WebResources/mea_/Pages/FlowWidgetSPA.html&quot;,
},
cache: {
cacheLocation: "sessionStorage",
storeAuthStateInCookie: true
},
flowScopes: ["https://service.flow.microsoft.com//Flows.Read.All&quot;, "https://service.flow.microsoft.com//Flows.Manage.All&quot;, "https://service.flow.microsoft.com//Approvals.Manage.All&quot;, "https://service.flow.microsoft.com//Approvals.Read.All&quot;]
};
var flowAccessToken;
var currentAccount;
const msalInstance = new msal.PublicClientApplication(msalConfig);
msalInstance.handleRedirectPromise().then(handleResponse).catch(err => {
console.error(err);
});
function handleResponse(resp) {
if (resp !== null) {
username = resp.account.username;
currentAccount = resp.account;
acquireTokenPopupAndLoadFlowsWidget();
} else {
const currentAccounts = msalInstance.getAllAccounts();
if (currentAccounts.length == 0) {
signIn();
} else if (currentAccounts.length > 1) {
// Add choose account code here
console.warn("Multiple accounts detected.");
} else if (currentAccounts.length === 1) {
username = currentAccounts[0].username;
console.log(currentAccounts[0]);
currentAccount = currentAccounts[0]
acquireTokenPopupAndLoadFlowsWidget();
}
}
}
function signIn() {
var loginRequest = {
scopes: msalConfig.flowScopes
}
msalInstance.loginPopup(loginRequest).then(handleResponse).catch(error => {
console.error(error);
});
}
function signOut() {
msalInstance.logout();
}
function acquireTokenPopupAndLoadFlowsWidget() {
const accessTokenRequest = {
scopes: msalConfig.flowScopes,
account: currentAccount
}
msalInstance.acquireTokenSilent(accessTokenRequest).then(function (accessToken) {
flowAccessTokenAcquired(accessToken);
}, function (error) {
console.log(error);
if (error.indexOf("consent_required") !== 1 || error.indexOf("interaction_required") !== 1 || error.indexOf("login_required") !== 1) {
msalInstance.acquireTokenPopup(msalConfig.flowScopes).then(function (accessToken) {
flowAccessTokenAcquired(accessToken);
}, function (error) {
console.log(error);
});
}
});
}
function flowAccessTokenAcquired(accessToken) {
flowAccessToken = accessToken.accessToken;
console.log(accessToken.accessToken);
loadApprovalsWidget();
}
function loadApprovalsWidget(accessToken) {
resetWidgetContainer();
var sdk = new window.MsFlowSdk({ hostName: window.location.origin, locale: 'en-us', hostId: window.WellKnownHostIds.WIDGETTEST });
var widget = sdk.renderWidget('approvalCenter', {
container: 'flowsDiv',
enableRegionalPortal: true,
enableOnBehalfOfTokens: true,
environmentId: '43391dad-6733-4eff-9765-027bedf0b244',
approvalCenterSettings: {
autoNavigateToDetails: true,
hideFlowCreation: true,
hideInfoPaneCloseButton: true,
hideLink: true,
showSimpleEmptyPage: true
}
});
widget.listen("GET_ACCESS_TOKEN", function (requestParam, widgetDoneCallback) {
widgetDoneCallback(null, {
token: flowAccessToken
});
});
widget.listen("WIDGET_READY", function () {
console.log("The flow widget is now ready.");
});
}
function resetWidgetContainer() {
var container = document.getElementById("flowsDiv");
while (container.firstChild) {
container.removeChild(container.firstChild);
}
}
</script>
</body>
</html>

The Azure Active Directory App configuration:

Hope it helps…

12 thoughts on “Integrate the approval center into a model driven app/Dynamics 365 CE

  1. Hi Mehdi!

    Where did you get the “EnvironmentID” from listed at line: 106 in the code?
    As well as the authority id at the end of the URL at line: 23?

    Thanks for the help!

    Like

    1. Hi Johan,

      You will find your EnvironmentID by going through the following steps:

      Navigate to make.powerapps.com, then choose your environment. Now your URL will look like this: make.powerapps.com/environments/{EnvironmentID}/home

      the authority ID at the end of the URL at line: 23 is your tenantId

      Like

  2. Hi Mehdi
    I followed your instructions and it works very well for me in powerapps from the browser on my computer, but when I enter from the powerapps application on my phone, the widget does not seem to load, at least everything is blank, I am not sure if it has to do with authentication, it could be because not being a browser can not open the authentication popup
    Could you tell me if it is normal that I do not load in powerapps from the phone? or I may have done something wrong, thank you very much for the excellent article.
    Kind regards,

    Like

  3. Hello Mehdi,

    This post is excellent. I am facing one issue. My widget for approval center is loading fine but the data is not showing up in approval center. It keeps on spinning but data does not load in approval center. I have followed same configuration steps for Azure app registration. Can you let me know if you came across this issue?

    Like

      1. I figured it out. It seems by flow approval entity had lot of data which was taking lot of time to load leading to this issue. After I cleared data in this entity, it loaded successfully.
        Follow up question:
        Since I want to show approval history for particular process, how do I connect meta data associated with that process to approval? Flow approval is a system entity and would not allow creation of lookup column to a particular entity from where I can show it as related data.
        Do I need to create a link entity in between which will populate data from process and associated approval data in that entity which I can then use to show approval history.
        Any thoughts on the same?

        Like

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out /  Change )

Twitter picture

You are commenting using your Twitter account. Log Out /  Change )

Facebook photo

You are commenting using your Facebook account. Log Out /  Change )

Connecting to %s