Entra App Registrations and Enterprise Applications: The Definitive Guide

For those that must manage application integrations in Entra ID, it’s an inevitable question: What is the difference between an App Registration and an Enterprise Application? Why are there two different management blades? Why do I see some applications in both places?

I’ll admit that this is not the first take at answering this question, and there are already some good answers out there. Microsoft breaks things down here, Apps & service principals in Azure AD – Microsoft Entra | Microsoft Learn, with a decent visual at the end.

Marilee Turscak also has an excellent breakdown here, The Differences Between App Registrations, Enterprise Applications, and Service Principals in Azure AD | Marilee Turscak.

And John Savill has fantastic video published covering this as well, Azure AD App Registrations, Enterprise Apps and Service Principals – YouTube.

As John points out in his video, the understanding of App Registrations and Enterprise Apps can further be enhanced by understanding OpenID Connect and OAuth 2.0 flows. This is especially important for identity professionals and ITPros who may come from a sysadmin background. Modern authentication flows and concepts may feel foreign to folks who built their career on identity platforms such as Active Directory.

For those that find it easiest to learn by doing, if you want to play around with App Registrations, Enterprise Aps and Service Principals, but don’t want to mess with your prod environment, sign-up for an M365 Developer account, to have your own free tenant to work with here, Developer Program | Microsoft 365 Dev Center

We’ll start with some definitions, and then try to walk through various scenarios that you may encounter. If you don’t see your question answered within the definitions, keep reading… we’ll try to hit on all the areas here. If there is something I’ve missed or isn’t clear, reach out to me on LinkedIn or Twitter (@msft_hiker).

App Registrations

An application registration (app registration) is the definition of the application, as an application object, in Entra ID. It “explains” to Entra ID what the application wants, such as permissions for certain APIs, as well as where and how the application resides, with information such as the Redirect URI.

For a line-of-business (LOB) it would usually be your developers specifying the contents within an application object. Where identity professionals usually get involved:

  • The Entra tenant is locked down, and developers do not have permissions to create App Registrations. Someone with permissions in the tenant will create the app registration and assign ownership to the developer, or potentially work with the developer to configure it.
  • The organization has procured an application that they need to integrate into Entra ID. The application may be either a SaaS application or a piece of software run in an IaaS or PaaS environment, or on-prem.

An application definition, at its core, is the application manifest. Except for the value of a secret, any configuration you make within the app registration portal will be reflected in the application manifest.

If we look at My Test Application in the Entra portal:

We can then select Manifest from application configuration blade, and see these represented within the manifest:

"requiredResourceAccess": [
		{
			"resourceAppId": "00000003-0000-0000-c000-000000000000",
			"resourceAccess": [
				{
					"id": "14dad69e-099b-42c9-810b-d002981feec1",
					"type": "Scope"
				},
				{
					"id": "7427e0e9-2fba-42fe-b0c0-848c9e6a8182",
					"type": "Scope"
				},
				{
					"id": "37f7f235-527c-4136-accd-4a02d197296e",
					"type": "Scope"
				},
				{
					"id": "64a6cdd6-aab1-4aaf-94b8-3cc8405e90d0",
					"type": "Scope"
				}
			]
		}
	]

In this specific example they are represented as their actually Microsoft Graph API resource identifier; the term scope comes from OAuth 2.0. The portal does a lot of work “translating” the manifest into something easier for those less familiar with the underlying terminology to understand.

In OAuth 2.0 scope effectively equates to permissions, and the majority of what many ITPros may be concerned with as far as integrations go would be Microsoft Graph API permissions. Note that several different APIs can be scoped within an application, not just Graph permissions.

For more details on the application manifest, see Understanding the Azure Active Directory app manifest – Microsoft Entra | Microsoft Learn.

⚠️ Editing the application manifest directly without thoroughly understanding of the changes you are making may break your application.

App registrations are considered a globally unique definition of the application across all Entra tenants, even if it’s an application that is only to be used by your tenant.

Entra ID, as a global Identity-as-a-service (IDaaS) platform, has shared backend components, and for the purposes of a multitenant application it’s important to ensure that the application is globally unique from a security and functionality perspective. Since organizations can choose to change a single-tenant application to a multitenant application, the ID needs to remain unique.

Service Principals

Service principals are a security principal that define the allowed permissions for the application in Entra ID and give the application representation within the directory. Users are another type of security principal – we assign permissions and roles to users within Entra ID, as well as other associated resources.

I highlight allowed permissions because while the App registration defines what permissions the app developer needs for the application to function, it’s the service principal that actually defines what is allowed in the tenant. The service principal is also the object that will appear in various Azure “pick lists”, if you are applying something like a Conditional Access policy, or assigning the service principal rights in an Azure subscription.

There are three types of service principals in Entra ID:

  • Enterprise applications
  • Managed identities
  • Legacy

Since legacy service principals are something that organizations shouldn’t be using these days, we really just have two types that we’ll focus on.

Enterprise Applications

Enterprise applications, as a type of service principal, could effectively be thought of as a child if you were diagram a hierarchy. Now if service principals are the parent to enterprise applications, why is the blade in Entra admin center titled Enterprise applications? It’s because that’s primarily what we are concerned with when working within that blade. If you’ve used PowerShell or the Graph API for enumerating and working with enterprise applications, it’s why you use commands like get-azureadserviceprincipal or /servicePrincipals in Graph for accessing these.

If you browse to Enterprise applications, you’ll notice that the view is actually pre-scoped to Application type == Enterprise Applications

We can actually clear the filter, and we’ll end up being able to view not just our enterprise applications, but also our service principals, and Microsoft applications.

Microsoft applications are still service principals in our Entra tenant, but they generally relate to more tightly integrated services. For the purposes of our exploration, we won’t dive into these, but will note that they should primarily be left unaltered.

Every time you see the term enterprise application, just remember it’s a type of service principal.

Gluing the two together

It’s important to come back to the understanding that there is a direct link between an app registration and a service principal.

Single Tenant Application

For a single tenant application, we’ll have the app registration along with the corresponding service principal. If the app registration occurs in the tenant, there will always be a corresponding service principal created.

Multitenant Application

For a multitenant application, we’ll have the app registration along with the corresponding service principal in all tenants that the application has been granted consent to.

Consent Grant

This is probably a good place to bring up that the application consent grant process is what permits the service principal creation with the associated API permissions. This is when you receive a popup such as the following:

Image courtesy Microsoft Learn

At the time of providing the consent grant, a service principal is created in your Entra tenant indicating the permissions that were consented to.

What about Passwords

This is where it gets a bit complex, and existing documentation tends to really pass over what passwords live where and when to use what.

For purposes of this document I’ll use the term password to represent a shared secret. I mention this because while the Entra admin center places these under the term Client secrets, in the application manifest and service principal definition, passwordCredentials is what holds the data on the secret. For simplicity we’ll be looking at shared secrets, but you can, and should whenever possible, use Certificates, also referred to as keyCredentials.

Most documentation would lead you to believe that you create a password in the application object, and then this is referenced by the service principal. However, you also can create and associate a password with the service principal itself.

Service Principal Passwords

We can test creation of a service principal and assign a password directly to it with the following bit of PowerShell:

$notAfter = (Get-Date).AddMonths(6) # Valid for 6 months
$application = New-AzureADApplication -DisplayName "Test Service Principal with Password"
$sp=New-AzureADServicePrincipal -AppId $application.AppId -Tags @("WindowsAzureActiveDirectoryIntegratedApp")
New-AzureADServicePrincipalPasswordCredential -ObjectId $sp.ObjectId -EndDate $notAfter

Reinforcing the relationship between a service principal and an application object (application registration), notice that we must reference an application ID when creating a service principal. This means we always have to create an application object before being able to create a service principal.

This should give us output with a password. If we query Graph for the details of the service principal, we can see the following:

{
    "@odata.context": "https://graph.microsoft.com/v1.0/$metadata#servicePrincipals/$entity",
    "id": "c5f53a03-3928-4608-a4ba-3286a2d84f60",
    "deletedDateTime": null,
    "accountEnabled": true,
    "alternativeNames": [],
    "appDisplayName": "Test Service Principal with Password",
    "appDescription": null,
    "appId": "8c31d7fe-57b9-4d65-8905-2b2c03c09dd6",
    "applicationTemplateId": null,
    "appOwnerOrganizationId": "",
    "appRoleAssignmentRequired": false,
    "createdDateTime": "2023-03-13T23:27:20Z",
    "description": null,
    "disabledByMicrosoftStatus": null,
    "displayName": "Test Service Principal with Password",
    "homepage": null,
    "loginUrl": null,
    "logoutUrl": null,
    "notes": null,
    "notificationEmailAddresses": [],
    "preferredSingleSignOnMode": null,
    "preferredTokenSigningKeyThumbprint": null,
    "replyUrls": [],
    "servicePrincipalNames": [
        "8c31d7fe-57b9-4d65-8905-2b2c03c09dd6"
    ],
    "servicePrincipalType": "Application",
    "signInAudience": "AzureADMyOrg",
    "tags": [
        "WindowsAzureActiveDirectoryIntegratedApp"
    ],
    "tokenEncryptionKeyId": null,
    "samlSingleSignOnSettings": null,
    "addIns": [],
    "appRoles": [],
    "info": {
        "logoUrl": null,
        "marketingUrl": null,
        "privacyStatementUrl": null,
        "supportUrl": null,
        "termsOfServiceUrl": null
    },
    "keyCredentials": [],
    "oauth2PermissionScopes": [
        {
            "adminConsentDescription": "Allow the application to access Test Service Principal with Password on behalf of the signed-in user.",
            "adminConsentDisplayName": "Access Test Service Principal with Password",
            "id": "2420f00e-c0a0-4a83-b318-e5fbca42de2f",
            "isEnabled": true,
            "type": "User",
            "userConsentDescription": "Allow the application to access Test Service Principal with Password on your behalf.",
            "userConsentDisplayName": "Access Test Service Principal with Password",
            "value": "user_impersonation"
        }
    ],
    "passwordCredentials": [
        {
            "customKeyIdentifier": null,
            "displayName": null,
            "endDateTime": "2023-08-13T23:27:19.4255334Z",
            "hint": "Vdh",
            "keyId": "746ca1b3-927b-431f-980d-4b7b8048a0d6",
            "secretText": null,
            "startDateTime": "2023-03-13T23:27:20.8660257Z"
        }
    ],
    "resourceSpecificApplicationPermissions": [],
    "verifiedPublisher": {
        "displayName": null,
        "verifiedPublisherId": null,
        "addedDateTime": null
    }
}

Now, if we look at the app registration for this service principal, note that the passwordCredentials is empty:

{
    "@odata.context": "https://graph.microsoft.com/v1.0/$metadata#applications/$entity",
    "id": "e3af89d1-585a-472c-9b46-b110bf1969ce",
    "deletedDateTime": null,
    "appId": "8c31d7fe-57b9-4d65-8905-2b2c03c09dd6",
    "applicationTemplateId": null,
    "disabledByMicrosoftStatus": null,
    "createdDateTime": "2023-03-13T23:27:20Z",
    "displayName": "Test Service Principal with Password",
    "description": null,
    "groupMembershipClaims": null,
    "identifierUris": [],
    "isDeviceOnlyAuthSupported": null,
    "isFallbackPublicClient": null,
    "notes": null,
    "publisherDomain": "fabrikam.cloud",
    "serviceManagementReference": null,
    "signInAudience": "AzureADMyOrg",
    "tags": [],
    "tokenEncryptionKeyId": null,
    "samlMetadataUrl": null,
    "defaultRedirectUri": null,
    "certification": null,
    "optionalClaims": null,
    "servicePrincipalLockConfiguration": null,
    "requestSignatureVerification": null,
    "addIns": [],
    "api": {
        "acceptMappedClaims": null,
        "knownClientApplications": [],
        "requestedAccessTokenVersion": null,
        "oauth2PermissionScopes": [
            {
                "adminConsentDescription": "Allow the application to access Test Service Principal with Password on behalf of the signed-in user.",
                "adminConsentDisplayName": "Access Test Service Principal with Password",
                "id": "2420f00e-c0a0-4a83-b318-e5fbca42de2f",
                "isEnabled": true,
                "type": "User",
                "userConsentDescription": "Allow the application to access Test Service Principal with Password on your behalf.",
                "userConsentDisplayName": "Access Test Service Principal with Password",
                "value": "user_impersonation"
            }
        ],
        "preAuthorizedApplications": []
    },
    "appRoles": [],
    "info": {
        "logoUrl": null,
        "marketingUrl": null,
        "privacyStatementUrl": null,
        "supportUrl": null,
        "termsOfServiceUrl": null
    },
    "keyCredentials": [],
    "parentalControlSettings": {
        "countriesBlockedForMinors": [],
        "legalAgeGroupRule": "Allow"
    },
    "passwordCredentials": [],
    "publicClient": {
        "redirectUris": []
    },
    "requiredResourceAccess": [],
    "verifiedPublisher": {
        "displayName": null,
        "verifiedPublisherId": null,
        "addedDateTime": null
    },
    "web": {
        "homePageUrl": null,
        "logoutUrl": null,
        "redirectUris": [],
        "implicitGrantSettings": {
            "enableAccessTokenIssuance": false,
            "enableIdTokenIssuance": true
        },
        "redirectUriSettings": []
    },
    "spa": {
        "redirectUris": []
    }
}

And to validate this, we can look at the associated application object in the portal:

Application Object (App registration) Passwords

This time, let’s create another service principal but we’ll set a password in the application object:

$application = New-AzureADApplication -DisplayName "Test App Registration with Password"
$sp=New-AzureADServicePrincipal -AppId $application.AppId -Tags @("WindowsAzureActiveDirectoryIntegratedApp")
New-AzureADApplicationPasswordCredential -ObjectId $application.ObjectId

Interestingly we see that the service principal has no reference to the password:

{
    "@odata.context": "https://graph.microsoft.com/v1.0/$metadata#servicePrincipals/$entity",
    "id": "fd6543af-4432-4ab6-bcdf-9ac120b36aab",
    "deletedDateTime": null,
    "accountEnabled": true,
    "alternativeNames": [],
    "appDisplayName": "Test App Registration with Password",
    "appDescription": null,
    "appId": "dfc6c44a-562a-4914-9f88-3fae17098fbc",
    "applicationTemplateId": null,
    "appOwnerOrganizationId": "",
    "appRoleAssignmentRequired": false,
    "createdDateTime": "2023-03-13T23:37:59Z",
    "description": null,
    "disabledByMicrosoftStatus": null,
    "displayName": "Test App Registration with Password",
    "homepage": null,
    "loginUrl": null,
    "logoutUrl": null,
    "notes": null,
    "notificationEmailAddresses": [],
    "preferredSingleSignOnMode": null,
    "preferredTokenSigningKeyThumbprint": null,
    "replyUrls": [],
    "servicePrincipalNames": [
        "dfc6c44a-562a-4914-9f88-3fae17098fbc"
    ],
    "servicePrincipalType": "Application",
    "signInAudience": "AzureADMyOrg",
    "tags": [
        "WindowsAzureActiveDirectoryIntegratedApp"
    ],
    "tokenEncryptionKeyId": null,
    "samlSingleSignOnSettings": null,
    "addIns": [],
    "appRoles": [],
    "info": {
        "logoUrl": null,
        "marketingUrl": null,
        "privacyStatementUrl": null,
        "supportUrl": null,
        "termsOfServiceUrl": null
    },
    "keyCredentials": [],
    "oauth2PermissionScopes": [
        {
            "adminConsentDescription": "Allow the application to access Test App Registration with Password on behalf of the signed-in user.",
            "adminConsentDisplayName": "Access Test App Registration with Password",
            "id": "a396508a-24eb-421e-add0-e40c23995d9c",
            "isEnabled": true,
            "type": "User",
            "userConsentDescription": "Allow the application to access Test App Registration with Password on your behalf.",
            "userConsentDisplayName": "Access Test App Registration with Password",
            "value": "user_impersonation"
        }
    ],
    "passwordCredentials": [],
    "resourceSpecificApplicationPermissions": [],
    "verifiedPublisher": {
        "displayName": null,
        "verifiedPublisherId": null,
        "addedDateTime": null
    }
}

However looking at the app registration, we can see the password reference:

{
    "@odata.context": "https://graph.microsoft.com/v1.0/$metadata#applications/$entity",
    "id": "be545f8f-9a7c-4299-9a9b-2d996fb059da",
    "deletedDateTime": null,
    "appId": "dfc6c44a-562a-4914-9f88-3fae17098fbc",
    "applicationTemplateId": null,
    "disabledByMicrosoftStatus": null,
    "createdDateTime": "2023-03-13T23:37:58Z",
    "displayName": "Test App Registration with Password",
    "description": null,
    "groupMembershipClaims": null,
    "identifierUris": [],
    "isDeviceOnlyAuthSupported": null,
    "isFallbackPublicClient": null,
    "notes": null,
    "publisherDomain": "fabrikam.cloud",
    "serviceManagementReference": null,
    "signInAudience": "AzureADMyOrg",
    "tags": [],
    "tokenEncryptionKeyId": null,
    "samlMetadataUrl": null,
    "defaultRedirectUri": null,
    "certification": null,
    "optionalClaims": null,
    "servicePrincipalLockConfiguration": null,
    "requestSignatureVerification": null,
    "addIns": [],
    "api": {
        "acceptMappedClaims": null,
        "knownClientApplications": [],
        "requestedAccessTokenVersion": null,
        "oauth2PermissionScopes": [
            {
                "adminConsentDescription": "Allow the application to access Test App Registration with Password on behalf of the signed-in user.",
                "adminConsentDisplayName": "Access Test App Registration with Password",
                "id": "a396508a-24eb-421e-add0-e40c23995d9c",
                "isEnabled": true,
                "type": "User",
                "userConsentDescription": "Allow the application to access Test App Registration with Password on your behalf.",
                "userConsentDisplayName": "Access Test App Registration with Password",
                "value": "user_impersonation"
            }
        ],
        "preAuthorizedApplications": []
    },
    "appRoles": [],
    "info": {
        "logoUrl": null,
        "marketingUrl": null,
        "privacyStatementUrl": null,
        "supportUrl": null,
        "termsOfServiceUrl": null
    },
    "keyCredentials": [],
    "parentalControlSettings": {
        "countriesBlockedForMinors": [],
        "legalAgeGroupRule": "Allow"
    },
    "passwordCredentials": [
        {
            "customKeyIdentifier": null,
            "displayName": null,
            "endDateTime": "2024-03-13T23:37:59.1883406Z",
            "hint": "3nS",
            "keyId": "45aee527-4b1e-427d-8d0f-b7aab037f087",
            "secretText": null,
            "startDateTime": "2023-03-13T23:37:59.1883406Z"
        }
    ],
    "publicClient": {
        "redirectUris": []
    },
    "requiredResourceAccess": [],
    "verifiedPublisher": {
        "displayName": null,
        "verifiedPublisherId": null,
        "addedDateTime": null
    },
    "web": {
        "homePageUrl": null,
        "logoutUrl": null,
        "redirectUris": [],
        "implicitGrantSettings": {
            "enableAccessTokenIssuance": false,
            "enableIdTokenIssuance": true
        },
        "redirectUriSettings": []
    },
    "spa": {
        "redirectUris": []
    }
}

If we look at the associated application object, we can see this password:

Since there is no place in the Entra admin center that exposes service principal passwords, there is nothing to compare to there.

So Which Passwords Where?

If we can register a password on either the service principal or application object, how do we decide which to use and where?

In a test, I’ve assigned both the service principal and application object with a password set to an Azure subscription, and you can authenticate using either type of password credential.

Service Principal password:

c:\>az login --service-principal -u 1820efb7-fc82-46f6-90c8-bcfd5ca256d5 -p "e/ZtjS1f/ZIQhGEBOV1slzjhYI03OrMkysPcPh7uykU=" --tenant 11ae06df-10e8-4b9e-bf66-2a91f4955339
[
  {
    "cloudName": "AzureCloud",
    "homeTenantId": "",
    "id": "50d2637c-63fc-47e1-b9b1-114c5a08c7f5",
    "isDefault": true,
    "managedByTenants": [],
    "name": "MSDN Platforms",
    "state": "Enabled",
    "tenantId": "",
    "user": {
      "name": "1820efb7-fc82-46f6-90c8-bcfd5ca256d5",
      "type": "servicePrincipal"
    }
  }
]

Application Object password:

c:\>az login --service-principal -u dfc6c44a-562a-4914-9f88-3fae17098fbc -p "3nSvCR0zCTG8z0rew0jy23gmhh2fXkLsx7x3+hmaGhI=" --tenant 11ae06df-10e8-4b9e-bf66-2a91f4955339
[
  {
    "cloudName": "AzureCloud",
    "homeTenantId": "",
    "id": "50d2637c-63fc-47e1-b9b1-114c5a08c7f5",
    "isDefault": true,
    "managedByTenants": [],
    "name": "MSDN Platforms",
    "state": "Enabled",
    "tenantId": "",
    "user": {
      "name": "dfc6c44a-562a-4914-9f88-3fae17098fbc",
      "type": "servicePrincipal"
    }
  }
]

Notice the service principal and application object relationship again. While both are a type of service principal shown in the connection, we had to use the client ID as the user ID. The client ID is a value that only exists within the application object.

Most documentation from Microsoft indicates that if you are using service principals for integrations into Azure for things like subscription management, to use service principal passwords. I don’t see any downside to this, with the exception that the secrets cannot be managed within the Azure portal.

From a security boundary perspective, some folks will mention that not showing secrets within the Azure portal helps create a security boundary, however, many tenant-wide RBAC roles in Entra ID that permit secret management, such as adding/replacing/removing secrets, would have that capability on both service principals and application objects.

If you are developing or deploying a multitenant application you will need to use a client secret (password) within an application object (or preferably a certificate). This is because only the credentials in an application can be used across tenants.

This is where an understanding of OAuth 2.0 comes into play. There are several different ways in which OAuth 2.0 flows work, and you can also add OpenID Connect on top of things there as well. Keeping things a bit simplified, we’ll look at two common examples:

  • Delegated permissions
    1. When you attempt to access the application, you will be sent to the Entra ID OAuth 2.0 authorization endpoint.
    2. If you are not signed in you will be asked to do so.
    3. If you have not previously consented to the application, you will be prompted to. Alternatively, your organization may have already performed a tenant-wide consent.
    4. The app registration defines a redirect URI, which you will pass an authorization code you receive from Entra ID back to.
    5. The application will then take the authorization code and pass it to Entra ID, and in return obtain an access and refresh token.
    6. The access token can then be used for access to Microsoft Graph API, as an example, in which the permissions you consented to, the application will have access to on your behalf.
  • Application permissions
    1. When onboarding an application with application permissions, the application permissions must be granted with admin consent.
    2. If you are not signed in you will be asked to do so.
    3. Upon authentication and consent, you will provide a response to the application with your tenant ID.
    4. The application takes the secret and provides it to Entra ID, along with your tenant ID, to request an access token for the consented permissions.
    5. Entra ID provides the access token, and the application can then use that for access to the Microsoft Graph API, leveraging the permissions you granted.

It’s important to understand that in the world of Entra ID, there is a common OAuth 2.0 endpoint. While ever tenant has a security boundary, Entra ID itself is a single identity platform, and this is what allows authorization codes and secrets to be leveraged across tenants by the application developer. This is a good thing, as it removes any need for a developer to get into the sticky business of having to manage and coordinate secrets with customers.

If you are using a multitenant application, you can see these results yourself if you look at your Entra Sign-in logs for service principals:

If we look at the sign-in above, and then check our enterprise applications we’ll find the associated service principal ID (object ID):

And if we look at the app registration in the other tenant:

And we can even see that the certificate registered in the app registration was used for authentication, by matching the key ID. Of course, this is only to show that the secret/certificate is used cross-tenant, and we luckily have access to both tenants in our example:

"keyCredentials": [
		{
			"customKeyIdentifier": "VGVzdDEyMw==",
			"endDate": "2023-09-11T18:17:58.8837147Z",
			"keyId": "dc0c5dcd-7dd3-4f05-8bf7-5edc2dc4150e",
			"startDate": "2023-03-11T19:19:29.8224747Z",
			"type": "AsymmetricX509Cert",
			"usage": "Verify",

In my example I’m using a certificate, not a password, as the sample code used was out of the box designed to use a certificate (key pair).

Questions and Scenarios

Now that we have some foundation, let’s take a look at some common questions and scenarios.

Will Every App Registration Have a Corresponding Service Principal

Yes. Every app registration will have at least one service principal, which is created in the home tenant that the app registration was created in.

There are some examples of PowerShell code out there that will create a service principal but are missing the WindowsAzureActiveDirectoryIntegratedApp tag, which will prevent them from showing up in enterprise applications (even with the filter removed).

Will Every Enterprise Application/Service Principal have a corresponding app registration?

Yes, but not necessarily in your tenant.

To be more specific, and this is also where we need to understand that enterprise applications are more than just OAuth 2.0/OIDC applications.

If the application is OpenID Connect or OAuth 2.0, as we have seen above, the resource tenant will only have a service principal in the applications home tenant.

However, you may see other times that when you create an enterprise application that an associated app registration will show up in your tenant. In reality any application you add from enterprise applications – SAML, Linked, Password-based, or Entra Application Proxy, are all going to create an associated app registration. Why? Because the service principal was not designed to hold all the information that defines the app (back to our definition of an app registration).

You can add a SAML application into Entra ID, and then go to its corresponding app registration and look at the manifest.

Here is an example of the manifest for the SAML app of Claims X-Ray. Notice that some of the configuration settings for the application, such as the ACS URL, are stored within the manifest.

"name": "Claims X-Ray",
	"notes": null,
	"oauth2AllowIdTokenImplicitFlow": true,
	"oauth2AllowImplicitFlow": false,
	"oauth2Permissions": [
		{
			"adminConsentDescription": "Allow the application to access Claims X-Ray on behalf of the signed-in user.",
			"adminConsentDisplayName": "Access Claims X-Ray",
			"id": "a25f7792-6629-4854-a533-4914cc79ce41",
			"isEnabled": true,
			"lang": null,
			"origin": "Application",
			"type": "User",
			"userConsentDescription": "Allow the application to access Claims X-Ray on your behalf.",
			"userConsentDisplayName": "Access Claims X-Ray",
			"value": "user_impersonation"
		}
	],
	"oauth2RequirePostResponse": false,
	"optionalClaims": null,
	"orgRestrictions": [],
	"parentalControlSettings": {
		"countriesBlockedForMinors": [],
		"legalAgeGroupRule": "Allow"
	},
	"passwordCredentials": [],
	"preAuthorizedApplications": [],
	"publisherDomain": "fabrikam.cloud",
	"replyUrlsWithType": [
		{
			"url": "https://adfshelp.microsoft.com/ClaimsXray/TokenResponse",
			"type": "Web"
		}
	],

If you’ve integrated certain SAML applications and wanted to assign groups to custom roles to then emit in the SAML response, you would find yourself in the App registration to further define those.

But take notice that from a permissions perspective, going back to the security principal side of things, it is the enterprise application where you configure who is allowed to access the application.

Why do we need to handle the secret management for our single tenant application?

This is something that your developers are likely used to. Going back to the beginning of this post, while you have to agree to the permissions the application developer needs, the developer is the one who would be likely handling the secrets.

But if you’ve purchased a piece of software that ITPros would be the owners of – third party Exchange Online backup software, Identity Governance and Administration products, security assessment platforms – these are the things you will need to provide the secret to, in order for the applications to obtain the necessary access tokens. But, because of the way that OAuth 2.0 functions, you still need to go through that initial consent process.

What about for certain SaaS applications? Why do they need secrets?

It usually comes down to the type of SaaS application and how the applications multi-tenancy is handled.

Look at Salesforce. While this is a SaaS platform, each instance of Salesforce is left up to the organization that has acquired it to manage who can authenticate to what and how. So, while Salesforce supports OpenID Connect, it’s up to the organization that manages their instance of Salesforce to configure that. In this example for every Entra ID customer out there with Salesforce and OpenID Connect, there are that many essentially “duplicate” app registrations for it in Entra ID. Also – nothing is forcing Salesforce (in this example) to register their application in Entra ID. And it could easily go down a rabbit hole quite quickly – what about needing to register with Okta? Ping? Who and what do they have potentially partner relationships with? You also have to think about things like token customization and the data that you may want to be sent to Salesforce. You need to have control over the app registration to be able to do this with OIDC.

As such, it’s common for large and complex enterprise-oriented SaaS vendors to still require you to manage both ends of the OIDC/OAuth 2.0 configuration.

Why Can I consent in either the enterprise application or app registration?

Because the portal tries to make things magic. It’s simply an ease thing – the function behind the scenes is the same, regardless of where you consent.

Why do some service principals I’ve created with PowerShell not appear in the Enterprise applications view?

When creating a service principal, if the tag WindowsAzureActiveDirectoryIntegratedApp is not included in the definition, the service principal will not show up in Enterprise Applications. Microsoft is not completely clear as to why this tag is optional, as the application will still be available in the Conditional Access and Azure RBAC pick lists. One could presume you would not necessarily concern yourself with service principals created for things like automation products. Still, if you accidently created a service principal without this, you could simply run the following command to update it:

Set-AzureADServicePrincipal -ObjectId <objectid> -Tags @("WindowsAzureActiveDirectoryIntegratedApp")

Additional Resources

Authenticating to Entra ID with PowerShell using a service principal, Using a service principal | Microsoft Learn.

OAuth 2.0 client credentials flow on the Microsoft identity platform – Microsoft Entra | Microsoft Learn

Microsoft identity platform and OAuth 2.0 authorization code flow – Microsoft Entra | Microsoft Learn

Some great explanations from Frontegg on OAuth 2.0 grant types, OAuth Grant Types: Explained | Frontegg

Additional explanations from Frontegg on OAuth 2.0 flows, Demystifying OAuth Flows | Frontegg

Okta guide to OpenID Connect and OAuth 2.0, An Illustrated Guide to OAuth and OpenID Connect | Okta Developer

About This Posts Featured Image

The chosen photo is the work of Javier Allegue Barros, used under the Unsplash license.

Eric Woodruff
Eric Woodruff

One Response

Leave a Reply

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

Author picture

Writing about all things identity and identity adjacent in the Microsoft ecosystem of Azure AD and Active Directory.

Read More
Mailing List

Subscribe to posts here

Categories