Calling Microsoft Graph from Power Automate and other daemon apps with delegated permissions

Calling Microsoft Graph from Power Automate and other daemon apps with delegated permissions

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

  1. Gather the required information for authentication
  2. Authenticate to Microsoft Graph using the OAuth 2.0 password flow
  3. Parse the authorization response to obtain the access token
  4. 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



48 thoughts on “Calling Microsoft Graph from Power Automate and other daemon apps with delegated permissions”

  • Hi Laura,
    Would this approach (DELEGATED) work for reading emails also? I mean for permissions like Mail.ReadWrite and use the URL “https://graph.microsoft.com/v1.0/me/mailFolders/Inbox/messages”.

    I was able to make request for access_token, but when I do a GET request for messages – I am getting “Access is denied. Check credentials and try again.”

    Little Background: We have a linux based robotic application (in LISP language) to read application support mailbox and respond appropriately to customer emails. Currently the mail reading is done using the IMAP access protocol. As per new security policy we need to use Microsoft Graph APIs to access/read our application mail-box.

    We need to use “resource owner password credential” approach as we do not have any web (kind of daemon app).

    We wanted to use “Application permissions” like “Mail.ReadWrite” & “Mail.Send”. But these application permissions would give access to all the mail-boxes in the organization. So my administrator has denied our request.

    Looks like delegate permissions is the only way. For this I give a consent like a normal user, from browser. It takes me to the rediret-url. After this I am able send POST request (linux script) and get the access_token. When I send a GET request for inbox messages, I am getting “Access is denied”

  • Hi Laura,

    Thanks for sharing the article. It is very informative. I just have one question. Is Password Grant flow is supported by Azure AD as I didn’t find any piece of information in Microsoft documentation?

    • Hi Jeremy,

      Yes, I’m referencing that article in the afterword section. Takes you to a different page to start off with but once you click ”Begin”, it is the same tutorial. 🙂

      Laura

Leave a Reply

Your email address will not be published. Required fields are marked *

This site uses Akismet to reduce spam. Learn how your comment data is processed.