cancel
Showing results for 
Search instead for 
Did you mean: 
Reply
naveenrerd
Frequent Visitor

Call Azure function app from PCF using implicit flow

Hi,

 

I have a azure function with Azure AD authentication. I am not sure on how to trigger the function from PCF component. I tried using msal.js, but that did not work. Azure function is hosted in the same tenant as that of CRM in which I've deployed the PCF. PFB the code I tried. Could you guys help me to authenticate to call the function app.

Here I try to get the token and then planning to use the token with header in the call to Azure Function. I also like to aquire a token without a login popup if possible.

 

 

import { AxiosRequestConfig } from 'axios';
import * as Msal from "msal";

var appConfig = {
    genScope: ["https://url.azurewebsites.net/user_impersonation"]
};

export const msalConfig: Msal.Configuration = {
    auth: {
        clientId: 'bd73****-****-****-****-****74179bb',
        authority: "https://login.microsoftonline.com/0417****-****-****-****-****0dfff06c",
        validateAuthority: false,
        redirectUri: "https://url.azurewebsites.net/.auth/login/aad/callback"
    },
    cache: {
        cacheLocation: "localStorage",
        storeAuthStateInCookie: true
    }
};

const msalInstance = new Msal.UserAgentApplication(msalConfig);

const loginRequest = {
    scopes: appConfig.genScope
};

const tokenRequest = {
    scopes: appConfig.genScope
};

function signIn() {
    msalInstance.loginPopup(loginRequest).then(function (loginResponse) {
        getToken(tokenRequest).then(tk => {
            console.log(tk);
        });
    }).catch(function (error) {
        console.log(error);
    });
}

//acquire a token silently
function getToken(tokenRequest: any) {
    return msalInstance.acquireTokenSilent(tokenRequest).catch(function (error) {
        console.log("aquire token popup");
        // fallback to interaction when silent call fails
        return msalInstance.acquireTokenPopup(tokenRequest).then(function (tokenResponse) {
        }).catch(function (error) {
            console.log("Failed token acquisition", error);
        });
    });
}

msalInstance.handleRedirectCallback((error, response) => {
    // handle redirect response or error
});

export const getTokens = (): Promise<any> => {
    return getToken(tokenRequest);
}

export const runFunction = (): Promise<any> => {
    const requestConfig: AxiosRequestConfig = {
        url: "https://url.azurewebsites.net",
        method: "GET"
    };

    const token = getTokens();
    // use token and call Azure Function APP

    return Promise.resolve(true);
}

 

 

 

1 ACCEPTED SOLUTION

Accepted Solutions

@naveenrerd- You could follow a similar approach to this https://taerimhan.com/consuming-microsoft-graph-api-from-pcf-control/. Although this is for Graph API, the approach is something that you could use to execute Functions protected by Azure AD using the same MSAL library.

View solution in original post

10 REPLIES 10
DianaBirkelbach
Super User
Super User

Hi @naveenrerd 

 

My first impulse would be not to call it directly from the PCF.

 

I would try to use a CustomAction, and implement the call to the azure function with a CRM WebHook (or PlugIn), because you don't have authentication issues.

But there is still a problem with calling a CustomAction from the PCF: you need the "execute" method of the webAPI. In the PCF documentation , the WebAPI "execute" method is not documented (but it works), so it's actually unsupported for now. I'm not sure if that is a documentation issue or it's not supposed to work. Another way would be to implement the CustomAction call using the webAPI HttpRequest.

 

It's a very interesting question. I'm also looking forward to see the other answers that will come. 

 

Best regards,

Diana

Kind regards,
Diana
----------
Please click "Accept as Solution" if my post answered your question so that others may find it more quickly. If you found this post helpful consider giving it a "Thumbs Up."

@DianaBirkelbach I suspect the reason why execute isn't supported is because it execute could mean very different things in a canvas PCF component compared to a model driven PCF component.

 

The code we use to call an action is just a standard XMLHttpRequest like below (it's hacked from two pieces to give you a promise that makes a post request.

 

var req = new XMLHttpRequest();
		var baseUrl=this.baseUrl;
var query="/api/data/v9.1/hdn_ValidateLicense";
		return new Promise(function (resolve, reject) {

			req.open("POST", baseUrl + query, true);
			req.onreadystatechange = function () {
				
				if (req.readyState !== 4) return;
				if (req.status >= 200 && req.status < 300) {
					
					// If successful
					try {
						
						var result = JSON.parse(req.responseText);
						if (parseInt(result.StatusCode) < 0) {
							reject({
								status: result.StatusCode,
								statusText: result.StatusMessage
							});
						}
						resolve(req.responseText);
					}
					catch (error) {
						throw error;
					}

				} else {
					// If failed
					reject({
						status: req.status,
						statusText: req.statusText
					});
				}

			};
			req.setRequestHeader("OData-MaxVersion", "4.0");
			req.setRequestHeader("OData-Version", "4.0");
			req.setRequestHeader("Accept", "application/json");
			req.setRequestHeader("Content-Type", "application/json; charset=utf-8");
var body=	"{'ProductName': '"+productName+"'}";
			req.send(body);
		});

 

And as you say actions are great ways to avoid security issues (I've often seen security people ask why does this website talk to xyz.com?). By using an action to call the function you bypass that pain and avoid any Cross Domain issues you may hit and you don't want those as debugging that is a complete nightmare as you will need to know the complete settings of a users browsers to identify whether it's an issue, a firewall issue or something else.

 

 

---
If this post has answered your question please consider it for "Accept as Solution" or if it has been helpful give it a "Thumbs Up".

Hi @ben-thompson , 

I agree with you, except that I don't understand the explanation why webAPI.execute is not documented for PCF.

The "feature-usage" webAPI is not available for CanvasApps at all. This is only a Model-Driven App feature. So why should be a difference between webAPI.execute and webAPI.create (for instance)?

 

The problem with self-made requests, is that it is a hard-coded url , at least the "/api/data/v9.1/" part (which will change, at least in version), and that I would need an own library for this requests, if I call it more often.

But I agree, for now is the only supported way for CustomActions.

 

Best regards,

Diana

Kind regards,
Diana
----------
Please click "Accept as Solution" if my post answered your question so that others may find it more quickly. If you found this post helpful consider giving it a "Thumbs Up."

The /api/data/v9.1 part is very unlikely to change as removing it would just break a pile of code without any real benefit - I suspect MS will just keep the existing versions going until a breaking change was required (and even then it will require 2 years of notice to avoid pain). 

---
If this post has answered your question please consider it for "Accept as Solution" or if it has been helpful give it a "Thumbs Up".

Thanks @ben-thompson 

I was hoping that the client-side webAPI feature would last longer than the webAPI version (or the endpoint itself), since it could be under the hood shifted to another endpoint (saw a lot of this breaking changes since CRM 1.2).

 

Beside that, I'm not sure if webAPI feature is working offline. The Apps are designed for offline mode too (I know, not really, not for now).

But now thinking about that, I think I just understood why the webAPI feature doesn't have an execute method: in the model-driven sdk (Xrm.WebApi), the execute method is only available for online mode. That would make the difference: the "execute" is not supported for pcf, because the "pcf webAPI feature" doesn't differentiate between online and offline.

But thinking that way, the HttpRequest will not work offline at all. 🤔

 

Best regards,

Diana

Kind regards,
Diana
----------
Please click "Accept as Solution" if my post answered your question so that others may find it more quickly. If you found this post helpful consider giving it a "Thumbs Up."

Hi @DianaBirkelbach ,

 

Thanks for your reply. But I would prefer to call the function app directly instead of calling it via a plugin. I am still keeping this issue open.

 

Thanks

@naveenrerd 

Are you having issues authenticating or invoking Azure Functions? Also this is more of an Azure related question rather than PowerApps. You can call any HTTP request from PCF and that applies to Azure services as well.

You can use MSAL Library for authentication, but I always had issues with it and have been using JQuery/XHR/Fetch for getting the token. Also, I normally would do an App Registrations and create a secret so I do not need to show Login Pop-up. Use registered App's Client Id and created Secret for Basic Auth and retrieve token.

Once, token is retrieved; you can invoke the function (again using JQuery/XHR/Fetch) and pass in the retrieved token as Access Token in the header. 

You can also validate your connections using Postman (which also provides you with code snippets)

Below is the code that will provide you with access_token which can be used as a Bearer Token when calling Azure Function.

 

const getAuthenticationToken = async function() {
    let config = require(__dirname + "/../config/config.json");
    var request = require('request');
    var options = {
        'method': 'POST',
        'url': 'https://login.microsoftonline.com/' + config.tenantId + '/oauth2/token',
        'headers': {
            'Authorization': 'Basic ' + new Buffer(config.clientId + ":" + config.clientSecret).toString('base64'),
            'Content-Type': 'application/x-www-form-encoded'
        },
        form: {
            'grant_type': 'client_credentials',
            'resource': 'your Azure function URL'
        }
    };

    return new Promise(
        (resolve, reject) => {
            request(options, function (error, response) {
                if (error) {
                    reject(error);
                }
                resolve(JSON.parse(response.body));
            });
        }
    );
}

module.exports.getAuthenticationToken = getAuthenticationToken;

 


----
Danish Naglekar | Power Maverick
If this post helps, then please consider Accept it as the solution to help the other members.


Power Maverick | Microsoft Business Application MVP

Hi @PowerMaverick ,

 

Thank you for you suggestion. As I am using frontend code, I cannot expose client secret as any user can read the client secret. This is again a security vulnerability. I am expecting something like aadhttp which SharePoint provides along with context so that AAD can be accessed with current logged in users.

 

Thanks,

Naveen Kumar Ravichandran

@naveenrerd- You could follow a similar approach to this https://taerimhan.com/consuming-microsoft-graph-api-from-pcf-control/. Although this is for Graph API, the approach is something that you could use to execute Functions protected by Azure AD using the same MSAL library.

View solution in original post

Helpful resources

Announcements
UG GA Amplification 768x460.png

Launching new user group features

Learn how to create your own user groups today!

Community Connections 768x460.jpg

Community & How To Videos

Check out the new Power Platform Community Connections gallery!

Welcome Super Users.jpg

Super User Season 2

Congratulations, the new Super User Season 2 for 2021 has started!

Carousel 2021 Release Wave 2 Plan 768x460.jpg

2021 Release Wave 2 Plan

Power Platform release plan for the 2021 release wave 2 describes all new features releasing from October 2021 through March 2022.

Users online (1,783)