Category Archives: OAuth2

Protect Logic Apps with Azure AD OAuth – Part 2 Expose Logic App as API

This blog article will build on the previous blog post published, Protect Logic Apps with Azure AD OAuth โ€“ Part 1 Management Access | GoToGuy Blog, which provided some basic understanding around authorizing to Logic Apps request triggers using OAuth and Access Tokens.

In this blog I will build on that, creating a scenario where a Logic App will be exposed as an API to end users. In this API, I will call another popular API: Microsoft Graph.

My scenario will use a case where end users does not have access themselves to certain Microsoft Graph requests, but where the Logic App does. Exposing the Logic App as an API will let users be able to authenticate and authorize, requesting and consenting to the custom Logic App API permissions I choose. Some of these permissions can users consent to themselves, while other must be admin consented. This way I can use some authorizing inside the Logic App, and only let the end users be able to request what they are permitted to.

I will also look into assigning users and groups, and using scopes and roles for additional fine graining end user and principal access to the Logic App.

A lot of topics to cover, so let’s get started by first creating the scenario for the Logic App.

Logic App calling Microsoft Graph API

A Logic App can run requests against the Microsoft Graph API using the HTTP action and specifying the method (GET, POST, etc) and resource URI. For authentication against Graph from the Logic App you can use either:

  • Using Azure Active Directory OAuth and Client Credentials Flow with Client Id and Secret.
  • Using System or User Assigned Managed Identity.

Permissions for Microsoft Graph API are either using “delegated” (in context of logged in user) or “application” (in context of application/deamon service). These scenarios using Logic App will use application permissions for Microsoft Graph.

PS! Using Logic Apps Custom Connectors (Custom connectors overview | Microsoft Docs) you can also use delegated permissions by creating a connection with a logged in user, but this outside of the scope of this article.

Scenario for using Microsoft Graph in Logic App

There are a variety of usage scenarios for Microsoft Graph, so for the purpose of this Logic App I will focus on one of the most popular: Device Management (Intune API) resources. This is what I want the Logic App to do in this first phase:

  • Listing a particular user’s managed devices.
  • Listing all of the organization’s managed devices.
  • Filtering managed devices based on operating system and version.

In addition to the above I want to implement the custom API such that any assigned user can list their own devices through end-user consent, but to be able to list all devices or any other user than your self you will need an admin consented permission for the custom API.

Creating the Logic App

In your Azure subscription, add a new Logic App to your chosen resource group and name it according to your naming standard. After the Logic App is created, you will need add the trigger. As this will be a custom API, you will need it to use HTTP as trigger, and you will also need a response back to the caller, so the easiest way is to use the template for HTTP Request-Response as shown below:

Your Logic App will now look like this:

Save the Logic App before proceeding.

Create a Managed Identity for the Logic App

Exit the designer and go to the Identity section of the Logic App. We need a managed identity, either system assigned or user assigned, to let the Logic App authenticate against Microsoft Graph.

A system assigned managed identity will follow the lifecycle of this Logic App, while a user assigned managed identity will have it’s own lifecycle, and can be used by other resources also. I want that flexibility, so I will create a user assigned managed identity for this scenario. In the Azure Portal, select to create a new resource and find User Assigned Managed Identity:

Create a new User Assigned Managed Identity in your selected resource group and give it a name based on your naming convention:

After creating the managed identity, go back to your Logic App, and then under Identity section, add the newly created managed identity under User Assigned Managed Identity:

Before we proceed with the Logic App, we need to give the Managed Identity the appropriate Microsoft Graph permissions.

Adding Microsoft Graph Permissions to the Managed Identity

Now, if we wanted the Logic App to have permissions to the Azure Rest API, we could have easily added Azure role assignments to the managed identity directly:

But, as we need permissions to Microsoft Graph, there are no GUI to do this for now. The permissions needed for listing managed devices are documented here: List managedDevices – Microsoft Graph v1.0 | Microsoft Docs.

So we need a minimum of: DeviceManagementManagedDevices.Read.All.

To add these permissions we need to run some PowerShell commands using the AzureAD module. If you have that installed locally, you can connect and proceed with the following commands, for easy of access you can also use the Cloud Shell in the Azure Portal, just run Connect-AzureAD first:

PS! You need to be a Global Admin to add Graph Permissions.

You can run each of these lines separately, or run it as a script:

# Microsoft Graph App Well Known App Id
$msGraphAppId = "00000003-0000-0000-c000-000000000000"

# Display Name if Managed Identity
$msiDisplayName="msi-ops-manageddevices" 

# Microsoft Graph Permission required
$msGraphPermission = "DeviceManagementManagedDevices.Read.All" 

# Get Managed Identity Service Principal Name
$msiSpn = (Get-AzureADServicePrincipal -Filter "displayName eq '$msiDisplayName'")

# Get Microsoft Graph Service Principal
$msGrapSpn = Get-AzureADServicePrincipal -Filter "appId eq '$msGraphAppId'"

# Get the Application Role for the Graph Permission
$appRole = $msGrapSpn.AppRoles | Where-Object {$_.Value -eq $msGraphPermission -and $_.AllowedMemberTypes -contains "Application"}

# Assign the Application Role to the Managed Identity
New-AzureAdServiceAppRoleAssignment -ObjectId $msiSpn.ObjectId -PrincipalId $msiSpn.ObjectId -ResourceId $msGrapSpn.ObjectId -Id $appRole.Id

Verify that it runs as expected:

As mentioned earlier, adding these permissions has to be done using script commands, but there is a way to verify the permissions by doing the following:

  1. Find the Managed Identity, and copy the Client ID:
  1. Under Azure Active Directory and Enterprise Applications, make sure you are in the Legacy Search Experience and paste in the Client ID:
  1. Which you then can click into, and under permissions you will see the admin has consented to Graph permissions:

The Logic App can now get Intune Managed Devices from Microsoft Graph API using the Managed Identity.

Calling Microsoft Graph from the Logic App

Let’s start by adding some inputs to the Logic App. I’m planning to trigger the Logic App using an http request body like the following:

{
 "userUpn": "someuser@elven.no",
 "operatingSystem": "Windows",
 "osVersion": "10"
}

In the Logic App request trigger, paste as a sample JSON payload:

The request body schema will be updated accordingly, and the Logic App is prepared to receive inputs:

Next, add a Condition action, where we will check if we should get a users’ managed devices, or all. Use an expression with the empty function to check for userUpn, and another expression for the true value, like below:

We will add more logic and conditions later for the filtering of the operating system and version, but for now add an HTTP action under True like the following:

Note the use of the Managed Identity and Audience, which will have permission for querying for managed devices.

Under False, we will get the managed devices for a specific user. So add the following, using the userUpn input in the URI:

Both these actions should be able to run successfully now, but we will leave the testing for a bit later. First I want to return the managed devices found via the Response action.

Add an Initialize Variable action before the Condition action. Set the Name and Type to Array as shown below, but the value can be empty for now:

Next, under True and Get All Managed Devices, add a Parse JSON action, adding the output body from the http action and using either the sample response from the Microsoft Graph documentation, or your own to create the schema.

PS! Note that if you have over 1000 managed devices, Graph will page the output, so you should test for odata.nextLink to be present as well. You can use the following anonymized sample response for schema which should work in most cases:

{
     "@odata.context": "https://graph.microsoft.com/v1.0/$metadata#deviceManagement/managedDevices",
     "@odata.count": 1000,
     "@odata.nextLink": "https://graph.microsoft.com/v1.0/deviceManagement/managedDevices?$skiptoken=",
     "value": [
         {
             "id": "id Value",
             "userId": "User Id value",
             "deviceName": "Device Name value",
             "managedDeviceOwnerType": "company",
             "operatingSystem": "Operating System value",
             "complianceState": "compliant",
             "managementAgent": "mdm",
             "osVersion": "Os Version value",
             "azureADRegistered": true,
             "deviceEnrollmentType": "userEnrollment",
             "azureADDeviceId": "Azure ADDevice Id value",
             "deviceRegistrationState": "registered",
             "isEncrypted": true,
             "userPrincipalName": "User Principal Name Value ",
             "model": "Model Value",
             "manufacturer": "Manufacturer Value",
             "userDisplayName": "User Display Name Value",
             "managedDeviceName": "Managed Device Name Value"
         }
     ]
 }

PS! Remove any sample response output from schema if values will be null or missing from your output. For example I needed to remove the configurationManagerClientEnabledFeatures from my schema, as this is null in many cases.

Add another Parse JSON action under the get user managed devices action as well:

Now we will take that output and do a For Each loop for each value. On both sides of the conditon, add a For Each action, using the value from the previous HTTP action:

Inside that For Each loop, add an Append to Array variable action. In this action we will build a JSON object, returning our chosen attributes (you can change to whatever you want), and selecting the properties from the value that was parsed:

Do the exact same thing for the user devices:

Now, on each side of the condition, add a response action, that will return the ManagedDevices array variable, this will be returned as a JSON som set the Content-Type to application/json:

Finally, remove the default response action that is no longer needed:

The complete Logic App should look like the following now:

As I mentioned earlier, we’ll get to the filtering parts later, but now it’s time for some testing.

Testing the Logic App from Postman

In the first part of this blog post article series, Protect Logic Apps with Azure AD OAuth โ€“ Part 1 Management Access | GoToGuy Blog, I described how you could use Postman, PowerShell or Azure CLI to test against REST API’s.

Let’s test this Logic App now with Postman. Copy the HTTP POST URL:

And paste it to Postman, remember to change method to POST:

You can now click Send, and the Logic App will trigger, and should return all your managed devices.

If you want a specific users’ managed devices, then you need to go to the Body parameter, and add like the following with an existing user principal name in your organization:

You should then be able to get this users’ managed devices, for example for my test user this was just a virtual machine with Window 10:

And I can verify a successful run from the Logic App history:

Summary so far

We’ve built a Logic App that uses it’s own identity (User Assigned Managed Identity) to access the Microsoft Graph API using Application Permissions to get managed devices for all users or a selected user by UPN. Now it’s time to exposing this Logic App as an API son end users can call this securely using Azure AD OAuth.

Building the Logic App API

When exposing the Logic App as an API, this will be the resource that end users will access and call as a REST API. Consider the following diagram showing the flow for OpenID Connect and OAuth, where Azure AD will be the Authorization Server from where end users can request access tokens where the audience will be the Logic App resource:

Our next step will be to create Azure AD App Registrations, and we will start with the App Registration for the resource API.

Creating App Registration for Logic App API

In your Azure AD tenant, create a new App Registration, and call it something like (YourName) LogicApp API:

I will use single tenant for this scenario, leave the other settings as it is and create.

Next, go to Expose an API:

Click on Set right next to Application ID URI, and save the App ID URI to your choice. You can keep the GUID if you want, but you can also type any URI value you like here (using api:// or https://). I chose to set the api URI to this:

Next we need to add scopes that will be the permissions that delegated end users can consent to. This will be the basis of the authorization checks we can do in the Logic App later.

Add a scope with the details shown below. This will be a scope end users can consent to themselves, but it will only allow them to read their own managed devices:

Next, add another Scope, with the following details. This will be a scope that only Admins can consent to, and will be authorized to read all devices:

You should now have the following scopes defined:

Next, go to the Manifest and change the accessTokenAcceptedVersion from null to 2, this will configure so that Tokens will use the OAuth2 endpoints:

That should be sufficient for now. In the next section we will prepare for the OAuth client.

Create App Registration for the Logic App Client

I choose to create a separate App Registration in Azure AD for the Logic App Client. This will represent the OAuth client that end users will use for OAuth authentication flows and requesting permissions for the Logic App API. I could have configured this in the same App Registration as the API created in the previous section, but this will provide better flexibility and security if I want to share the API with other clients also later, or if I want to separate the permission grants between clients.

Go to App Registrations in Azure AD, and create a new registration calling it something like (yourname) LogicApp Client:

Choose single tenant and leave the other settings for now.

After registering, go to API permissions, and click on Add a permission. From there you can browse to “My APIs” and you should be able to locate the (yourname) Logic API. Select to add the delegated permissions as shown below:

These delegated permissions reflect the scopes we defined in the API earlier. Your App registration and API permission should now look like below. NB! Do NOT click to Grant admin consent for your Azure AD! This will grant consent on behalf of all your users, which will work against our intended scenario later.

Next, we need to provide a way for clients to authenticate using Oauth flows, so go to the Certificates & secrets section. Click to create a Client secret, I will name my secret after where I want to use it for testing later (Postman):

Make sure you copy the secret value for later:

(Don’t worry, I’ve already invalidated the secret above and created a new one).

Next, go to Authentication. We need to add a platform for authentication flows, so click Add a platform and choose Web. For using Postman later for testing, add the following as Redirect URI: https://oauth.pstmn.io/v1/callback

Next, we will also provide another test scenario using PowerShell or Azure CLI client, so click on Add a platform one more time, this time adding Mobile desktop and apps as platform and use the following redirect URI: urn:ietf:wg:oauth:2.0:oob

Your platform configuration should now look like this:

Finally, go to advanced and set yes to allow public client flows, as this will aid in testing from PowerShell or Azure CLI clients later:

Now that we have configured the necessary App registrations, we can set up the Azure AD OAuth Authorization Policy for the Logic App.

Configuring Azure AD OAuth Authorization Policy for Logic App

Back in the Logic App, create an Azure AD Authorization Policy with issuer and audience as shown below:

Note the Claims values:

We are using the v2.0 endpoint as we configured in the manifest of the App Registration that accessTokenAcceptedVersion should be 2. (as opposed to v1.0 issuer that would be in the format https://sts.windows.net/{tenantId}/). And the Audience claim would be our configured API App ID. (for v1.0 the audience would be the App ID URI, like api://elven-logicapp-api).

Save the Logic App, and we can now start to do some testing where we will use the client app registration to get an access token for the Logic App API resource.

Testing with Postman Client

The first test scenario we will explore is using Postman Client and the Authorization Code flow for getting the correct v2.0 Token.

A recommended practice when using Postman and reusing variable values is to create an Environment. I’ve created this Environment for storing my Tenant ID, Client ID (App ID for the Client App Registration) and Client Secret (the secret I created for using Postman):

Previously in this blog article, we tested the Logic App using Postman. On that request, select the Authorization tab, and set type to OAuth 2.0:

Next, under Token configuration add the values like the following. Give the Token a recognizable name, this is just for Postman internal refererence. Make sure that the Grant Type is Authorization Code. Note the Callback URL, this is the URL we configured for the App registration and Callback Url. In the Auth and Access Token URL, configure the use of the v2.0 endpoints, using TenantID from the environment variables. (Make sure to set the current environment top right). And for Client ID and Client Secret these will also refer to the environment variables:

One important step remains, and that is to correctly set the scope for the access token. Using something like user.read here will only produce an Access Token for Microsoft Graph as audience. So we need to change to the Logic App API, and the scope for ManagedDevices.Read in this case:

Let’s get the Access Token, click on the Get New Access Token button:

A browser window launches, and if you are not already logged in, you must log in first. Then you will be prompted to consent to the permission as shown below. The end user is prompted to consent for the LogicApp API, as well as basic OpenID Connect consents:

After accepting, a popup will try to redirect you to Postman, so make sure you don’t block that:

Back in Postman, you will see that we have got a new Access Token:

Copy that Access Token, and paste it into a JWT debugger like jwt.ms or jwt.io. You should see in the data payload that the claims for audience and issuer is the same values we configured in the Logic App Azure AD OAuth policy:

Note also the token version is 2.0.

Click to use the Token in the Postman request, it should populate this field:

Before testing the request, remember to remove the SAS query parameters from the request, so that sv, sp and sig are not used with the query for the Logic App:

Now, we can test. Click Send on the Request. It should complete successfully with at status of 200 OK, and return the managed device details:

Let’s add to the permission scopes, by adding the ManagedDevices.Read.All:

Remember just to have a blank space between the scopes, and then click Get New Access Token:

If I’m logged on with a normal end user, I will get the prompt above that I need admin privileges. If I log in with an admin account, this will be shown:

Note that I can now do one of two actions:

  1. I can consent only on behalf of myself (the logged in admin user), OR..
  2. I can consent on behalf of the organization, by selecting the check box. This way all users will get that permission as well.

Be very conscious when granting consents on behalf of your organization.

At this point the Logic App will authorize if the Token is from the correct issuer and for the correct audience, but the calling user can still request any managed device or all devices. Before we get to that, I will show another test scenario using a public client like PowerShell.

Testing with PowerShell and MSAL.PS

MSAL.PS is a perfect companion for using MSAL (Microsoft Authentication Library) to get Access Tokens in PowerShell. You can install MSAL.PS from PowerShellGallery using Install-Module MSAL.PS.

The following commands show how you can get an Access Token using MSAL.PS:

# Set Client and Tenant ID
$clientID = "cd5283d0-8613-446f-bfd7-8eb1c6c9ac19"
$tenantID = "104742fb-6225-439f-9540-60da5f0317dc"

# Get Access Token using Interactive Authentication for Specified Scope and Redirect URI (Windows PowerShell)
$tokenResponse = Get-MsalToken -ClientId $clientID -TenantId $tenantID -Interactive -Scope 'api://elven-logicapp-api/ManagedDevices.Read' -RedirectUri 'urn:ietf:wg:oauth:2.0:oob'

# Get Access Token using Interactive Authentication for Specified Scope and Redirect URI (PowerShell Core)
$tokenResponse = Get-MsalToken -ClientId $clientID -TenantId $tenantID -Interactive -Scope 'api://elven-logicapp-api/ManagedDevices.Read' -RedirectUri 'http://localhost'


MSAL.PS can be used both for Windows PowerShell, and for PowerShell Core, so in the above commands, I show both. Note that the redirect URI for MSAL.PS on PowerShell Core need to be http://localhost. You also need to add that redirect URI to the App Registration:

Running the above command will prompt an interactive logon, and should return a successful response saved in the $tokenResponse variable.

We can verify the response, for example checking scopes or copying the Access Token to the clipboard so that we can check the token in a JWT debugger:

# Check Token Scopes
$tokenResponse.Scopes

# Copy Access Token to Clipboard
$tokenResponse.AccessToken | Clip

In the first blog post of this article series I covered how you can use Windows PowerShell and Core to use Invoke-RestMethod for calling the Logic App, here is an example where I call my Logic App using the Access Token (in PowerShell Core):

# Set variable for Logic App URL
$logicAppUrl = "https://prod-05.westeurope.logic.azure.com:443/workflows/d429c07002b44d63a388a698c2cee4ec/triggers/request/paths/invoke?api-version=2016-10-01"

# Convert Access Token to a Secure String for Bearer Token
$bearerToken = ConvertTo-SecureString ($tokenResponse.AccessToken) -AsPlainText -Force

# Invoke Logic App using Bearer Token
Invoke-RestMethod -Method Post -Uri $logicAppUrl -Authentication OAuth -Token $bearerToken

And I can verify that it works:

Great. I now have a couple of alternatives for calling my Logic App securely using Azure AD OAuth. In the next section we will get into how we can do authorization checks inside the Logic App.

Authorization inside Logic App

While the Logic App can have an authorization policy that verifies any claims like issuer and audience, or other custom claims, we cannot use that if we want to authorize inside the logic app based on scopes, roles etc.

In this section we will look into how we can do that.

Include Authorization Header in Logic Apps

First we need to include the Authorization header from the OAuth access token in the Logic App. To do this, open the Logic App in code view, and add the operationOptions to IncludeAuthorizationHeadersInOutputs for the trigger like this:

        "triggers": {
            "manual": {
                "inputs": {
                    "schema": {}
                },
                "kind": "Http",
                "type": "Request",
                "operationOptions": "IncludeAuthorizationHeadersInOutputs"
            }
        }

This will make the Bearer Token accessible inside the Logic App, as explained in detail in my previous post: Protect Logic Apps with Azure AD OAuth โ€“ Part 1 Management Access | GoToGuy Blog. There I also showed how to decode the token to get the readable JSON payload, so I need to apply the same steps here:

After applying the above steps, I can test the Logic App again, and get the details of the decoded JWT token, for example of interest will be to check the scopes:

Implement Logic to check the Scopes

When I created the LogicApp API app registration, I added two scopes: ManagedDevices.Read and ManagedDevices.Read.All. The authorization logic I want to implement now is to only let users calling the Logic App and that has the scope ManagedDevices.Read.All to be able to get ALL managed devices, or to get managed devices other than their own devices.

The first step will be to check if the JWT payload for scope “scp” contains the ManagedDevices.Read.All. Add a Compose action with the following expression:

contains(outputs('Base64_to_String_Json').scp,'ManagedDevices.Read.All')

This expression will return either true or false depending on the scp value.

Next after this action, add a Condition action, where we will do some authorization checks. I have created two groups of checks, where one OR the other needs to be true.

Here are the details for these two groups:

  • Group 1 (checks if scp does not contain ManagedDevices.Read.All and calling user tries to get All managed devices):
    • Outputs('Check_Scopes') = false
    • empty(triggerBody()?['userUpn']) = true
  • Group 2 (checks if scp does not contain ManagedDevices.Read.All, and tries to get managed devices for another user than users’ own upn):
    • Outputs('Check_Scopes') = false
    • triggerBody()?['userUpn'] != Outputs('Base64_to_String_Json')['preferred_username']

If either of those two groups is True, then we know that the calling user tries to do something the user is not authorized to do. This is something we need to give a customized response for. So inside the True condition, add a new Response action with something like the following:

I’m using a status code of 403, meaning that the request was successfully authenticated but was missing the required authorization for the resource.

Next, add a Terminate action, so that the Logic App stops with a successful status. Note also that on the False side of the condition, I leave it blank because I want it to proceed with the next steps in the Logic Apps.

Test the Authorization Scope Logic

We can now test the authorization scopes logic implemented above. In Postman, either use an existing Access Token or get a new Token that only include the ManagedDevices.Read scope.

Then, send a request with an empty request body. You should get the following response:

Then, try another test, this time specifying another user principal name than your own, which also should fail:

And then test with your own user principal name, which will match the ‘preferred_username’ claim in the Access Token, this should be successful and return your devices:

Perfect! It works as intended, normal end users can now only request their own managed devices.

Let’s test with an admin account and the ManagedDevices.Read.All scope. In Postman, add that scope, and get a new Access Token:

When logging in with a user that has admin privileges you will now get a Token that has the scope for getting all devices, for which your testing should return 200 OK for all or any users devices:

Adding Custom Claims to Access Token

In addition to the default claims and scopes in the Access Token, you can customize a select set of additional claims to be included in the JWT data payload. Since the Access Token is for the resource, you will need to customize this on the App Registration for the LogicApp API.

In Azure AD, select the App Registration for the API, and go to API permissions first. You need to add the OpenID scopes first. Add the following OpenID permissions:

Your API App Registration should look like this:

Next, go to Token configuration. Click Add optional claim, and select Access Token. For example you can add the ipaddr and upn claims as I have done below:

Note the optional claims listed for the resource API registration:

Next time I get a new access token, I can see that the claims are there:

Summary of User Authorization so far

What we have accomplished now is that users can get an Access Token for the Logic App API resource. This is the first requirement for users to be able to call the Logic App, that they indeed have a Bearer Token in the Authorization Header that includes the configured issuer and audience.

In my demos I have shown how to get an access token using Postman (Authorization Code Flow) and a Public Client using MSAL.PS. But you can use any kind of Web application, browser/SPA or, Client App, using any programming libraries that either support MSAL or OpenID Connect and OAuth2. Your solution, your choice ๐Ÿ˜‰

After that I showed how you can use scopes for delegated permissions, and how you can do internal authorization logic in the Logic App depending on what scope the user has consented to/allowed to.

We will now build on this, by looking into controlling access and using application roles for principals.

Assigning Users and Restricting Access

One of the most powerful aspects of exposing your API using Microsoft Identity Platform and Azure AD is that you now can control who can access your solution, in this case call the Logic App.

Better yet, you can use Azure AD Conditional Access to apply policies for requiring MFA, devices to be compliant, require locations or that sign-ins are under a certain risk level, to name a few.

Let’s see a couple of examples of that.

Require User Assignment

The first thing we need to do is to change the settings for the Enterprise Application. We created an App registration for the LogicApp Client, for users to able to authenticate and access the API. From that LogicApp Client, you can get to the Enterprise Application by clicking on the link for Managed application:

In the Enterprise App, go to Properties, and select User assignment required:

We can now control which users, or groups, that can authenticate to and get access to the Logic App API via the Client:

If I try to log in with a user that is not listed under Users and groups, I will get an error message that the “signed in user is not assigned to a role for the application”:

PS! The above error will show itself a little different based on how you authenticate, the above image is using a public client, if you use Postman, the error will be in the postman console log, if you use a web application you will get the error in the browser etc.

Configuring Conditional Access for the Logic App

In addition to controlling which users and groups that can access the Logic App, I can configure a Conditional Access policy in Azure AD for more fine grained access and security controls.

In your Azure AD blade, go to Security and Conditional Access. If you already have a CA policy that affects all Applications and Users, for example requiring MFA, your LogicApp API would already be affected by that.

Note that as we are protecting the resource here, your Conditional Access policy must be targeted to the LogicApp API Enterprise App.

Click to create a new policy specific for the Logic App API, as shown below:

For example I can require that my Logic App API only can be called from a managed and compliant device, or a Hybrid Azure AD Joined device as shown below:

If I create that policy, and then tries to get an access token using a device that are not registered or compliant with my organization, I will get this error:

Summary of Restricting Access for Users and Groups

With the above steps we can see that by adding an Azure AD OAuth authorization policy to the Logic App, we can control which users and groups that can authenticate to and get an Access Token required for calling the Logic App, and we can use Conditional Access for applying additional fine grained access control and security policies.

So far we have tested with interactive users and delegated permission acccess scenarios, in the next section we will dive into using application access and roles for authorization scenarios.

Adding Application Access and Roles

Sometimes you will have scenarios that will let application run as itself, like a deamon or service, without requiring an interactive user present.

Comparing that to the OIDC and OAuth flow from earlier the Client will access the Resource directly, by using an Access Token aquired from Azure AD using the Client Credentials Flow:

Using the Client Credentials Flow from Postman

Back in the Postman client, under the Authorization tab, just change the Grant Type to Client Credentials like the following. NB! When using application access, there are no spesific delegated scopes, so you need to change the scope so that it refers to .default after the scope URI:

Click Get New Access Token, and after successfully authenticating click to Use Token. Copy the Token to the Clipboard, and paste to a JWT debugger. Let’s examine the JWT payload:

Note that the audience and issuer is the same as when we got an access token for an end user, but also that the JWT payload does not contain any scopes (scp) or any other user identifiable claims.

Using the Client Credentials Flow from MSAL.PS

To get an Access Token for an application client in MSAL.PS, run the following commands:

# Set Client and Tenant ID
$clientID = "cd5283d0-8613-446f-bfd7-8eb1c6c9ac19"
$tenantID = "104742fb-6225-439f-9540-60da5f0317dc"
# Set Client Secret as Secure String (keep private)
$clientSecret = ConvertTo-SecureString ("<your secret in plain text") -AsPlainText -Force

# Get Access Token using Client Credentials Flow and Default Scope
$tokenResponse = Get-MsalToken -ClientId $clientID -ClientSecret $clientSecret -TenantId $tenantID -Scopes 'api://elven-logicapp-api/.default'

You can then validate this Token and copy it to a JWT debugger:

# Copy Access Token to Clipboard
$tokenResponse.AccessToken | Clip

Calling the Logic App using Client Application

We can send requests to the Logic App using an Access Token in an application by including it as a Bearer Token in the Authorization Header exactly the same way we did previously, however it might fail internally if the Logic App processing of the access token fails because it now contains a different payload with claims:

Looking into the run history of the Logic App I can see that the reason it fails is that it is missing scp (scopes) in the token.

This is expected when authenticating as an application, so we will fix that a little later.

A few words on Scopes vs. Roles

In delegated users scenarios, permissions are defined as Scopes. When using application permissions, we will be using Roles. Role permissions will always be granted by an admin, and every role permission granted for the application will be included in the token, and they will be provided by the .default scope for the API.

Adding Application Roles for Applications

Now, let’s look into adding Roles to our LogicApp API. Locate the App registration for the API, and go to the App roles | Preview blade. (this new preview let us define roles in the GUI, where until recently you had to go to the manifest to edit).

Next, click on Create app role. Give the app role a display name and value. PS! The value must be unique, so if you already have that value as a scope name, then you need to distinguish it eg. by using Role in the value as I have here:

The allowed member types give you a choice over who/what can be assigned the role. You can select either application or user/groups, or both.

Add another App Role as shown below:

You should now have the following two roles:

Assigning Roles to Application

I recommend that you create a new App Registration for application access scenarios. This way you can avoid mixing delegated and application permissions in the same app registration, it will make it easier to differentiate user and admins consents, and secret credentials will be easier to separate, and you can use different settings for restricting access using Azure AD Users/Groups and Conditional Access.

So create a new App registration, call it something like (Yourname) LogicApp Application Client:

Choose single tenant and leave the other settings as default. Click Register and copy the Application (Client ID) and store it for later:

Next, go to Certificates & secrets, and create a new Client secret:

Copy the secret and store it for later.

Go to API permissions, click Add a permission, and from My APIs, find the LogicApp API. Add the Application permissions as shown below, these are the App Roles we added to the API earlier:

Under API permissions you can remove the Microsoft Graph user.read permission, it won’t be needed here, the two remaining permissions should be:

These you NEED to grant admin consent for, as no interactive user will be involved in consent prompt:

The admin consent are granted as shown below:

Now we can test getting access token via this new app registration, either use Postman or MSAL.PS , remember to use the new app (client) id and app (client) secret. I chose to add the two values to my Postman environment like this:

Next, change the token settings for Client Credentials flow so that the Client ID and Secret use the new variable names. Click to Get New Access Token:

After successfully getting the access token, click Use Token and copy it to clipboard so we can analyze it in the JWT debugger. From there we can indeed see that the roles claims has been added:

We will look for these roles claims in the Logic App later. But first we will take a look at how we can add these roles to users as well.

Assigning Roles to Users/Groups

Adding roles to users or groups can be used for authorizing access based on the roles claim. Go to the Enterprise App for the Logic App API registration, you can get to the Enterprise App by clicking on the Managed application link:

In the Enterprise App, under Users and Groups, you will already see the ServicePrincipal’s for the LogicApp Application Client with the Roles assigned. This is because these role permissions were granted by admin consent:

Click on Add user/group, add for a user in your organization the selected role:

You can add more users or groups to assigned roles:

Lets do a test for this user scenario. We need to do an interactive user login again, so change to using Authorization Code Flow in Postman, and change to the originial ClientID and ClientSecret:

Click to Get New Access Token, authenticate with your user in the browser (the user you assigned a role to), and then use the token and copy it to clipboard. If we now examine that token and look at the JWT data payload, we can see that the user has now a role claim, as well as the scope claim:

We can now proceed to adjust the authorization checks in the Logic App.

Customizing Logic App to handle Roles Claims

Previously in the Logic App we did checks against the scopes (scp claim). We need to do some adjustment to this steps, as it will fail if there are no scp claim in the Token:

Change to the following expression, with a if test that returns false if no scp claim, in addition to the original check for scope to be ManagedDevices.Read.All:

This is the expression used above:

if(empty(outputs('Base64_to_String_Json')?['scp']),false,contains(outputs('Base64_to_String_Json').scp,'ManagedDevices.Read.All'))

Similary, add a new Compose action, where we will check for any Roles claim.

This expression will also return false if either the roles claim is empty, or does not contain the ManagedDevices.Role.Read.All:

if(empty(outputs('Base64_to_String_Json')?['roles']),false,contains(outputs('Base64_to_String_Json').roles,'ManagedDevices.Role.Read.All'))

Next we need to add more checks to the authorization logic. Add a new line to the first group, where we also check the output of the Check Roles action to be false:

In the above image I’ve also updated the action name and comment to reflect new checks.

To the second group, add two more lines, where line number 3 is checking outputs from Check Roles to be false (same as above), and line 4 do a check if the roles claim contains the role ManagedDevices.Role.Read:

The complete authorization checks logic should now be:

And this is the summary of conditions:

  • Group 1 (checks if scp does not contain ManagedDevices.Read.All and roles does not contain ManagedDevices.Role.Read.All and calling user tries to get All managed devices):
    • Outputs('Check_Scopes') = false
    • empty(triggerBody()?['userUpn']) = true
    • Outputs('Check_Roles') = false
  • Group 2 (checks if scp does not contain ManagedDevices.Read.All and roles does not contain ManagedDevices.Role.Read.All, and tries to get managed devices for another user than users’ own upn, and roles does not contain ManagedDevices.Role.Read):
    • Outputs('Check_Scopes') = false
    • triggerBody()?['userUpn'] != Outputs('Base64_to_String_Json')['preferred_username']
    • Outputs('Check_Roles') = false
    • contains(outputs('Base64_to_String_Json')?['roles'],'ManagedDevices.Role.Read') = false

If any of the two groups of checks above returns true, then it means that the request was not authorized. To give a more customized response, change the response action like the following:

In the above action I have changed that response is returned as a JSON object, and then changed the body so that it returns JSON data. I have also listed the values from the token that the user/application use when calling the Logic App. The dynamic expression for getting roles claim (for which will be in an array if there are any roles claim) is:
if(empty(outputs('Base64_to_String_Json')?['roles']),'',join(outputs('Base64_to_String_Json')?['roles'],' '))
And for getting any scopes claim, which will be a text string or null:
outputs('Base64_to_String_Json')?['scp']

Test Scenario Summary

I’ll leave the testing over to you, but if you have followed along and customized the Logic App as I described above, you should now be able to verify the following test scenarios:

User/AppScopeRolesResult
UserManagedDevices.ReadCan get own managed devices.
Not authorized to get all devices or other users’ managed devices.
User (Admin)ManagedDevices.Read.AllCan get any or all devices.
UserManagedDevices,ReadManagedDevices.Role.ReadCan get own managed devices.
Can get other users’ managed devices by userUpn.
Not authorized to get all devices.
UserManagedDevices.ReadManagedDevices.Role.Read.AllCan get any or all devices.
ApplicationManagedDevices.Role.ReadCan any users’ managed devices by userUpn.
Not authorized to get all devices.
ApplicationManagedDevices.Role.Read.AllCan get any or all devices.

When testing the above scenarios, you need a new access token using either authorization code flow (user) or client credentials (application). For testing with roles and user scenarios, you can change the role assignments for the user at the Enterprise Application for the LogicAPI API. For testing with roles with application scenarios, make sure that you only grant admin consent for the applicable roles you want to test.

Final Steps and Summary

This has been quite the long read. The goal of this blog post was to show how your Logic App workflows can be exposed as an API, and how Azure AD OAuth Authorization Policies can control who can send requests to the Logic App as well as how you can use scopes and roles in the Access Token to make authorization decisions inside the Logic App. And even of more importance, integrating with Azure AD let’s you control user/group access, as well as adding additional security layer with Conditional Access policies!

My demo scenario was to let the Logic App call Microsoft Graph and return managed devices, which require privileged access to Graph API, and by exposing the Logic App as an API I can now let end users/principals call that Logic App as long as they are authorized to do so using my defined scopes and/or roles. I can easily see several other Microsoft Graph API (or Azure Management APIs, etc) scenarios using Logic App where I can control user access similarly.

Note also that any callers of the Logic App that now will try to call the Logic App using SAS access scheme will fail, as a Bearer Token is expected in the Authorization Header and the custom authorization actions that has been implemented. You might want to implement some better error handling if you like.

There’s an added bonus at the end of this article, where I add the filters for getting managed devices. But for now I want to thank you for reading and more article in this series will come later, including:

  • Calling Logic Apps protected by Azure AD from Power Platform
  • Protecting Logic App APIs using Azure API Management (APIM)

Bonus read

To complete the filtering of Managed Devices from Microsoft Graph, the Logic App prepared inputs of operatingSystem and osVersion in addition to userUpn. Let’s how we can implement that support as well.

After the initialize variable ManagedDevices action, add a Compose action. In this action, which I rename to operatingSystemFilter, I add a long dynamic expression:

This expression will check if the request trigger has an operatingSystem value, it not this value will be a empty string, but if not empty the I start building a text string using concat function where I build the filter string. There are some complexities here, amongs others using escaping of single apostroph, by adding another single apostroph etc. But this expression works:

if(empty(triggerBody()?['operatingSystem']),'',concat('/?$filter=operatingSystem eq ''',triggerBody()?['operatingSystem'],''''))

Next, add another Compose action and name it operatingSystemVersionFilter. This expression is even longer, checking the request trigger for osVersion, and if empty, it just returns the operatingSystemFilter from the previos action, but if present another string concat where I ‘and’ with the previous filter:

The expression from above image:

if(empty(triggerBody()?['osVersion']),outputs('operatingSystemFilter'),concat(outputs('operatingSystemFilter'),' and startswith(osVersion,''',triggerBody()?['osVersion'],''')'))

We can now add that output to the Graph queries, both when getting all or a specific user’s devices:

I can now add operatingSystem and osVersion to the request body when calling the Logic App:

And if I check the run history when testing the Logic App, I can see that the filter has been appended to the Graph query:

You can if you want also build more error handling logic for when if users specify the wrong user principalname, or any other filtering errors that may occur because of syntax etc.

That concludes the bonus tip, thanks again for reading ๐Ÿ™‚

Protect Logic Apps with Azure AD OAuth – Part 1 Management Access

Azure Logic Apps are great for creating workflows for your IT automation scenarios. Logic App workflows can be triggered using a variety of sources and events, including schedules, but a popular trigger is using a HTTP trigger for starting the Logic App workflow interactively or on-demand from outside the Logic App.

To trigger a Logic App using a HTTP trigger, you need to know the endpoint URL, for example:

This URL consist of the endpoint address of the Logic App and workflow trigger, and with the following query parameters:

  • api-version
  • sp (specifies permissions for permitted HTTP methods to use)
  • sv (SAS version to use)
  • sig (shared access signature)

Anyone with access to this URL and query parameters kan trigger the Logic App, so it’s very important to protect it from unauthorized access and use.

In this multi part blog post series we will look into how Logic Apps can be protected by Azure AD.

Scenarios this multi-part blog post articles will cover:

  • Provide Management Access Tokens and Restrict Issuer and Audience via OAuth
  • Restrict External Guest User Access
  • Expose Logic App as API
  • Restrict permitted Enterprise Application Users and Groups and Conditional Access policies.
  • Scopes and Roles Authorization in Logic Apps.
  • Logic Apps and APIM (Azure API Management).

Lets first look at the other methods for protecting Logic Apps you should be aware of.

Protect Logic Apps Keys and URLs

Before we move on to protecting Logic Apps with Azure AD Open Authentication (OAuth), lets take a quick summary of other protections you should be aware of:

  • Regenerate access keys. If you have reason to think SAS keys are shared outside your control, you can regenerate and thus making previous SAS keys invalid.
  • Create expiring Callback URLs. If you need to share URLs with people outside your organization or team, you can limit exposure by creating a Callback URL that expire on a certain date and time.
  • Create Callback URL with primary or secondary key. You can select to create the Callback URL with the specified primary or secondary SAS key.

The above methods should be part of any governance and security strategy for protecting Logic Apps that perform privilieged actions or might return sensitive data.

Protect Logic Apps via restricting inbound IP address

Another way to protect Logic Apps is to restrict from where the Logic App can be triggered via inbound IP address restrictions.

This opens up scenarios where you can specify your datacenter IP ranges, or only let other Logic Apps outbound IP addresses call nested Logic Apps, or only allow Azure API management to call Logic App.

Protect Logic Apps with Azure AD OAuth

By creating an Authtorization Policy for your Logic App you can use a Authorization header with a Bearer Token and require that the token contains the specified issuer, audience or other claims. Showing how that works in detail, and usable scenarios will be the main focus for this blog post.

Let’s start by building a basic Logic App we can use for demo purpose.

Creating a basic Logic App with HTTP Trigger and Response

In you Azure Subcription, create a new Logic App, specifying to use a HTTP trigger. In my example below I have named my Logic App “logicapp-test-auth”:

Next, add a Parse JSON action, where the Content is set to the trigger headers, as shown below. I’ve just specified a simple schema output, this can be customized later if needed:

After that activity, add a Response action to return data to the caller. In my example below I return a Status Code of 200 (OK), and set the Content-Type to application/json, and return a simple JSON body of UserAgent where the value is set to the parsed header output from the trigger, using dynamic expression:
body('Parse_JSON_Headers')?['User-Agent']

Testing Logic App with Postman

A great way to test and explore HTTP and REST API calls from your client is to use Postman (Download Postman | Try Postman for Free). When testing the above Logic App, paste in the HTTP POST URL for your trigger, and set the method to POST as shown below:

From the above image, you can see the URL, and also the query parameters listed (api-version, sp, sv, and sig, remember that these should be shared publicly).

When I send the request, it will trigger the Logic App, and should response back:

We can also verify the run history for the Logic App:

We have now successfully tested the Logic App using SAS authentication scheme, and can proceed to adding Azure AD OAuth. First we need to create an Authorization Policy.

Creating an Azure AD Authorization Policy

Under Settings and Authorization for your Logic App, add a new Authorization Policy with your name, and add the Issuer claim for your tenant. Issuer will be either https://sts.windows.net/{your-tenant-id}/ or https://login.microsoftonline.com/{your-tenant-id}/ depending on the version of the Access Token:

We will add more Claims later, but for now we will just test against the Issuer. Before we can test however, we need to get an Access Token. There are several ways to easily get an access token, basically we can consider one of the following two scenarios:

  1. Aquire an Access Token for well known Azure Management Resource Endpoints.
  2. Create an App Registration in Azure AD exposing an API.

We’ll cover App Registration and more advanced scenarios later, but for now we will get an Access Token using well known resource endpoints for Azure management.

PS! Just a quick note on Access Tokens aquired for Microsoft Graph resources: These cannot be used for Logic Apps Azure AD OAuth authorization policies, because Graph access tokens does not allow for signature validation.

Management Access Tokens

The following examples require that you either have installed Azure CLI (Install the Azure CLI | Microsoft Docs) or Az PowerShell (Install Azure PowerShell with PowerShellGet | Microsoft Docs).

Azure CLI

If you haven’t already, you will first need to login to Azure using az login. You can login interactively using default browser which supports modern authentication, including MFA, but if you are running multiple browsers and profiles it might be easier to use the device code flow:

az login --use-device-code

You will be prompted to open the microsoft.com/devicelogin page and enter the supplied device code, and after authentication with your Azure AD account, you will get a list of all subscriptions you have access to.

PS! If your account has access to subscriptions in multiple tenants, you can also specify which tenant to log into using:

az login --tenant elven.onmicrosoft.com --use-device-code

To get an Access Token you can just run az account get-access-token, like the following:

Let’s save that into a variable and get the token:

$accessToken = az account get-access-token | ConvertFrom-Json
$accessToken.accessToken | Clip

The above command copies the Access Token to the Clipboard. Let’s take a look at the token. Open the website jwt.ms or jwt.io, and paste in the token. From the debugger you can look at the decoded token payload. The most interesting part for now is the issuer (iss) and audience (aud), which tells us where the token has been issued from, and to which audience:

As we can see from above, the audience for the token is “management.core.windows.net”. You can also get an access token for a specific resource endpoint using:

$accessToken = az account get-access-token --resource-type arm | ConvertFrom-Json

To show all available resource endpoints use:

az cloud show --query endpoints

Now that we have the method to get the access token using Az Cli, lets take a look at Az PowerShell as well. For reference for az account command and parameters, see docs here: az account | Microsoft Docs

Azure PowerShell

First you need to login to your Azure Subscription by using:

Connect-AzAccount

If your account has access to multiple subscriptions in multiple tenant, you can use the following command to specify tenant:

Connect-AzAccount -Tenant elven.onmicrosoft.com

If there are multiple subscriptions, you might need to specify which subscription to access using Set-AzContext -Subscription <Subscription>. Tip, use Get-AzContext -ListAvailable for listing available subscriptions.

To get an access token using Az PowerShell, use the following command to save to variable and copy to clipboard:

$accessToken = Get-AzAccessToken
$accessToken.Token | Clip

We can once again look at the JWT debugger to verify the token:

As with Az CLI, you can also specify resource endpoint by using the following command in Az PowerShell specifying the resource Url:

$accessToken = Get-AzAccessToken -ResourceUrl 'https://management.core.windows.net'

Now that we have the Access Token for an Azure Management resource endpoint, let’s see how we can use that against the Logic App.

Use Bearer Token in Postman

From the previous test using Postman earlier in this article, go to the Authorization section, and specify Bearer Token, and then Paste the management access token you should still have in your clipboard like the following:

When clicking Send request, observe the following error:

We cannot combine both SAS (Shared Access Signature) and Bearer Token, so we need to adjust the POST URL to the Logic Apps. In Postman, this can be easily done in Postman under Params. Deselect the sp, sv and sig query parameters like the following, which will remove these from the POST URL:

When you now click Send request, you should get a successful response again, provided that tha access token is valid:

Perfect! We have now authorized triggering the Logic App using Azure AD OAuth, based on the Authorization policy:

And the Access Token that match that Issuer:

I will now add the audience to the Authorization Policy as well, so that only Access Tokens for the management endpoint resource can be used:

Any HTTP requests to the Logic App that has a Bearer Token that does not comply with the above Authorization Policy, will received this 403 – Forbidden error:

Test using Bearer Token in Azure CLI

You can trigger HTTP REST methods in Azure CLI using az rest --method .. --url ...

When using az rest an authorization header with bearer token will be automatically added, trying to use the url as resource endpoint (if url is one of the well known resource endpoints). As we will be triggering the Logic App endpoint as url, we need to specify the resource endpoint as well. In my example, I will run the following command for my Logic App:

az rest --method POST --resource 'https://management.core.windows.net/' --url 'https://prod-72.we
steurope.logic.azure.com:443/workflows/2fa8c6d0ed894b50b8aa5af7abc0f08b/triggers/manual/paths/invoke?api-
version=2016-10-01'

From above, I specify the resource endpoint of management.core.windows.net, from which the access token will be aquired for, and for url I specify my Logic App endpoint url, without the sp, sv and sig query parameters. This results in the following response:

So the Logic App triggered successfully, this time returning my console (Windows Terminal using Azure CLI).

Test using Bearer Token in Azure PowerShell

I will also show you how you can do a test using Az PowerShell. Make sure that you get an access token and saving the bearer token to a variable using this command first:

$accessToken = Get-AzAccessToken
$bearerToken = $accessToken.Token

I will also set the Logic App url to a variable for easier access:

$logicAppUrl = 'https://prod-72.westeurope.logic.azure.com:443/workflows/2fa8c6d0ed894b50b8aa5af7abc0f08b/triggers/manual/paths/invoke?api-version=2016-10-01'

There are 2 ways you can use Az PowerShell, either using Windows PowerShell or PowerShell Core.

For Windows PowerShell, use Invoke-RestMethod and add a Headers parameter specifying the Authorization header to use Bearer token:

Invoke-RestMethod -Method Post -Uri $logicAppUrl -Headers @{"Authorization"="Bearer $bearerToken"}

Running this should return this successfully, specifying my Windows PowerShell version (5.1) as User Agent:

For PowerShell Core, Invoke-RestMethod has now support for using OAuth as authentication, but I first need to convert the Bearer Token to a Secure String:

$accessToken = Get-AzAccessToken
$bearerToken = ConvertTo-SecureString ($accessToken.Token) -AsPlainText -Force

Then I can call the Logic App url using:

Invoke-RestMethod -Method Post -Uri $logicAppUrl -Authentication OAuth -Token $bearerToken

This should successfully return the following response, this time the User Agent is my PowerShell Core version (7.1):

Summary so far of Management Access Tokens and Logic Apps

At this point we can summarize the following:

  1. You can trigger your Logic App either by using SAS URL or using Bearer Token in Authorization Header, but not both at the same time.
  2. You can add an Azure AD Authorization Policy to your Logic App that specifies the Issuer and Audience, so that calling clients only can use Bearer Tokens from the specified issuer (tenant id), and audience (resource endpoint).
  3. While you cannot disable use of SAS signatures altogether, you can keep them secret, and periodically rollover, and only share the Logic App url endpoint and trigger path with clients.
  4. This is especially great for automation scenarios where users can use CLI or Azure PowerShell and call your Logic Apps securely using OAuth and Access Tokens.

In the next part we will look more into how we can customize the Logic App to get the details of the Access Token so we can use that in the actions.

Include Authorization Header in Logic Apps

You can include the Authorization header from the OAuth access token in the Logic App. To do this, open the Logic App in code view, and add the operationOptions to IncludeAuthorizatioNHeadersInOutputs for the trigger like this:

        "triggers": {
            "manual": {
                "inputs": {
                    "schema": {}
                },
                "kind": "Http",
                "type": "Request",
                "operationOptions": "IncludeAuthorizationHeadersInOutputs"
            }
        }

For subsequent runs of the Logic App, we can now see that the Authorization Header has been included:

And if we parse the headers output, we can access the Bearer Token:

If we want to decode that Bearer token to get a look into the payload claims, we can achieve that with some custom expression magic. Add a Compose action to the Logic App and use the following custom expression:

Let’s break it down:

  • The Replace function replaces the string ‘Bearer ‘ with blank (including the space after)
  • The Split function splits the token into the header, payload data, and signature parts of the JWT. We are interested in the payload, so refers to that with index [1]
split(replace(body('Parse_JSON_Headers')?['Authorization'], 'Bearer ',''), '.')[1]

Now it becomes a little tricky. We will have to use the base64ToString function to get the payload into readable string, but if the length of the payload isn’t dividable by 4 we will get an error. Therefore we need to see if we need to add padding (=), as explained here: (Base64 – Wikipedia).

First I get the length of the payload data, and then use modulo function to see if there are any remaining data after dividing by 4:

mod(length(outputs('Get_JWT_Payload')),4)

Then I can do a conditional logic, where I use concat to add padding (=) to make it dividable by 4:

if(equals(outputs('Length_and_Modulo'),1),concat(outputs('Get_JWT_Payload'),'==='),if(equals(outputs('Length_and_Modulo'),2),concat(outputs('Get_JWT_Payload'),'=='),if(equals(outputs('Length_and_Modulo'),3),concat(outputs('Get_JWT_Payload'),'='),outputs('Get_JWT_Payload'))))

After this I can use base64ToString to convert to a readable string object and format to JSON object:

json(base64ToString(outputs('Padded_JWT_Payload')))

Now that we have access to the claims, we can later be able to do some authorization in the Logic Apps, for example based on roles or scopes, but we can also get some information on which user that has called the Logic App.

In the Response action, add the following to return the Name claim:

And if I test the Logic App http request again, I can see at it indeed returns my name based on the claims from the access token:

We now have a way to identify users or principals calling the Logic App. The Authorization Policy for the Logic App now permits users and principals from my own organization (based on the issuer claim) and as long as the audience is for management.core.windows.net. But what if I want external access as well? Let’s add to the authorization policies next.

Add OAuth Authorization Policy for Guests

A logic app can have several Azure AD Authorization Policies, so if I want to let external guest users to be allowed to trigger the logic app, I will need to create another authorization policy that allows that issuer:

Lets also add the upn claim to the response, so it is easier to see which user from which tenant that triggered the Logic App:

When I now test with different users, internal to my tenant and external, I can see that it works and the response output is as expected:

Summary of Management Access Scenarios

The purpose of the above steps has been to provide a way for management scenarios, where users and principals can get an Access Token using one of the Azure Management well known endpoints, and provide that when calling the Logic App. The access token is validated using OAuth Authorization Policies, requiring specific issuer (tenant id) and audience (management endpoint). This way we can make sure that we don’t have to share the SAS details which lets users that have access to this URL call the Logic App without authentication.

In the next parts of this blog post articles, we will look into more advanced scenarios where we will expose the Logic App as an API and more.

Blog Series – Power’ing up your Home Office Lights: Part 8 – Using Power Automate Flows to Get and Set Lights State

This blog post is part of the Blog Series: Power’ing up your Home Office Lights with Power Platform. See introduction post for links to the other articles in the series:
https://gotoguy.blog/2020/12/02/blog-series—powering-up-your-home-office-lights-using-power-platform—introduction/

In Part 7 we built the main screen of the PowerApp, the topic for today is to build Flows and the PowerApp screen for controlling the Hue Lights:

If you want a quick summary of how this screen works, take a look at this video:

<YOUTUBE VIDEO PROCESSING, AVAILABLE SOON>

Building the Lights Control Screen

Start by adding another screen to the Hue PowerApp. If you have used a custom background color, logo and other graphical elements like I have you can do the same for this screen also. In addition to the label controls I’ve added for texts, I’ve added the following controls to my Hue PowerApp:

  • Small circle icons/shapes to reflect color states.
  • Toggle controls to set Light state On/Off and sync with Teams Presence On/Off.
  • Dropdown list for listing the Hue Lights.
  • Slider control for setting Brightness.
  • I’ve also added a Timer control and set it to not visible.

After adding and customizing the controls and named your controls after your chosen naming convention, your Hue PowerApp might look like the following:

Now we need to create a couple of Flows (as of today these are names Cloud Flows) for getting and setting Light State.

Creating Flow for Getting Lights and State

Create a new Instant Flow with PowerApps as Trigger. Name the Flow “Hue – Get Lights and State”. First add a Compose action, name the action “Access Token and User Name”, and select Ask in PowerApps under Dynamic Content:

Next, add a Parse JSON action below:

You can use the following schema:

{
    "type": "object",
    "properties": {
        "access_token": {
            "type": "string"
        },
        "username": {
            "type": "string"
        }
    }
}

We are now ready to query for the Lights for my Hue Remote API. But first it is helpful to understand a little about how the Hue Remote API returns lights. Earlier this year I published this blog post about exploring the Hue Remote API using Postman: Remote Authentication and Controlling Philips Hue API using Postman | GoToGuy Blog. For example when I query for all lights, https://api.meethue.com/bridge/{{username}}/lights/, I get a response similar to this:

The special thing to note here is that Hue returns every light as a named object identified by a light number. This is not an Array, so you cannot loop through that as you would expect. So I needed to think a little different in my solution.

I decided to create my own Array, and get the Lights one-by-one. For this I needed to start at light number “1”, and then do until some maximum value. I have currently 13 lights, so I created a variable for “13”. It makes it a little static, but at least it works with as little hassle as possible.

First add an Initialize variable action, of type Array and name arrayLights, and using the expression json('[]') as an empty json array as value:

Next, add two more Initialize variables actions, both of type Integer and named LightNumber with value 1, and NumberOfLights with value 13 (or whatever number of lights you have!).

Now, add a “Do until” action, setting LightNumber is greater than NumberOfLights as loop control:

Inside the Do until-loop, add a HTTP action, where we will run a GET query against the https://api.meethue.com/bridge/<whitelist identifer>/lights/<lightnumber>, using the access_token as a Bearer token in the Authorization Header:

This will return the first light state. Add a Append to array variable action, selecting the “arrayLights”, and adding the value like following:

This will add the Light number, the name of the Light source (body('Get_Light')?['name']) and if state on is true or false (body('Get_Light')?['state/on']).

Next action is to add an Increment variable action to increase the LightNumber by 1:

And last, outside the Do until, add a Response action so that we can return the data to the PowerApp. The important part here is to specify status code 200 and content-type application/json, and return the arrayLights variable as shown below:

Getting the Lights and State to the PowerApp

Now that we have to Flow for getting Lights and State, we can get that data into the PowerApp. Back in the PowerApp, select the Button control in the Main Screen with the name Control Lights. Click on the Action menu, and Power Automate to link the “Hue – Get Lights” and State Flow, and add the following lines to the OnSelect event:

Navigate(screenPresenceLights);
Set(wait,true);
ClearCollect(MyHueLights,'Hue-GetLightsandState'.Run(JSON(HueResponse)));
Set(wait,!true)

To explain, the Navigate(<screen>), is for changing to the other screen of course. I also use the Set(wait,true) and Set(wait,!true) on either side of the Flow run to make the PowerApp appear busy. And then, I save all the Lights and State back from the response from the Flow to a Collection, using ClearCollect and the Collection name “MyHueLights”. The Flow run expects that I supply the access_token and username, which I already has as a record variable in the shape of “HueResponse”. So, I’ll just add a JSON(..) function around that.

We can test. Hold down the “ALT” on your keyboard, and click on the “Control Lights” button. After this, go to the View menu and select Collections. You should see the “MyHueLights” collection, and a preview of the first 5 items:

Now we can get that data in to the PowerApp controls. Select the Drop Down list control, and set the Items property to “MyHueLights” and the Value to “Name”:

This should fill the Drop Down with Light names. Next, for the Drop Down list OnChange event, add the following:

Set(SelectedLight,(ddlMyLights.SelectedText));
Set(CheckStatus,false);
If(SelectedLight.State="True",Set(CheckStatus,true);Set(LightState,true),
Set(CheckStatus,true);Set(LightState,false)
)

So in the above expression for the OnChange event, I set a variable “SelectedLight” to the selected text from the Drop Down, and then I’m manipulating another variable with set “CheckStatus” and set “LightState”, depending on if the state on is true or false.

Proceeed to select the toggleLightState control, and set the Default property to the variable “LightState” and Reset property to “CheckStatus”:

We now have what we need for getting the Lights and State into the PowerApp. The next thing we need to build is to actually set Light states and colors back to the Hue Remote API.

Creating Flow for Setting Lights and State

Create a new Instant Flow with PowerApps as trigger, and name it “Hue – Set Light and State”. Start by adding the same two Compose actions as the “Hue – Get Light and State” Flow:

Next, add an Initialize variable action, with the name “Initialize LightNumber”, and select “Ask in PowerApps” under Dynamic content so that this input will be submitted from the PowerApp:

After that, add a Compose action. Name it “Body State”, and select “Ask in PowerApps” for input:

This input parameter is where we will supply the light state, colors etc.

Next add a Parse JSON action, using the outputs of the previous Body State input:

You can use the following schema:

{
    "type": "object",
    "properties": {
        "on": {
            "type": "boolean"
        },
        "xy": {
            "type": "array",
            "items": {
                "type": "number"
            }
        },
        "bri": {
            "type": "integer"
        }
    }
}

After this, add an HTTP action, using method PUT, and the address https://api.meethue.com/bridge/<whitelist identifier>/lights/<lightnumber>/state, and including the access_token as a Bearer token in the Authorization Header. For Body, construct the following JSON body:

And last, add a Response action to return status code and body to the PowerApp:

We now have a Flow in which we can call to set the light states in the PowerApp.

Control Light States from PowerApp

Lets start by turning selected Lights on and off. Select the Toggle control for Light State, and for the “OnCheck” event add the Power Automate Flow “Hue – Set Light and State” under the Action menu. For the OnCheck event add the following expression:

Set(MyLightState, "{'on':true }");
'Hue-SetLightandState'.Run(JSON(HueResponse), SelectedLight.LightNumber , MyLightState)

And for the UnCheck event:

Set(MyLightState, "{'on':false }");
'Hue-SetLightandState'.Run(JSON(HueResponse), SelectedLight.LightNumber , MyLightState)

So as you can see above, I’m using a variable named “MyLightState”, for dynamically storing the different light states I want to set and submit to the Flow. The ‘Hue-SetLightandState.Run’ takes three inputs in the form of access_token and username (via HueResponse variable), then selected LightNumber, and the MyLightState variable.

Next, lets go to the Slider control for setting Brightness. On the OnChange event, add the following expression:

Set(MyLightState, "{'bri': " & sliderBrightness.Value & " }");
'Hue-SetLightandState'.Run(JSON(HueResponse), SelectedLight.LightNumber , MyLightState)

Here I’m changing the state via the ‘bri’ value, and the sliderBrightness.Value. Btw, the Slider is set to minimum 2 and max 254, to support the values expected by the Hue API for ‘bri’.

And then finally we can set the color states for the three icons I have prepared. I have created pre-defined colors reflecting my presence status, green for available, red for busy and yellow for away.

For each of these, change the “OnSelect” event to the following:

Green (Available):

Set(MyLightState, "{'on':true, 'xy': [ 0.358189, 0.556853 ], 'bri':" & sliderBrightness.Value & " }");
 'Hue-SetLightandState'.Run(JSON(HueResponse), SelectedLight.LightNumber , MyLightState)

Red (Busy):

Set(MyLightState, "{'on':true, 'xy': [ 0.626564, 0.256591 ], 'bri':" & sliderBrightness.Value & " }");
 'Hue-SetLightandState'.Run(JSON(HueResponse), SelectedLight.LightNumber , MyLightState)

Yellow (Away):

Set(MyLightState, "{'on':true, 'xy': [ 0.517102, 0.474840 ], 'bri':" & sliderBrightness.Value & " }");
 'Hue-SetLightandState'.Run(JSON(HueResponse), SelectedLight.LightNumber , MyLightState)

A few words about the colors, this is something that could be a little difficult to get a grasp on. Hue has an explanation on the CIE color space and the “xy” resource here: Core Concepts – Philips Hue Developer Program (meethue.com).

You can also see some conversion functions here: Color Conversion Formulas RGB to XY and back – Philips Hue Developer Program (meethue.com)

Basically I’ve tested and learned. A good tip is to set the color you like using the official Hue Mobile App, and then read the state for the light.

Summary and Next Steps

The Hue PowerApp has now a working solution for getting Lights and State, as well as manually controlling colors, toggle on and off, and setting brightness.

In the next part of this blog post series, we will look into getting the presence status from Teams and show that in the Power App.

Thanks for reading!

Blog Series – Power’ing up your Home Office Lights: Part 7 – Building the PowerApp for Hue to Get Config and Link user

This blog post is part of the Blog Series: Power’ing up your Home Office Lights with Power Platform. See introduction post for links to the other articles in the series:
https://gotoguy.blog/2020/12/02/blog-series—powering-up-your-home-office-lights-using-power-platform—introduction/

With the Power Automate Flows we’ve built in the previous parts, we should now be able to get the Link and Whitelist the user and get the Hue Bridge Configuration details. It is time to build the main screen of the “Hue PowerApp”!

Here is a short video where I talk about the basics of the main screen of the PowerApp we are going to build:

Building the PowerApp and Main Screen

In my solution I wanted to build a canvas app with a phone layout, to be able to use it when on my mobile as well. Start by logging in to make.powerapps.com, and creating a new app from Blank, and either phone or tablet layout by your preference:

This next step is up to your preference and personal choice, but what I did was the following:

  • Added a custom background color from your palette (if you have a branding profile) or you could choose one of the built-in themes:
  • Add a Header logo
  • Add elements like frames and icons. I often use a Label control and set the border for it to create a frame like figure.
  • Add label controls for your text and placeholders for where you will update values later. Set font colors for labels and labels where you will have values.
  • Add some Images for where you want to add an action to the OnSelect event.
  • Add Button controls or Icons for navigating between screens.
  • Use a naming convenvtion for your controls.

In the end, adding and formatting all controls, and before I add any data to the PowerApp, my Hue PowerApp ends up like this:

I’ve uploaded Images for the Authorization and Linking, for your convenience I’ve attached those here:

After finishing the PowerApp main screen design, we can proceed to adding actions and getting data.

Connecting the PowerApp to Power Automate Flows

Start by selecting the Refresh Icon, on the Action menu, click the On Select button to change to the OnSelect event, and click the Power Automate button:

Under Data, select to associate the “Hue – Get Access Token and Config” Flow:

This will start populating the OnSelect event field, which you would edit so that you use the “Se”t function and save the response from calling the Flow in the variable HueResponse like this: Set(HueResponse,'Hue-GetAccessTokenandConfig'.Run())

Lets test this action. Before this I have removed my user from the Microsoft List “Elven Hue Users”, this list is empty now:

Hold down the “ALT” button on your keyboard, and click on the Refresh icon. The Flow will now run, you will see the small dots flying over the screen, but you won’t see any data yet. But you can check the contents of the “HueResponse” variable. Do this by going to the View menu, and click on the “Variables” button. From there you should see the HueResponse variable, it is of type “Record” and you can click on that Record icon:

You should now see something like the following values, if I hadn’t deleted the username from my List earlier I would see values for all these fields:

If I compare this with the response output from the Flow I triggered with the refresh icon above, I can see that the output really reflects the contents of the “HueResponse” variable:

Lets add these values to the labels I prepared in the PowerApp.

For the label containing the Hue name value, add the following to the Text property: If(HueResponse.access_token="","Hue not Connected!",If(HueResponse.username="","Connection to Hue OK, but User not linked!",HueResponse.name))

This should return something like this:

Proceed to add the following to the Text property for each of the remaining configuration value labels:

HueResponse.ipaddress
HueResponse.apiversion
HueResponse.internet
HueResponse.remoteaccess
HueResponse.devicetype

They won’t show any value in the PowerApp yet though. First we need to get the user registered at the Hue Remote API, which is the next step. Select the following image:

On the Action menu, for the OnSelect event, add the Power Automate Flow for Link and Whitelist User. Change the OnSelect event so that also this is using “Set” function and taking the response from the Flow to the same HueResponse variable, but you also need to supply an input to this flow. For this we will use the HueResponse.access_token, so your OnSelect event should look like this:

Set(HueResponse, 'Hue-LinkandWhitelistUser'.Run(HueResponse.access_token))

Lets test this button. Hold down “ALT” on your keyboard, and click on the image. The Flow should now run, register a user at Hue Remote API, create a new List item and return the configuration to the PowerApp:

Checking the HueResponse record variable now:

A couple of more things remain on the main screen. First, on the App’s OnStart event, add the same event as the refresh icon, this would get the config automatically at start:

Next, select this Image:

On the OnSelect event, add the following:

Launch("https://api.meethue.com/oauth2/auth?clientid=<your_client_id>&response_type=code&state=<youranystring>&appid=<your_app_id>&deviceid=<your_device_id>&devicename=<your device name>")

Replace the <your_…> values with the client id and app id from the Hue Remote API app registration, and your values for device id and name.

Clicking this image will now launch the Hue Developers portal, asking you to Grant permission to the App, and return to the Logic App that retrieves the Bearer Token and store that in the Key Vault as we have seen in previous parts of this blog series.

Summary and Next Steps

We’ve now built the foundation and first part of the PowerApp to retreive the configuration, create and link username, and if needed authorizing and getting a new Bearer Token via Hue Remote API if needed.

In the next part we will build the screen for getting lights and setting lights state and color.

Thanks for reading, see you in the next part!

Blog Series – Power’ing up your Home Office Lights: Part 6 – Using Power Automate Flow to Link Button and Whitelist user

This blog post is part of the Blog Series: Power’ing up your Home Office Lights with Power Platform. See introduction post for links to the other articles in the series:
https://gotoguy.blog/2020/12/02/blog-series—powering-up-your-home-office-lights-using-power-platform—introduction/

In the previous part 5 we created the first Power Automate Flow of the solution, for retreiving the Access Token and getting the configuration of the Hue Bridge via Remote API. To get all the configuration details of the Bridge, we were dependent on that the user had a Whitelist Identifier in the Microsoft List, and this is the Flow we will be working on in this blog post.

Lets do a quick video where I talk about this Flow and what it does:

Create the Flow for Linking and Whitelisting User

Create a new instant Flow, with PowerApps as Trigger. In my case I have named this Flow “Hue – Link and Whitelist User”.

As the first action in the Flow after the PowerApps trigger, add a Compose action:

Tips: Make sure that you set a name for the action, in my case I’ve named it “Access Token”, before you under Dynamic content selects “Ask in PowerApps”. This way the Input parameter will get a more descriptive name like “AccessToken_Inputs”, when we later call the Flow from the PowerApp.

Next, add two Initialize variable actions, called UserDisplayName and UserEmail and type String. For values use the following custom expressions (see comment for expression):

Next, add a Get Items action from SharePoint, and specify your Site and List. For Filter Query, add Title eq ‘<UserDisplayName variable>’:

Add a Condition action, where we will check if the Get Items returns an empty result to be false:

If false, meaning that the user already have a configuration in the List, under “If yes”, add a Get Item action. Specify the Site and List Name, and for Id add the following expression to return the first instance of results first(body('Check_if_User_Already_Linked')?['Value'])?['Id'] :

Next, add a HTTP action where we will query the Hue Remote API for the Bridge configuration details. Specify the URI to be https://api.meethue.com/bridge/<whitelist identifier>/config, and add an Authorization Header with Bearer <Token Outputs>:

This action should return all the details we want from the Hue Bridge, and we can add a Response action to return that back to the PowerApp:

In the other case, when a User Linked was not found in the SharePoint List, we need to add that user and get the Whitelist Identifier. Under “If no”, add a HTTP action. In this action we will “remotely” push the Hue Bridge button via a PUT method. This is basically the same procedure as when you add new lights or equipments, where you need to run and press the button down. But here we do it via the API like below:

PS! Note that above I’ve used “Raw” Authentication and for Value selected Bearer “AccessToken Outputs”. This is just another option to show, I could have have used an Authorization Header instead.

After the Link Button is enabled, we can add another HTTP action, this will register the username via a POST method and a request body containing the “devicetype” value. Device type is so that you can identify the registered usernames on your bridge:

After this action, add a Parse JSON action so that we can more easliy reuse the outputs from adding the username:

For Schema, select “Generate from sample”, and paste the sample output provided by the Hue API documentation here, https://developers.meethue.com/develop/hue-api/7-configuration-api/#create-user, under 7.1.5. Sample Response.

Next, add a “Create Item” action. Specify the Site and List Name, and add the following values for List columns:

Note that for Whitelist Identifier, use the expression body('Parse_JSON')?[0]?['success/username'], this is because the output from Hue API returns an array, so the [0] is to specify the first instance.

Now, using that newly created username, we can query for the Bridge config using the “Whitelist Identifier” from above:

And lastly, add a Response action that returns this back to the PowerApp:

Verify and Test the Flow

That should complete the Flow. We will link that into a PowerApp later, but if you want to you can test the Flow by performing the Trigger action yourself. Then you need to specify a valid Access Token, and the Flow should run successfully, creating a Linked User if you haven’t already:

If you check the List a new item should now represent your user:

Summary and Next Steps

We are now ready to start working on the PowerApp, linking the Flows we have created in this and the previous blog posts. That will come in the next part!

Thanks for reading so far ๐Ÿ™‚

Blog Series – Power’ing up your Home Office Lights: Part 4 – Using Logic Apps to Get Access Token and Renew Access Token if needed

This blog post is part of the Blog Series: Power’ing up your Home Office Lights with Power Platform. See introduction post for links to the other articles in the series:
https://gotoguy.blog/2020/12/02/blog-series—powering-up-your-home-office-lights-using-power-platform—introduction/

After building the Logic App in part 3 that will authorize and get access token via Oauth2, we will now create another Logic App that will retrieve the Bearer Token from the Key Vault secret, and renew the Token using Refresh Token whenever it is expired.

Here is a short video where I walk through that Logic App scenario:

Create the Logic App and HTTP Trigger

The first thing you need to do, is to create a new Logic App in your Azure subscription. Select the Resource Group you have contributor access to, and give the Logic App a suitable name, as per your naming guidelines. This is the Logic App I created in my environment:

Add a HTTP request trigger for this Logic App as well:

In the Logic App Designer, make sure you hit Save on the Logic App before the next step. You will now be shown the URL, but first go down to the โ€œAdd new parameterโ€ and select Method and GET for method. This way your Logic App will trigger on HTTP GET requests.

Adding Logic App Identity and Key Vault Access

As this Logic App also will request secrets from Key Vault, we will need to add a Managed Service Identity and add that to the Key Vault access policy.

Go to Identity settings, and set the System assigned Identity to On:

Next, go to your Key Vault and under Access policies, add the the newly created Logic App with the following Secret permissions (Get, Set, List):

Add Actions for Getting or Renewing Bearer Token

The actions in this Logic App will retrieve the Bearer Token from the Key Vault and return the Access Token as a Response. If Token is expired, it will be renewed using the Refresh token.

Start by adding a HTTP request, and get the Secret for the Bearer Token like the following:

Next, add a Compose action, getting the outputs from the Get KV Secret Bearer Token action. This secret was stored as a Json Object, but will be returned as a String, so I have used the following custom expression to convert to Json:

json(outputs('Get_KV_Secret_Bearer_Token')?['body/value'])

Next, add the following Compose actions for getting the timestamps in Ticks, and converting to Epoch. See the previous blog post for explanation of why this is necessary, but we need to do this to be able to calculate wether the secret is expired or not:

For your convenience, I’ve added the custom expressions as comments to the actions above, or you can copy it from below:

ticks(utcNow())

ticks('1970-01-01T00:00:00Z')

div(sub(outputs('GetNowTimeStampInTicks'), outputs('Get1970TimestampInTicks')), 10000000)

Next, add a Condition action. Here we will check if the expiry date time of the secret is greater than the current calculated timestamp in Epoch:

Use the following custom expression for getting the “exp” attribute from Key Vault Secret:

outputs('Get_KV_Secret_Bearer_Token')?['body/attributes/exp']

If the secret hasn’t expired, we will return the access token as a Response action as shown below. Note that I will only return the access_token, not the complete Bearer Token stored in the Key Vault secret, as this also contains the refresh_token. The reasoning behind this is that the calling clients (users from PowerApps/Automate) only need the access_token.

As you see from above, I’ve built a Json body and schema for the response, and the custom expression returing the value of access_token is outputs('Compose_Bearer_Token')?['access_token'].

On the False side of the Condition, meaning that the Secret is expired, we will have the logic that renews the Bearer Token. First add two HTTP actions, for getting the Client Id and Client Secret from Key Vault:

Next, add another HTTP action, using Method POST we will send a request to the oauth2/refresh endpoint at Hue Remote API:

The refresh_token need to be sent in a Request Body, using the expression: outputs('Compose_Bearer_Token')?['refresh_token']

Remember to set Content-Type: application/x-www-form-urlencoded. And Authentication Type should be set to Basic, using the retrieved Client Id and Secret from Key Vault as username and password.

Refreshing the Token correctly will return a new Bearer Token. We now need to get and convert the time stamps to Epoch integer, to calulate when the Access Token expires. This is the same process as we used in the Logic App “logicapp-hue-authorize” in part 3 of this blog series. Add 3 new Compose actions like below:

For your convenience, here are the custom expressions used for the above actions:

addSeconds(utcNow(), int(body('Post_Refresh_Code_with_Basic_Auth_to_Update_Access_Token')?['access_token_expires_in']))

ticks(outputs('AccessToken_Expires_Utc'))

div(sub(outputs('GetTimestampInTicks'), outputs('Get1970TimestampInTicks')), 10000000)

Next step is to update the Bearer Token Secret in Key Vault with the new Token we received now and the new expiry date. Add a HTTP action like below:

We can now return the access_token using the HTTP action like below:

The last action we need to add is a default response if any accest_token could not be returned. This is important as we are going to call this Logic App using Power Automate Flows, and we need to have a response for any scenario. Add this after the condition action like below:

For the Null Response action, change the Configure run after setting like the following:

That should be it. Remember to secure outputs for any actions that return credential information:

Verify Logic App

We can now test the Logic App. You can use Postman, Invoke-RestMethod in PowerShell, or just run in the Browser your Logic App Http Trigger Url:

This should return your Access Token:

Looking at the Run History for the Logic App, we should see a sucessful run:

Summary and Next Steps

That should conclude this blog post. In this post, and the previous, we have built the logic behind authorizing and getting the Bearer Token for Philips Hue Remote API, as well as providing and refresh the Token when needed.

In the next part we are going to start building the solution in Power Automate. Thanks for reading, see you in the next part!

Blog Series – Power’ing up your Home Office Lights: Part 3 – Using Logic Apps to Authorize and Get Access Token using Oauth and Hue Remote API

This blog post is part of the Blog Series: Power’ing up your Home Office Lights with Power Platform. See introduction post for links to the other articles in the series:
https://gotoguy.blog/2020/12/02/blog-series—powering-up-your-home-office-lights-using-power-platform—introduction/

Now that we have registered the application for Hue Remote API, and stored the client Id and Secret in Azure Key Vault, we can start build the Logic App that will authorize and get access token via Oauth2.

Here is a short video where I introduce the concept:

Create the Logic App and HTTP Trigger

The first thing you need to do, is to create a new Logic App in your Azure subscription. Select the Resource Group you have contributor access to, and give the Logic App a suitable name, as per your naming guidelines:

Next, select HTTP request to be the trigger for the Logic App:

After that you will see the following in the Logic App designer:

Make sure you hit Save on the Logic App before the next step. You will now be shown the URL, but first go down to the “Add new parameter” and select Method and GET for method. This way your Logic App will trigger on HTTP GET requests, which is required for the Authorization Code flow with Hue:

Now copy this URL, and make sure that no one but you can access this URL, as this is a SAS (Shared Access Signature) URL that anyone in the world can send requests to if they know the URL. Save the Logic App.

Go back to your App Registration in the Hue Developers Portal, and change your temporary http://localhost/logicapp Callback URL to your Logic App URL:

This means that from now on, the Logic App will handle the authorization code for the Hue App. But first we will need to let the Logic App access the Key Vault.

Adding Logic App Identity and Key Vault Access

For the Logic App, under Settings and Identity, set system assigned managed identity to On:

After this setting is saved, you can later see the status and the object id of the service principal.

Next, under your Key Vault, click on Access Policies and Add Access Policy, from there selec the Get, Set and List Secret Management operations, and for principal search for and add the Logic App. This should look like this after adding:

With permissions in place, we are now ready to add actions to the Logic App.

Add Actions for getting Access Token

The first ting we need to do is to get the authorization code after the Hue App registration redirects back to the callback URL. This is returned as a querystring appended to the URL. Add a “Compose” action and use the following expression for getting the request queries:

(I’ve added the custom expression to comments for better visibility).

Then we need to get the Client Id and Secret from the Kay Vault. Add a HTTP action next, where we will use the Azure Rest API for getting the secret, and authenticate with the Managed Service Identity:

The URI above points to my Azure Key Vault URI, and the specified secret. The documentation for getting secrets can be seen here: Get Secret – Get Secret (Azure Key Vault) | Microsoft Docs.

The same applies to getting the Client Secret, add another HTTP action:

The get an access token from Hue Remote API we must either use Basic Authentication or Digest Authentication. Since I’m running this as a Logic App in a controlled environment and trusting the SSL encryption I will use Basic Authentication. In addition, I will secure the outputs from getting Client Id and Secret from Key Vault, so that other users cannot see those values from the run history:

This setting has been enabled for both the actions getting KV Secret Client Id and Client Secret:

For obtaining an Access Token with Basic Authentication the following header is required: Authorization: Basic <base64(clientid:clientsecret)>

This means that we need to base64 encode the clientid + “:” + clientsecret. This can be done using this lengthy expression, here in a “initialize variable” action:

This above action is just for reference though, as the HTTP action supports base64 encoding out of the box. So when posting to the token endpoint, the best way is to use the following:

(PS! Another way to do Basic Authentication in a HTTP action would be to add the Authorization header manually in the Action above with: “Basic <your calculated base64>” as value, and leaving the Authentication type to None.)

From the above settings, in the URI add the authorization code we got from the request queries earlier, using the expression:

outputs('Compose_Authorization_Code')?['code']

When selecting Authentication type to Basic, the username (client id) and password (secret) will automatically be base64 encoded. The values for username and password are the valies from the Get KV Secret Client actions earlier, in the following format:

body('Get_KV_Secret_Client_Id')?['value']

And this action will return the Bearer Token from Hue Remote API if everything is correctly inputted.

I also make sure that the output of this action is secured from viewing:

With the Bearer Token now retrieved, the next actions is to calculate the expiry time and write the Token back to the Key Vault secret.

Add Actions for getting expiry time and write Token to Key Vault

The Bearer Token returned by Hue Remote API will be in the format of the following masked response:

The _expires_in values are in seconds, so that means that the Access Token is valid for 7 days, and the Refresh Token about 112 days. It would then make sense to only refresh the token when needed.

Lets start by calculating when the access_token expires, with the following expression in a Compose action:

addSeconds(utcNow(), int(body('Post_Auth_Code_with_Basic_Auth_to_Get_Access_Token')?['access_token_expires_in']))

The above expression takes the current time and add the number of seconds for when the access token expires.

This will return a new datetime 7 days ahead. This value will be used to set the expiry time on the Key Vault secret for Bearer Token. By setting an expiry time I can later calculate if I need to refresh the Access Token or not.

But it’s not that easy.. The calculated time above will need to be converted to Epoch (32-bit “Unix”) integer format to be able to set the Key Vault secret “exp” attribute. This isn’t so clear when seeing the API docs, Set Secret – Set Secret (Azure Key Vault) | Microsoft Docs, so took me a little trial and error. And I found great help in this blog article: https://devkimchi.com/2018/11/04/converting-tick-or-epoch-to-timestamp-in-logic-app/.

Based on this I need to convert the timestamp to Ticks (64-bit). Ticks is a built in function in Logic Apps, but to be able to convert to Epoch I will need to calculate the difference in ticks between when the Access Token expire, and the first value of Epoch which is 1970-01-01T00:00:00Z. This is well explained in the above reference blog, but here are my resulting actions.

After calculating the Access Token expiry, I add a compose action which converts this to Ticks:

Using the following expression: ticks(outputs('AccessToken_Expires_Utc'))

Then I need to find the 1970-01-01T00:00:00Z value in Ticks:

Using this expression: ticks('1970-01-01T00:00:00Z')

Then we can convert this to Epoch by subtracting the two Ticks values calculated above, and divide by 1 million:

This is the expression used above: div(sub(outputs('GetTimestampInTicks'), outputs('Get1970TimestampInTicks')), 10000000)

We now have the correct format for the “exp” attribute to update the Key Vault secret. Add a new HTTP action and configure like below:

Remember to secure the Output for this action also:

Finally we can finish this Logic App by adding a Response action and do a quick test to verify that everything works as expected.

Adding Response action and verify Logic App

Add a Response action with status code 200 and a body like below:

Tips: It can be difficult to troubleshoot the Logic App when securing outputs, so you might hold back in that when testing. It will show your secrets in the run history though, so it might be best to do this in a test enviroment depending on your needs.

Now we can test. Construct the URL for authorizing the App again, like we did in Part 1:

https://api.meethue.com/oauth2/auth?clientid=&response_type=code&state=elvenanystring&appid=elven_demo_hue_app&deviceid=elven_demo&devicename=Elven Demo

Paste it in the Browser, and after granting access to the App in Hue Developer portal:

You should be redirected to the Logic App:

.. and with a respons success!

Looking at the Run history, we can verify the steps were successful:

You can also look into the inputs and outputs of the actions, except the actions where we secured the output:

We can also verify that the Key Vault secret storing the Bearer Token has been updated and have an Expiration Date one week forward:

Summary and next steps

That concludes this blog post. Thanks for reading this far, in the next part we will build the Logic App that will respond back the Access Token and renew using Refresh Token if needed.