Calling Microsoft Graph from Power Automate and other daemon apps with delegated permissions
Last updated on November 3, 2024
Whenever you have some kind of an automated background process that needs to perform tasks via Microsoft Graph without logged-in user identity, you should always configure your app to use application permissions rather than delegated. However, there are quite a few Graph operations (e.g., those related to Planner) that only support delegated permissions, which is why sometimes we need to set up our background processes to run with a service account.
There are two ways of achieving this in Azure Logic Apps and Power Automate:
- Preferably you should always create a a custom connector for situations like this. The benefit of a custom connector is that the service account can have multifactor authentication (MFA) enabled, and there’s no need to set up Azure Key Vault for storing the password or client secret. To sum it up, this method is more secure.
- However, if for some reason you can’t create a custom connector, you can implement the OAuth resource owner credentials flow directly in your daemon application — whether your application is an Azure Logic App, a Power Automate flow, an Azure function or some other background process.
Because Microsoft has already provided us with a great tutorial for creating custom connectors, I won’t go through the steps here. Instead, I’ll focus on the alternative.
Table of Contents
- Gather the required information for authentication
- Authenticate to Microsoft Graph using the OAuth 2.0 password flow
- Parse the authorization response to obtain the access token
- Include the access token in the request when making calls to Microsoft Graph
Gather the required information for authentication
To get authorized to call Microsoft Graph, we’ll need the following pieces of information:
- Tenant ID
- Client ID
- Client secret
- Username
- Password
The username and password should be a set of service credentials that have permissions to the resources you are planning to manage via Microsoft Graph. If you are planning on managing Teams via Graph, the service account also needs to have a Teams license.
The tenant ID, client ID and client secret you can obtain from the application registration you are using for authentication. If you haven’t set up an application registration for your flow in Azure AD yet, feel free to check out another one of my blog posts for instructions.
Both the user account password and the client secret are considered sensitive information. If they were leaked, they’d allow a potential attacker to perform tasks (within the scope of granted permissions) on our tenant via Microsoft Graph. Because of this, we need to store them in a secure place, namely Azure Key Vault. You should utilize an Azure Key Vault also in other types of daemon apps, such as Azure functions.
There is an Azure Key Vault connector in both Azure Logic Apps and Power Automate. The Get Secret action allows us to retrieve secret values from the specified key vault. To be able to do that, the Azure Logic Apps managed identity or the Power Automate flow author need to be granted “Get Secret” permissions to the vault. After that, you can get secrets from the vault simply by providing their name in the action.
Note that by default the secret value fetched from Azure Key Vault will show on the run logs which is a security issue. To prevent that from happening, we need to adjust the Get secret action settings, and turn on securing inputs and outputs. When we do this, the secret value won’t be visible on the run logs. It won’t show in any of the actions where it is used. Great!
We also need to restrict the edit permissions of the flow to the minimum. If we allow a lot of people to edit the flow, someone could edit the Get secret action settings to display the fetched values on the run log again, and this way discover the sensitive information we are attempting to hide.
After fetching the top-secret information from the key vault, I tend to put all of the pieces of information listed above into variables. This way it is easy for me to update them if they are changed or if I move the Power Automate flow to another tenant.
When the secret values are also in variables, it is faster to add them into actions. As I mentioned before, the secret values won’t show on the run log in relation to any of the actions where they are used, including these variables. The secret values are always hidden once the secure inputs and outputs options are turned on.
Authenticate to Microsoft Graph using the OAuth 2.0 password flow
Now that we’ve gathered all the information required for authentication, let’s do just that. What I am showing you here is in no way Azure Logic Apps or Microsoft Power Automate specific. We authenticate against Azure AD using OAuth 2.0 password flow (a.k.a. resource owner credentials flow) with a simple REST request in order to obtain an access token for Microsoft Graph. You can use this same method, e.g., in a PowerShell script or a C# daemon application because the only requirement for this is that you are able to make an HTTP request. Basically in any situation in which you need to use delegated permissions but are unable to have user interaction.
What we do here is call the token endpoint of our tenant’s Azure AD which will provide us an access token for Microsoft Graph (the resource) in exchange for the information contained in the request body.
Before we construct the request, we need to URL encode the client secret. You should also do the same for the password if contains special characters (which it should).
encodeUriComponent(variables('ClientSecret'))
After that, let’s configure an HTTP action with the following information:
The HTTP method | POST |
The request URL | https://login.microsoftonline.com/your tenant id/oauth2/token |
The Content-Type header | application/x-www-form-urlencoded |
The request body | grant_type=password&resource=https://graph.microsoft.com&client_id=your client id&username=service account username&password=the URL encoded service account password&client_secret=the URL encoded client secret |
Parse the authorization response to obtain the access token
When the above HTTP request is made, we get authenticated, and in the response, we’ll receive the access token for calling Microsoft Graph — amongst other pieces of information. Before we can use the access token, we need to parse the JSON in the response body to make the token available to us in the dynamic content panel.
Copy paste this schema to the action:
{ "type": "object", "properties": { "token_type": { "type": "string" }, "expires_in": { "type": "string" }, "ext_expires_in": { "type": "string" }, "expires_on": { "type": "string" }, "not_before": { "type": "string" }, "resource": { "type": "string" }, "access_token": { "type": "string" } } }
Include the access token in the request when making calls to Microsoft Graph
Now, the access token we just extracted doesn’t quite work on its own. We need to add the text “Bearer ” (notice the whitespace at the end) in front of the access token.
To make our lives a little bit easier, let’s create a new variable where we do just that. In the future, we can then just reference the variable without having to remember to type “Bearer ” to every request. Alternatively, you can use the Compose action to achieve the same thing; they are in fact a bit more performant than variables (a good thing to remember if your flows/apps are ever taking a long time to run).
There are actually two ways of how you can include the bearer token in the request.
1) Include the Authorization header.
2) Use the Raw authentication method.
Either one works just as well, so it really comes down to personal preference. Using the Raw dropdown value saves you from potential typos, but if you make a lot of HTTP requests on other platforms too, it might feel more natural to use the Authorization header.
Afterword
As I mentioned in the beginning, this is not the only way of authenticating to Microsoft Graph from Azure Logic Apps or Microsoft Power Automate using delegated permissions. You should always opt for creating a custom connector if at all possible. In that scenario, you’ll authenticate the connector when building the flow. It requires a bit more effort to set up but it allows the service account to use MFA, and you don’t need to set up an Azure key vault for storing the sensitive information.
Hopefully, you enjoyed reading this article, and if you’d like to hear from me again, the best way to ensure that is to follow me on Twitter. That’s the place where I’ll announce my new blog articles, share some tips and tricks and sometimes also offer glimpses to other things that I’m up to.
Thank you for reading and until next time!
Laura
Hi Laura,
thank you bery much for this awesome flow, very usefull !
There is one little detail to make this post ever better (could it be ?) :
as well as we Url encode the client secret, we have to Url encode the password, as it is also part of the request url !
This one cost me some hours of head banging ! 😉
Thanks a lot for the great work you do
Thanks for pointing that out Julien! I’ll add this to the blog post the next time I’m updating it. 🙂
Laura
Thank you so much for the write-up it really helps!
Hi Nikhil,
Aww, great to hear! You are welcome. 🙂
Laura
Hi Laura, Thanks a lot of this blog post. I have an issue getting access token due MFA enabled and when I try and get the access token, I get an error. Do you know if there is any workaround for this issue.
Thanks and Regards,
Nidhi
Hi Nidhi,
When your service account has MFA enabled, you need to create a custom connector as instructed in this tutorial: https://docs.microsoft.com/en-au/graph/tutorials/power-automate
Laura
Hi, just wondering on the last step where the dynamic content {id} is coming from? I don’t have the option to insert this content, and I don’t see where it is explicitly or otherwise defined (Not in the JSON parser template). Am I missing something? Thanks very much!
Hi Jacob,
The ID is coming from some other action in the flow. It is not relevant to the blog post topic (authentication), and hence I have not included it in the post.
Laura
Hi Laura, Thanks a lot for the great demonstration.
I tried to register an application on Azure AD but I’m not an administrator. And I add some permissions like Files.Read.All / Mail.Send which don’t need admin consent. But when I tried to get Access token by Soap UI, I always get a response like this: “error”:”invalid_grant”,”error_description”:”AADSTS65001: The user or administrator has not consented to use the application”
Is that means when we use “grant_type=password” to get an Access token, an administrator must grant consent for this application and no matter what kind of permission we required?
Thanks Laura and respect your kind help
Hi Gerald,
Even though the permissions don’t require admin consent, they still need approval from the individual user who is about to use your application. Try navigating to the app URI in your browser first and log in using the service account. That should pop up the consent view for the user. After the service account has approved the permissions, you should be able to authenticate just fine in a daemon app as well. Alternatively, an administrator can consent on behalf of all users in the organisation.
Laura