MicrosoftDocs / azure-docs

Open source documentation of Microsoft Azure
https://docs.microsoft.com/azure
Creative Commons Attribution 4.0 International
10.2k stars 21.34k forks source link

Assign app roles to applications - Without 'expose an API' and API Scopes #110692

Closed Danielku15 closed 10 months ago

Danielku15 commented 1 year ago

The "Assign app roles to applications" guide explains that it is required to expose an API and define a scope to be able to assign one application to another one. How can I assign one application to another with a role without using API scopes using the Azure Portal?

Using the Graph API it is possible with the example below. It is not required to use custom scopes for our API but still the current guide only explains this workflow. I personally think it is just a missing feature that we can select also service principals in the "Users and groups" blade of the enterprise applications. Theoretically I could also add the service principal to a user group and then assign the group with the role and this will work.

Example:

I made a test where I created a "server" and a "client" app registration with an enterprise application (service pricipal). I defined a role "User" on the "server" registration. Then through the Graph API I was able to assign the client service principal with the "User" role to the server service principal.

HTTP POST https://graph.microsoft.com/v1.0/servicePrincipals/<client enterprise applicaiton object id>/appRoleAssignments

{
    "principalId": "<client enterprise app object id>",
    "resourceId": "<server enterprise app object id>",
    "appRoleId": "<server app role object id>"
}

After that the client service principal is listed under "Users and groups" in the same way like following the API scope workflow. When creating a token using

HTTP POST https://login.microsoftonline.com/<tenant id>/oauth2/v2.0/token

client_id=<client app registration app id>
scope=openid email <server app registration app id>/.default
client_secret=<client secret added to the client app registration>
grant_type=client_credentials

I get a token where the audience is correctly claims are

"aud": "<server app id>",
"appid": "<client app id>",
"roles": [ "<assigned app role>" ]

Looking into the client app registration > permissions blade. I see the Server App with the "User" permission granted. Funnily it mentions that it was granted through admin consent and by an administrator. but I am not an admin in our Azure AD and could assign the role.

All without having a custom dedicated scope (in expose an API) and Application ID URI defined.


Document Details

Do not edit this section. It is required for learn.microsoft.com ➟ GitHub issue linking.

RamanathanChinnappan-MSFT commented 1 year ago

@Danielku15 Thanks for your feedback! We will investigate and update as appropriate.

ManoharLakkoju-MSFT commented 1 year ago

@Danielku15 I'm going to assign this to the document author so they can take a look at it accordingly

@cilwerner Could you Please review this and update as appropriate.

cilwerner commented 1 year ago

Hi @Danielku15, interesting to see it work. This may be a bug, but I'll need to follow up with the developer team to check whether this is an oversight or not. Things may have changed now that we are looking towards the Microsoft Entra admin center instead of the Azure portal.

psignoret commented 11 months ago

@Danielku15 I don’t see where in the article it says defining a scope (delegated permission) or identifier URI is necessary, could you please share a link to the section?

If you weren’t assigned a directory role and were able to create an app role assignment, you are probably an owner of the resource app’s service principal (listed under “Owners” for the app in Enterprise Apps in the portal, or under …/servicePrincipals/{id}/owners in Microsoft Graph), which is sufficient for assigning app roles to users, groups, and service principals—this is intentional and not a bug.

Keeping this issue open because there are some points which could be rewritten to be more precise and avoid confusion (e.g. you do need to ba an admin to click the “Grant admin consent” button, but that’s not the only way to assign app roles to apps).

cilwerner commented 11 months ago

The relevant article for scopes is elsewhere in the identity platform docs, Configure an application to expose a web API.

Danielku15 commented 11 months ago

@psignoret

I don’t see where in the article it says defining a scope (delegated permission) or identifier URI is necessary, could you please share a link to the section?

For doing an authentication against an application with OAuth/OIDC you need a scope, that's an essential part of the authorization procedure. Without having scopes, any valid token of the Identity Providers would be treated as valid but as a developer you want to ensure that the user really has access to your application. And in order to create a scope in Azure, you need to also define an identifier URI.

But (!): Azure always has the implicit scope of {applicationId}/.default which other IdPs do not have. And this is where my request/question starts. You don't necessarily need to create a custom scope because this default scope exists.

Nevertheless, when you want to grant a service principles of application access to another application you are stuck in the Azure Portal because you cannot select service principles where you can add normal users. But you can add the service principle to a group and assign the group to the application. Or you use the API directly as indicated.

Or you use the currently "official" where you: Go to the client application > permissions and try to select the custom server application scope and select a role. But when doing so, an admin consent is needed (assuming the admins have set up to block 'unknown' scopes).

If you weren’t assigned a directory role and were able to create an app role assignment, you are probably an owner of the resource app’s service principal (listed under “Owners” for the app in Enterprise Apps in the portal, or under …/servicePrincipals/{id}/owners in Microsoft Graph), which is sufficient for assigning app roles to users, groups, and service principals—this is intentional and not a bug.

Yes, I created the app registrations in our directory and therefore the enterprise applications. But as indicated above, our admins have set up restrictions to not trust any scope which they do not have on their grant-list. This includes the applications in the own directory and therefore we need an admin to grant consent between our applications.

And this restrictions is not required when doing things through the API or through groups (as mentioned above).

(e.g. you do need to ba an admin to click the “Grant admin consent” button, but that’s not the only way to assign app roles to apps).

This is where it gets tricky to demonstrate but I think the examples mentioned before help understanding the setup. When we setup things through "permissions" an admin has to grant "admin consent", otherwise the "client application" doesn't show up the correct app roles. If we go through the other configuration path, the consent is not required, and the UI still shows "admin consent granted" even though an admin never approved anything.

psignoret commented 11 months ago

For doing an authentication against an application with OAuth/OIDC you need a scope, that's an essential part of the authorization procedure.

You don't always need to define a scope exposed by your API, no. In your original post, you talked about assigning app roles to a service principal and using the OAuth 2.0 Client Credentials Grant flow to request a token to the API you defined. In that scenario, there is no user involved, and there is no requirement for the API to have defined any scope values.

In Entra ID, scopes (delegated permissions, in this context) only need to be defined when an API is expected to be called by a client app which will be is acting on behalf of a different authenticated principal: the signed-in user (delegated access). In your original post, you only described a setup where a client will be accessing an API on its own, without any authenticated user.

Without having scopes, any valid token of the Identity Providers would be treated as valid but as a developer you want to ensure that the user really has access to your application.

It's unclear if you're referring to the "scope" parameter of an authentication, authorization, or token request (from OAuth 2.0 or Open ID Connect), or if you're referring to the scp claim in an access token issued by Entra ID. Regardless, in Microsoft Entra ID (formerly known as Azure AD), neither of those tell you what the user has access to. The scp claim in an access token is a constraint in the token, describing the most that can be done with that token. What authorization the signed-in user has to the service is determined by other claims (e.g. roles), or outside of the token itself. The service is responsible for ensuring that the user is authorized for the request in question and that the request does not exceed the scope of the token.

And in order to create a scope in Azure, you need to also define an identifier URI.

This is actually only true in the Azure portal, because it is common practice to use the friendlier identifier URI as the prefix for a scope value in the authentication request (e.g. scope=https://api.example.com/Scope1 ...). If you define the exposed scopes using the manifest, API, or PowerShell, you could also not define an identifier URI and then in your client request the scope using the API's app ID as the identifier (e.g. scope=225a5d9b-e70f-461c-9751-9b2c94d3eb29/Scope ...).

But (!): Azure always has the implicit scope of {applicationId}/.default which other IdPs do not have. And this is where my request/question starts. You don't necessarily need to create a custom scope because this default scope exists.

Sort of. The .default place-holder value is used when only the resource service is being identified in the request. There are two use-cases, with slightly different interpretations of .default:

Nevertheless, when you want to grant a service principles of application access to another application you are stuck in the Azure Portal because you cannot select service principles where you can add normal users.

It is true (and frustrating) that the "Users and groups" pane only allows assigning app roles to users and groups, not to service principals. The team has it on the backlog to offer a way to just directly assign app roles to service principals from the portal.

But you can add the service principle to a group and assign the group to the application.

No, this won't work. Today, only users inherit app roles assigned to groups (and only from groups of which they are direct members).

Or you use the currently "official" where you: Go to the client application > permissions and try to select the custom server application scope and select a role. But when doing so, an admin consent is needed (assuming the admins have set up to block 'unknown' scopes).

This does work and it's the only way currently to use the portal to grant an app role assignment to a service principal, but it's not the only "official" way. Using Microsoft Graph (e.g. Microsoft Graph PowerShell) is just as "official" for assigning app roles.

Yes, it's frustrating that the "Grant admin consent" button (as well as the interactive consent prompt) doesn't recognize the case where all of the client's required app roles are for resources that the user is an owner of (which means the user is actually authorized to grant the app roles). Also on the backlog.

Yes, I created the app registrations in our directory and therefore the enterprise applications.

Good, so you're owner, which explains why you're authorized to create app role assignments. Thanks for confirming!

But as indicated above, our admins have set up restrictions to not trust any scope which they do not have on their grant-list. This includes the applications in the own directory and therefore we need an admin to grant consent between our applications.

If you are owner of the resource app registration and service principal, and the client is registered in the same tenant as the resource and this is the same tenant where tokens are being issued from, you don't really need to go through your admins:

This is where it gets tricky to demonstrate but I think the examples mentioned before help understanding the setup. When we setup things through "permissions" an admin has to grant "admin consent", otherwise the "client application" doesn't show up the correct app roles. If we go through the other configuration path, the consent is not required, and the UI still shows "admin consent granted" even though an admin never approved anything.

I'm not sure I follow what feedback you're giving here. So far we've touched on a few places where things could be improved:

What else would you add to the list?

cilwerner commented 10 months ago

We are undergoing a repo migration from azure-docs, and therefore have to close this issue. It will be moved over to the new repository and resolved there once up and running.

please-close