While writing an earlier post I realized one can require access to Logic Apps be authenticated via Azure AD. It is not as extensive as with Azure Functions but should be fine as long as you only care about Azure AD. This doc has the deets so I am going to try it out.
By default you can run a Logic App using access keys. This is present in the URL that’s generated.
Simply calling the Url as is lets you run the Logic App. And without the keys it fails.
Problem with keys is about managing them and ensuring they are rotated regularly etc.
To protect via Azure AD one need to go to the Authorization section and create policies.
The only claim you need here is the Issuer. This has to start with either https://sts.windows.net/
or https://login.microsoftonline.com/
followed by the ID of the tenant. Any other Azure AD claims can be used in addition to this.
These are the default “standard” claims available in the drop down. The Audience claim can be used if I create an App Registration associated with this Logic App and then users have to get a token for that App Registration (this is how Azure AD authentication for Functions work as in my previous post).
I am not sure how to use the Subject claim. An example of all the claims and their values can be found in the doc itself. I’ve copy pasted it below for reference:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 |
{ "aud": "https://management.core.windows.net/", "iss": "https://sts.windows.net/<Azure-AD-issuer-ID>/", "iat": 1582056988, "nbf": 1582056988, "exp": 1582060888, "_claim_names": { "groups": "src1" }, "_claim_sources": { "src1": { "endpoint": "https://graph.windows.net/7200000-86f1-41af-91ab-2d7cd011db47/users/00000-f433-403e-b3aa-7d8406464625d7/getMemberObjects" } }, "acr": "1", "aio": "AVQAq/8OAAAA7k1O1C2fRfeG604U9e6EzYcy52wb65Cx2OkaHIqDOkuyyr0IBa/YuaImaydaf/twVaeW/etbzzlKFNI4Q=", "amr": [ "rsa", "mfa" ], "appid": "c44b4083-3bb0-00001-b47d-97400853cbdf3c", "appidacr": "2", "deviceid": "bfk817a1-3d981-4dddf82-8ade-2bddd2f5f8172ab", "family_name": "Sophia Owen", "given_name": "Sophia Owen (Fabrikam)", "ipaddr": "167.220.2.46", "name": "sophiaowen", "oid": "3d5053d9-f433-00000e-b3aa-7d84041625d7", "onprem_sid": "S-1-5-21-2497521184-1604012920-1887927527-21913475", "puid": "1003000000098FE48CE", "scp": "user_impersonation", "sub": "KGlhIodTx3XCVIWjJarRfJbsLX9JcdYYWDPkufGVij7_7k", "tid": "72f988bf-86f1-41af-91ab-2d7cd011db47", "unique_name": "SophiaOwen@fabrikam.com", "upn": "SophiaOwen@fabrikam.com", "uti": "TPJ7nNNMMZkOSx6_uVczUAA", "ver": "1.0" } |
I’ll keep it simple in this case and only require the Issuer claim. This means anyone authenticated against my tenant Azure AD can access this Logic App.
Next thing is to add an ‘Authorization’ header in the request trigger of the Logic App. This basically means add and set the operationOptions
property to IncludeAuthorizationHeadersInOutputs
in the code view of the Logic App.
1 2 3 4 5 6 7 8 |
"triggers": { "manual": { "inputs": {}, "kind": "Http", "operationOptions": "IncludeAuthorizationHeadersInOutputs", "type": "Request" } } |
(Update: In some subsequent testing where I forgot to add the above I realized that authentication via Azure AD works fine even without this property. Adding this property, however, makes the token available to the Logic App so you can slice and dice it like I did in a previous post to extract the claims if needed).
So that’s it on the Logic App side. As a client, I now have to include a token in the header while making a request to the Logic App. This is the standard bearer token stuff (a header called Authorization
with the value Bearer <token>
).
How do I get a token though? Typically you get it for an App Registration but that’s not the case here… I don’t have an App Registration. So I need something from Azure itself I suppose. Even Azure REST API needs an App Registration.
That said, I had recently learnt it is possible to get an Access Token for Azure via the Get-AzAccessToken
cmdlet. So maybe I’ll try with that?
First I do:
1 |
Connect-AzAccount -UseDeviceAuthentication |
This signs me into Azure PowerShell. Then I do:
1 |
Get-AzAccessToken |
This gives me the token:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 |
Token : eyJ0eXAiOiJKV1QiLCJhbGciOiJSUzI1NiIsIng1dCI6Imwzc1EtNTBjQ0g0eEJWWkxIVEd3blNSNzY4MCIsImtpZCI6Imwzc1EtNTBjQ0g0eEJWWkxIVEd3blNSNzY4MCJ9.eyJhdWQiOiJodHRwczovL21hbmFnZW1l bnQuY29yZS53aW5kb3dzLm5ldC8iLCJpc3MiOiJodHRwczovL3N0cy53aW5kb3dzLm5ldC9kYzNkOWYwOC1iNDZkLTQ3ZjItYjlhNS01NGY3YWEyZjM4YmYvIiwiaWF0IjoxNjM4MDM2NTU4LCJuYmYiOjE2MzgwMzY1N TgsImV4cCI6MNYzODA0MTA5MywiYWNyIjoiMSIsImFpbyI6IkFWUUFxLzhUQUFBQTA4R2JVdFdpdUlSZ2FQcnRuNG1OWFlUMDJSV2pMaUlJclcyNTVSSXlBZFNhNEVqcnlKRnVWU1oxcFppVXExeEtySFdMWHc2b3BQMG 8yUHFjOHI1eCtsb2FXNThkcXJwWHNsb0hMZVZIWGNvPSIsImFtciI6WyJwd2QiLCJtZmEiXSwiYXBwaWQiOiIxOTUwYTI1OC0yMjdiLTRlMzEtYTljZi03MTc0OTU5NDVmYzIiLCJhcHBpZGFjciI6IjAiLCJmYW1pbHl fbmFtZSI6IlNhc2lkaGFyYW4iLCJnaXZlbl9uYW1lIjoiUmFraGVzaCIsImdyb3VwcyI6WyJiMjFiZWNjZi0wNzcxLTQ4NTYtYjI0NC04MzNiNTMyNjE4YzciLCI3ODM5OTMxNC0wOTQwLTQ2ZWYtOGVhYy04NTE5YmUx YTJlNzUiLCJmNjk5NzIyMC02YTY3LTQxODEtOWFiYS1iYzY2NGNmZjcwMTIiLCI2M2QyZGRjMy1jNDIzLTRkMDYtYWRlNy02ZDI4OWQ1MjViN2IiLCIxOGNmM2Q1NC1lZTNlLTRlYzgtYmEwMC1iOWU1ZTJkYzgwMWIiL CJkY2Y5ZDUzY51mMzk5LTQyZDItOGU2Zi1lYTNhZDM5YTRhNWMiLCJkOWU0NTViNC1jOTc5LTQwZjUtYjFjZS1kNzNjOTBhM2ZhZDgiXSwiaXBhZGRyIjoiODkuMzYuNjUuOTAiLCJuYW1lIjoiUmFraGVzaCBTYXNpZG hhcmFuIiwib2lkIjoiNDI0NzRjMDctYmJhOS00YWZiLTk2YWEtNTZmNjRiNzFiZGRmIiwicHVpZCI6IjEwMDMyMDAwRTRDRTQ2NDEiLCJyaCI6IjAuQVhRQUNKODkzRzIwOGtlNXBWVDNxaTg0djFpaVVCbDdJakZPcWM 5eGRKV1VYOEowAUdjLiIsInNjcCI6InVzZXJfaW1wZXJzb25hdGlvbiIsInN1YiI6ImIzTTlCTmN0NFhXWXNtVi11WURSSkprY0hjNnMzTEJNTUVsaHQwZ042M3ciLCJ0aWQiOiJkYzNkOWYwOC1iNDZkLTQ3ZjItYjlh NS01NGY3YWEyZjM4YmYiLCJ1bmlxdWVfbmFtZSI6InJha2hlc2hAdnJpdHJhLm5ldCIsInVwbiI6InJha2hlc2hAdnJpdHJhLm5ldCIsInV0aSI6IjlLaFlJMnhGVms2aEhnNXREY0lDQVEiLCJ2ZXIiOiIxLjAiLCJ3a WRzIjpbIjExNjQ4NTk3LTkyNmMtNGNmMy05YzM2LWJjZWJiMGJhOGRjYyIsIjRkNmFjMTRmLTM0NTMtNDFkMC1iZWY5LWEzZTBjNTY5NzczYSIsImYyZWY5OTJjLTNhZmItNDZiOS1iN2NmLWExMjZlZTc0YzQ1MSIsIm I3OWZiZjRkLTNlZjktNDY4OS04MTQzLTc2YjE5NGU4NTUwOSJdLCJ4bXNfY2MiOlsiQ1AxIl0sInhtc190Y2R0IjoxNjAwNDMyNzIwfQ.EJqtKlHWns1H4RGRzCVFr0hzN2n5aXFrJlz_JLywYb6GBPYqcwvjl9V7Bn-m 9LcPpag5gvyqg8HjZcWVb8Wn12dUXZUdZh3VxO-X0DOuauXLe7zl-PWBYboapP9h2Jvf-z5Nk4kJxZ76-RWQP2hnnehT_541CiiPPiX3kttDlBzd1XFmrywjpk9b1-HS352ADGX_sDd_onMOeJqTfsZXfmBIJpXjcjVwh 7vMNC5WOlpnV6-BMpIvgEBGHplJ-LnijXgFvUhVdj_pTxEEJGFyPdEvzaR1Mk28Yz20MVvn4aOKf2ZKUcpafgtec8u0ZzFBqHkMkZ9syqFLKki_eolPLA ExpiresOn : 27/11/2021 19:24:52 +00:00 Type : Bearer TenantId : dc3d9f08-b46d-47f2-b9a5-54f7aa2f38bf UserId : rakhesh.sasidharan@wevilgenius.com |
Let’s try authenticating with that:
1 |
$header = @{ "Authorization" = "Bearer " + (Get-AzAccessToken).token } |
Hmm.
Upon a whim I checked the access token in jwt.ms and found the issue:
The issuer starts with https://sts.windows.net. So I changed that in the Logic App policy and tried again. That too didn’t work intially until I realized the actual Issuer claim has a trailing slash – https://sts.windows.net/xxx/
– and I had missed that. So I added it and now I was able to connect.
My Logic App is set to respone with the Headers it received so that’s why the output above has the token in it.
Digression
What’s the difference between sts.windows.net
and login.microsoftonline.com
? I’ve seen both but not paid attention to the difference. It looks like the former is for v1.0 Azure AD endpoints while the latter is v2.0 (according to StackOverflow) – or rather, the kind of token the Application is configured to respond with (v1.0 is sts.windows.net
)? Also found this answer in GitHub which says the same; as well as this answer that explains things a bit more (screenshot of the answer below).
This too gives the impression the endpoint doesn’t matter, but what matters is the kind of token the Application is configured to respond with. This is Microsoft’s page on the advantages of v2.0 over v1.0 (no mention of the endpoint there either). I also found this blog post introducing v2.0 but that gives the impression the issuer will always be sts.windows.net
for Azure AD (irrespective of v1.0 or v2.0).
So I am not a 100% sure but I think it’s safe to say create two Authorization policies for your Logic Apps – one with sts.windows.net
as Issuer, the other as login.microsoftonline.com
. Both will have the tenant ID so this way you can restrict to connections from your tenant.
Advanced Stuff
If I want to restrict the Logic App to groups of users I imagine I’d have to use an App Registration and limit its Service Principal to specific groups. Or I could use the custom claims feature and add the claims I am interested in. This is not scalable as I can only have 5 policies, and each policy only have 10 claims… so better to use the App Registration I suppose.
Update: Once Azure AD authorization is enabled SAS keys stop working. You get the following error if you try those:
1 |
Invoke-RestMethod: {"error":{"code":"DirectApiRequestHasMoreThanOneAuthorization","message":"The request has both SAS authentication scheme and 'Bearer' authorization scheme. Only one scheme should be used."}} |
Update 2: I have a follow-up post.