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, thanks for the article! I’m trying to implement that in a custom PowerShell script and I wonder what exactly is referred to by the “service account” (service account username and service account password used in oauth2 token request). Is this a normal Entra ID user account that is just used for this purpose ? Or is there some special kind of service account dedicated for such things?
Thanks again!
Hey Sam! You guessed it: a service account is a regular user account that is used only for running the automation. We should not use any person’s account for running processes, because if the person leaves the company and their account gets disabled, the automation stops working.
Laura
This is great content, thank you.
I just have a small question for you.
If I have a power apps that calls a power automate that has an http request action that is set up with delegation permissions the way it is set up in this article, will each user using the power apps only see the responses that are related to there account’s rights? Or will they be able to see the response that are related to the service account?
I hope that my question is understandble.
Hi Anis,
Thank you for your kind comment!
To answer your question: They will see the responses that are related to the service account.
Laura
Look and you shall find 😀
https://laurakokkarinen.com/authenticating-to-azure-ad-protected-apis-with-managed-identity-no-key-vault-required/
Hi Laura,
Thanks a bunch for this post. It really helped me a lot. Do you know where I can read a similar post explaining how to use the Custom Connector? I have a Custom Connector created with API permissions (delegated), because I could not get it to work with “Application” permissions. I like the way you make an Authentication header and include this every time. It works for me, but I think it is a bit dumb that I have a Custom Connector and use delegated permissions (I do use secure output).
Cheers,
Thomas
THANK YOU. I have been trying to work on an “at mention” in Teams for the last day and couldn’t get my head around the user delegation (worked fine in Graph Explorer where I was obviously logging in..) and couldn’t understand what I needed to construct to pass in the user/pass in the Flow.
You are most welcome, Jim. 🙂 Great to hear that you got your solution working!
Laura