4
votes

I could be completely off basis here on how this works, but this is what I'm looking to achieve.

In AAD I have

  • an App Registration called backend-api that represents an HTTP API
  • an App Registration called frontend-app that represents some client (lets say a console app)
  • an App Registration called another-app that represents nothing related to my solution

I have a console application where I put my client ID and client secret in for frontend-app and I can request an access_token with the aud of backend-api. This is great, exactly what I want. However, I can litterally do the same thing from another-app if I have the client ID and client secret for that app. What I would like to accomplish is that only frontend-app is allowed to get an access_token for backend-api.

I'm not quite sure how to go about configuring that specific requirement. I thought maybe I needed to add an appRoles entry for allowedMemberTypes Application on backend-api and then grant frontend-app that role but that didn't apply any restriction to another-app. Likewise I thought maybe backend-api needed to have it's "Require User Signin" option checked under Enterprise Applications, but I couldn't find a way to add frontend-app as a "user" -- probably the wrong direction anyhow.

What's the way to tell AAD to only hand out access_tokens for backend-api (aud claim) if they are being requested via frontend-app only? Maybe that's a silly question, and it just doesn't work this way?

1

1 Answers

4
votes

You are on the right path with your thinking about adding appRoles entry to backend-api and then assigning the role specifically to frontend-app.

Additionally, understand that enforcing this requirement that only applications coming in with this new role claim are allowed but others aren't is a responsibility of your API code.

I'll get to 2 specific approaches next. Both the approaches are explained on Microsoft Docs here - Microsoft identity platform and the OAuth 2.0 client credentials flow

Approach 1 - Use Application Permissions or Roles

Configure your API application to expose a set of application permissions (or roles).

This approach is a little more declarative, as you define an application permission that needs to be assigned to any application that can call your backend-api.

Navigate to Azure Active Directory > App Registrations > App registration for your backend-api app > Manifest

Add a new application role.. using json like this:

"appRoles": [
{
  "allowedMemberTypes": [
    "Application"
  ],
  "displayName": "Can invoke my API",
  "id": "fc803414-3c61-4ebc-a5e5-cd1675c14bbb",
  "isEnabled": true,
  "description": "Apps that have this role have the ability to invoke my backend API",
  "value": "MyAPIValidClient"
}]

Assign the app permission to your frontend app

New-AzureADServiceAppRoleAssignment -ObjectId <frontendapp.ObjectId> -PrincipalId <frontendapp.ObjectId> -Id "fc803414-3c61-4ebc-a5e5-cd1675c14bbb" -ResourceId <yourbackendapi.ObjectId>

Authenticate your frontend app to backend api using client credentials grant, i.e. using clientId and client secret.. as you're probably already doing.

Now, in the auth token received by your backend api, you can check that the role claims collection must contain a role named "MyAPIValidClient" otherwise you can reject the call with Unauthorized exception.

Approach 2 - Use Access Control Lists

When your backend API receives a token, it can decode the token and extract the client's application ID from the appid and iss claims. Then it compares the application against an access control list (ACL) that it maintains.

Depending on your requirement, API might grant only a subset of full permissions or all permissions to a specific client.

This 2nd approach may seem like a simpler one for some cases, although I like the first one better as it scales well when you have multiple application permissions/roles and different level of functionality to provide based on those roles.

Related SO Post and References