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", | |
redirectUri: "https://yourOrg.crm4.dynamics.com/WebResources/mea_/Pages/FlowWidgetSPA.html", | |
}, | |
cache: { | |
cacheLocation: "sessionStorage", | |
storeAuthStateInCookie: true | |
}, | |
flowScopes: ["https://service.flow.microsoft.com//Flows.Read.All", "https://service.flow.microsoft.com//Flows.Manage.All", "https://service.flow.microsoft.com//Approvals.Manage.All", "https://service.flow.microsoft.com//Approvals.Read.All"] | |
}; | |
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…
Reblogged this on Nishant Rana's Weblog and commented:
One more insightful blog post by @Mehdi.
LikeLike
thanks for this awesome blog post !
LikeLiked by 1 person
Thank you very much. I’m glad you liked it ✌
LikeLike
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!
LikeLike
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
LikeLike
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,
LikeLike
Hi yasmany,
Thank you for taking the time to read my blog. Unfortunately this approach works for the web client only.
LikeLike
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?
LikeLike
Thanks for your feedback. Do you have any errors on the console. Personally, I never had this problem.
LikeLike
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?
LikeLike