Azure Active Directory application model
- 1/22/2016
- The building blocks: Application and ServicePrincipal
- Consent and delegated permissions
- App user assignment, app permissions, and app roles
- Groups
- Summary
It’s time to take a closer look at how Azure AD represents applications and their relationships to other apps, users, and organizations.
You got a brief taste of the Azure AD application model in Chapter 3, “Introducing Azure Active Directory and Active Directory Federation Services.” Later on you experienced firsthand a couple of ways to provision apps and use their protocol coordinates in authentication flows. Here I will go much deeper into the constructs used by Azure AD to represent apps, the mechanisms used to provision apps beyond one’s own organization, and the consent framework, which is the backbone of pretty much all of this. I’ll also touch on roles, groups, and other features that Azure AD offers to grant fine-grained access control to your application.
The application model in Azure AD is designed to sustain many different functions:
- It holds all the data required to support authentication at run time.
- It holds all the data for deciding what other resources an application might need to access and whether a given request should be fulfilled and under what circumstances.
- It provides the infrastructure for implementing application provisioning, both within the app developer’s tenant and to any other Azure AD tenant.
- It enables end users and administrators to dynamically grant or deny consent for the app to access resources on their behalf.
- It enables administrators to be the ultimate arbiters of what apps are allowed to do and which users can use specific apps, and in general to be stewards of how the directory resources are accessed.
That is A LOT more than setting up a trust relationship, the basic provisioning step you perform with traditional on-premises authorities like ADFS. Remember how I often bragged about how much easier it is to provision apps in Azure AD? What makes that feat possible is the highly sophisticated application model in Azure AD, which goes to great lengths to make life easy for administrators and end users. Unfortunately, the total complexity of the system remains roughly constant, so somebody must work harder to compensate for that simplification, and this time that somebody is the developer. I could work around that complexity and simply give you a list of recipes to follow to the letter for the most common tasks, but by now you know that this book doesn’t work that way. Instead, we’ll dig deep to understand the building blocks and true motivation of each moving part—and once we emerge, everything will make sense. Don’t worry, the model is very manageable and, once you get the hang of it, even easy, but some investment is required to understand it. This chapter is here to help you do just that.
The building blocks: Application and ServicePrincipal
Since Azure AD first appeared on the market, a lot of content has been published about its application model. A large part of that content was produced while the application model had not yet solidified in its current form. To avoid any confusion, I am going to open this section with a bit of history: by understanding how we got to where we are today, you won’t risk getting confused if you happen to stumble on documentation and samples from another epoch.
In traditional Active Directory, every entity that can be authenticated is represented by a principal. That’s true for users, and that’s true for applications—in the latter case, we speak of service principals. In traditional Kerberos, service principals are used to ensure that a client is speaking to the intended service and that a ticket is actually intended for a given service. In other words, they are used for any activity that requires establishing the identity of the service application itself.
Although Azure AD has been designed from the ground up to address modern workloads, it remains a directory. As such, it retains many of the concepts and constructs that power its on-premises ancestor, and service principals are among those. If you use the Internet time machine and fish out content from summer 2012, describing the very first preview of Azure AD development features, you’ll see that at that time, provisioning an application in Azure AD was done by using special Windows PowerShell cmdlets, which created a new service principal for the app in the directory. Even the format of the service principal name was a reminder of its Kerberos legacy, following a fixed schema based on the app’s execution environment. Disregarding the protocols it enabled, that service principal already had all the things we know are needed for supporting authentication transactions: application identifiers, a redirect URI, and so on.
Service principals are a great way of representing an application’s instance, but they aren’t very good at supporting the development of the application itself. Their limitations stem from two key considerations:
- Applications are usually an abstract entity, made of code and resources: the service principal represents a concrete instance of that abstract entity in a specific directory. You will want that abstract entity to have many concrete instances, especially if you build and sell software for a living: one or more instances for each of your customers’ organizations. Even if you are building applications for your own organization, to be used by your colleagues, chances are that you’ll want to work with multiple instances—for example, development, staging, and production. If the only building block at your disposal were app instances, development and deployments would be unnatural, denormalized, and repetitive. For one thing, every time you changed something, you’d have to go chase all your app instances and make the same change everywhere.
- Although so far we have seen applications mostly as resources one user gains access to, a directory sees applications as clients, which need to access resources under the control of the directory. Even the act of a user requesting a token for accessing an application is seen by the directory as the application itself gaining access to the user’s identity information. Through this optic, you can see how some applications can be pretty powerful clients, performing functions that range from reading users' personally identifiable information (PII) to modifying the directory itself: deleting users, creating groups, changing passwords—the works. Application instances are normally put in operation by administrators, who enjoy those powers themselves. Hence, they have the faculty to imbue applications with such capabilities. If your company is big enough for employees not to have to juggle multiple hats, however, developers are traditionally not administrators. If service principals were the only way to create an application, very few employees in a company would have the power to develop apps. It gets worse: in today’s software as a service (SaaS) push, it is in the developer’s best interest that end users be empowered to elect to start using applications, but most users aren’t administrators either. Even more than in the development case, this exposes the limits of perpetrating the service principal model “as is” in the cloud.
Given this, and for various other reasons, Azure AD defines a new entity, the Application, which is meant to describe an application as an abstract entity: a template, if you will. As a developer, you work with Applications. At deployment time a given Application object can be used as a blueprint to create a ServicePrincipal representing a concrete instance of an application in a directory. It’s that ServicePrincipal that is used to define what the app can actually do in that specific target directory, who can use it, what resources it has access to, and so on.
Bear with me just a little longer, the abstract part is almost over. The main way through which Azure AD creates a ServicePrincipal from an Application is consent. Here’s a simplified description of the flow: Say that you create an Application object in directory A, supplying all the protocol coordinates we’ve discussed so far in earlier chapters. Say that a user from tenant B navigates to the app’s pages and triggers an authentication flow. Azure AD authenticates the user from B against its home directory, B. In so doing, it sees that there is no ServicePrincipal for the app in B; hence, it prompts the user about whether he or she wants to consent for that app to have access to the directory B (you’ll see later in what capacity). If the user grants consent, Azure AD uses the Application object in A as a blueprint for creating a ServicePrincipal in B. Along with that, B records that the current user consented to the use of this application (expect lots of details on this later on). Once that’s done, the user receives a token for accessing the app . . . and provisioning magically happens. No lengthy negotiations between administrators required. Isn’t Azure AD awesome? Figure 8-1 summarizes the process.
Figure 8-1 Simplified provisioning flow driven by consent: 1) a user from B attempts to sign in with the app; 2) the user credentials are acquired and verified; 3) the user is prompted to consent for the app to gain access to tenant B; the user consents; 4) Azure AD uses the Application object in A as a blueprint for creating a ServicePrincipal in B; 5) the user receives the requested token.
You can iterate the process shown in Figure 8-1 as many times as you want, for directory C, D, E, and so on. Directory A retains the blueprint of the app, in the form of its Application object. The users and admins of all the directories where the app is given consent retain control over what the application is allowed to do (and a lot more) through the corresponding ServicePrincipal object in each tenant.
In the next two subsections, you’ll take a look at the content of the Application and ServicePrincipal objects. This will give me an opportunity to introduce lots of new directory artifacts, which in turn will refine your understanding of what an application is for Azure AD and what it can do for you.
The Application
The Application object in Azure AD is meant to describe three distinct aspects of an application:
- The identifiers, protocol coordinates, and authentication options that come into play when a token is requested for accessing the application.
- The resources that the application itself might need to access, and the actions it might need to take, in order to perform its functions. For example, an application might need to write back to the directory, or it might need to send email via Exchange as the authenticated user. You’ll have to wait until the next chapter to learn how to actually perform these actions in code, but it’s important to understand in this context the provisioning and consent mechanisms underpinning this aspect.
- The actions that the application itself offers. For example, an application representing a facade for a data store might allow for read and write operations—and make it possible for the directory to decide whether to grant a client permission to do only read operations, or both read and write, depending on the identity of the client. This feature is used when the application is a web API, but it rarely comes into play when doing web sign-on, so I won’t spend much time on it in this chapter.
So far you’ve acted directly only on the first aspect. You indirectly took advantage of the defaults in the second point—every web app is configured to ask for permissions to sign in and access the user’s profile. You have not interacted with the third aspect yet, but you will in Chapter 9, “Consuming and exposing a web API protected by Azure Active Directory.”
Mercifully, neither the Azure portal or the Visual Studio ASP.NET project templates wizards ask you to provide values for all the properties that constitute an Application object. The vast majority of those properties are assigned default values that work great for most of the populace, who can get their web sign-on functionality by providing just a handful of strings (as you have seen, mainly name and redirect_uri) without ever being aware that there are customizations available.
That said, if you do want to know what’s available in the Application object, how would you go about it? You have three strategies to choose from:
Head to the Azure portal (https://manage.windowsazure.com), go to the Azure AD section, select the Applications tab, search for your app, select it, then click Configuration. You’ll see far more info there than you provided at creation time. One example you are already familiar with is the client_id, which is assigned by Azure AD to your app when it’s created.
The information shown there is what you would probably customize to meet the requirements of the most common scenarios. However, not all the application features are exposed there.
- Still in the Azure portal, with your app selected, you can use a link at the bottom of the page, Manage Manifest, to download a JSON file that contains the verbatim dump of the corresponding Application entity in the directory. You can edit this file to change whatever you want to control, then upload it again (through the same portal commands) to reflect your new options in the directory.
- Finally, you can use the Directory Graph API (mentioned in Chapter 3) to query the directory and GET the Application object, once again in JSON format.
The first method goes against the policy I am adopting in the book—the portal UX can change far too easily after the book is in print, so including screenshots of it would be a bad idea. Also, it does not go nearly deep enough for my purposes here.
The second method, the manifest, would work out well—and is the method I advise you to use when you work with your applications. However, there is something that makes it less suitable for explaining the anatomy of the Application object for the first time: the manifest is a true object dump from the directory, and for pure inheritance reasons it includes lots of properties that aren’t useful or relevant for the Application itself.
To keep the signal-to-noise ratio as crisp as possible, the JSON snippets I’ll show you here will all be obtained through the third method, direct queries through the Graph. I am using a very handy sample web app (which you can find at https://graphexplorer.cloudapp.net), which provides an easy UI for querying the graph. I cannot guarantee that the app will still be available when you read this book, but performing those queries through code, or with curl or via Fiddler, is extremely easy. In the next chapter you’ll learn how.
Following is a dump of the Application object that corresponds to the sample app we’ve been working with so far. The query I used for obtaining it is as follows:
https://graph.windows.net/developertenant.onmicrosoft.com/ applications?$filter=appId+eq+'e8040965-f52a-4494-96ab-0ef07b591e3f'&api-version=1.5
You’ll likely recognize the typical OData ‘$’ syntax. The GUID you see there is the client_id of the application. Here’s the complete JSON from the result:
{ "odata.metadata": "https://graph.windows.net/developertenant.onmicrosoft. com/$metadata#directoryObjects/Microsoft.DirectoryServices.Application", "value": [ { "odata.type": "Microsoft.DirectoryServices.Application", "objectType": "Application", "objectId": "c806648a-f27d-43fd-9f18-999f7708fcfc", "deletionTimestamp": null, "appId": "e8040965-f52a-4494-96ab-0ef07b591e3f", "appRoles": [], "availableToOtherTenants": false, "displayName": "WebAppChapter5", "errorUrl": null, "groupMembershipClaims": null, "homepage": "https://localhost:44300/", "identifierUris": [ "https://localhost:44300/WebProjectChapter5" ], "keyCredentials": [], "knownClientApplications": [], "logoutUrl": null, "oauth2AllowImplicitFlow": false, "oauth2AllowUrlPathMatching": false, "oauth2Permissions": [ { "adminConsentDescription": "Allow the application to access WebAppChapter5 on behalf of the signed-in user.", "adminConsentDisplayName": "Access WebAppChapter5", "id": "00431d04-5334-4da6-8396-0e6f54631f10", "isEnabled": true, "type": "User", "userConsentDescription": "Allow the application to access WebAppChapter5 on your behalf.", "userConsentDisplayName": "Access WebAppChapter5", "value": "user_impersonation" } ], "oauth2RequirePostResponse": false, "passwordCredentials": [], "publicClient": null, "replyUrls": [ "https://localhost:44300/" ], "requiredResourceAccess": [ { "resourceAppId": "00000002-0000-0000-c000-000000000000", "resourceAccess": [ { "id": "311a71cc-e848-46a1-bdf8-97ff7156d8e6", "type": "Scope" } ] } ], "samlMetadataUrl": null } ] }
Feel free to ignore anything that starts with “odata” here. Also, some properties listed are for internal use only or are about to be deprecated, so I won’t talk about those.
The most “meta” properties here are objectId and deletionTimestamp.
- objectId is the unique identifier for this Application entry in the directory. Note, this is not the identifier used to identify the app in any protocol transaction—you can think of it as the ID of the row where the Application object is saved in the directory store. It is used for referencing the object in most directory queries and in cross-entity references.
- deletionTimestamp is always null, unless you delete the Application, which in that case it records the instant in which you do so. Azure AD implements most eliminations as soft deletes so that you can repent and restore the object without too much pain should you realize the deletion was a mistake.
Properties used for authentication
The bulk of the properties of the Application object control aspects of the authentication, specifying parameters that define the app from the protocol’s perspective, turning options on and off, and providing experience customizations.
Here’s the complete list:
- appId This corresponds to the client_id of the application.
replyUrls This multivalue property holds the list of registered redirect_uri values that Azure AD will accept as destinations when returning tokens. No other URI will be accepted. This property is the source of some of the most common errors: even the smallest mismatch (trailing slash missing, different casing) will cause the token-issuance operation to fail.
Although at creation time the only URL in the collection is the one you specified, as is the case with the localhost-based URL in the sample here, you’ll often find yourself adding more URLs as your app moves past the development stage and gets deployed to staging and production. If you want to achieve complete isolation between application deployments, you can always create an entirely new Application for every environment, each with its own client_id.
identifierUris This multivalue property holds a collection of developer-assigned application identifiers, as opposed to the directory-assigned client_id.
These values are used to represent the application as a resource in protocols such as SAML and WS-Federation, where they map to the concept of realm. The URIs are also used as audience in access tokens issued for the app via OAuth2, when the app is consumed as a web API (as opposed to a web app with an HTML UX). This often generates confusion, given that this scenario can also be implemented by using the app’s client_id instead of one identifier URI. More about this in Chapter 9.
publicClient In the current Azure AD model, applications can be either confidential clients (apps that can have their own credentials, usually run on servers, etc.) or public clients (mobile and native apps running on devices, with no credentials, hence no strong identity of their own). The security characteristics of the two app types are very different, and so is the set of protocols that the two types support. For example, a native client cannot obtain a token purely with its app identity because it has no identity of its own; and a confidential client cannot request tokens with user-only flows, where the identity of the app would not play a role.
This book focuses on web apps; hence, confidential clients. That means that the apps discussed here will always have the publicClient property set to null.
- passwordCredentials, keyCredentials These properties hold references to application-assigned credentials, string-based shared secrets and X.509 certificates, respectively. Only confidential clients can have nonempty values here. Those credentials come into play when requesting access tokens—in other words, when the app is acting as a client rather than as a resource itself. You’ll see more of that in the next chapter.
- displayName This property determines how the application is called in interactions with end users, such as consent prompts. It’s also the mnemonic moniker used to indicate the application for the developer in the Azure management portal. Given that the display name has no uniqueness requirements, it’s not always a way to conclusively identify one app in a long list.
- Homepage The URL saved here is used to point to the application from its entry in application portals such as the Office 365 application store. It does not play any role in the protocol behavior of the app; it’s just whatever landing page you want visitors, prospective buyers, and corporate users (who might get there through the list of applications their company uses) to use as an entry point. At creation time, the Homepage value is copied from the replyUrls property. A common bit of advice to software developers from Office is to ensure that the URL in Homepage corresponds to a protected page/route in your application so that if visitors are already authenticated when they click the link, they’ll find themselves authenticated with the same identity in your app as well.
- samlMetadataUrl In case you are implementing SAML in your app, this property allows you to specify where your app publishes its own SAML metadata document.
- oauth2AllowImplicitFlow This flag, defaulting to false, determines whether your app allows requests for tokens for the app via implicit flow.
- oauth2AllowUrlPathMatching By default, Azure AD requires all redirect_uris in a request to be a perfect match of any of the entries in replyURLs. This is a very good policy, designed to mitigate the open redirector attack—an attack in which appending extra parameters to one redirect_uri might lead to the resulting token being forwarded to a malicious party. However, there are situations in which your app might need to have more flexibility and use return_uris that do have a tail of extra characters that aren’t part of the registered values. Setting this property to true tells Azure AD that you want to relax the perfect match constraint, and allows you to use URLs that are a superset of the ones you registered. Before changing this value, make sure you truly need it and that you have mitigations in place.
- oauth2RequirePostResponse Azure AD expects all requests to be carried through a GET operation. Setting this property to true relaxes that constraint.
- groupMembershipClaims If you want to receive group membership information as claims in the tokens issued for your user, you can use this property to express that requirement. Setting groupMembershipClaims to SecurityGroup results in a token containing all the security group memberships of the user. Setting it to All results in a token containing both security group and distribution list memberships. The default, null, results in no group information in the token. Note that the group claims do not include the group name; rather, they carry a GUID that uniquely identifies the group within the tenant. I’ll spend more time on this topic later in the chapter.
- appRoles This property is used for declaring roles associated with the application. I provide a complete explanation of this property in later sections of this chapter.
availableToOtherTenants This property deviates from the strictly protocol-related functionalities: it’s more about controlling the provisioning aspect. Every confidential client application starts its existence as an app that can be accessed only by accounts from the same directory tenant in which the application was created. That’s the typical line-of-business application scenario, where the IT department of one company develops an app to be used by their fellow employees. Any attempt to get tokens for the app from a different tenant will not work (excluding guest scenarios, which will be mentioned later).
However, that clearly does not work if your intent is to make the application available across organizations: that is the case for SaaS scenarios, naturally. If you are in that situation, you can flip availableToOtherTenants to true. That will make Azure AD allow requests from other tenants to trigger the consent flow I described briefly earlier instead of carrying out the default behavior, in which the request would be rejected right away.
Applications available across tenants (what we commonly call “multitenant apps”) have extra constraints. For example, whereas identifierURIs can normally be any URI with no restrictions, for multitenant apps those URIs must be proper URLs and their hostname must match a domain that is registered with the tenant. Also, only tenant administrators can promote an app to be multitenant. The consent for a multitenant app clearly identifies the tenant as the publisher of the app to potentially every other organization using Azure AD—with important repercussions on reputation should something go south.
knownClientApplications The last property listed here is also about provisioning. You have seen how consenting for one application to have access to your own directory results in the creation of a ServicePrincipal for the app in the target directory. To anticipate a bit the upcoming discussion on permissions, the idea is that the ServicePrincipal will also need to record the list of resources and actions on those resources that the user consented to. This is possible only if the requested resources are already present with their own ServicePrincipal entries in the target directory. That is usually the case for first-party resources: if your app needs access to the Directory Graph or Exchange online, you can expect those to already have an entry in the directory. It will occasionally happen that your solution includes both a client application and one custom web API application. You’ll want your prospective customers to have to consent only once, when they first try to get a token for the client application. If consent can happen only when all the requested resources are present as a ServicePrincipal in the target directory, and one of the resources you need is your own API, you have a problem. It looks like you have to ask your user to first consent to the web API so that it can create its ServicePrincipal in the target directory, and only after that ask the user to go back and consent to the client application.
Well, this property exists to save you from having to do all that work. Say that the application you are working on is the web API project. If you save in knownClientApplications the client_id (the appId, that is) of the client application you want to use for accessing your API, Azure AD will know that consenting to the client means implicitly consenting to the web API, too, and will automatically provision ServicePrincipals for both the client and web API at the same time, with a single consent. Handy!
The main catch in all this is that both the client and the web API application must be defined within the same tenant. You cannot list in knownClientApplications the client_id of a client defined in a different tenant.
oauth2Permissions: What actions does the app expose?
The oauth2Permissions collection publishes the list of things that client applications can do with your app—the scopes the app admits, mostly, but that comes into play only in case your app is a web API. If your app is a web application with a UX, the expectation is that browsers will request tokens for your app with the goal of signing in. That does not require any entry for web sign-on, the scenario considered in this chapter, so I thought of deferring coverage of this property until I get to exposing your own web API, but some of the concepts will come in handy sooner than that, so I’ll give you a bit of background now. Let’s take a closer look at the only entry in the oauth2Permissions collection for the sample application:
{ "adminConsentDescription": "Allow the application to access WebAppChapter5 on behalf of the signed-in user.", "adminConsentDisplayName": "Access WebAppChapter5", "id": "00431d04-5334-4da6-8396-0e6f54631f10", "isEnabled": true, "type": "User", "userConsentDescription": "Allow the application to access WebAppChapter5 on your behalf.", "userConsentDisplayName": "Access WebAppChapter5", "value": "user_impersonation" }
The schema is pretty straightforward:
The ID uniquely identifies the permission within this resource.
Each property ending in “description” or “name” indicates how to identify and describe this permission in the context of interactive operations, such as consent prompts or Application configuration at development time.
The type property indicates whether this permission can be granted by any user in the directory (in which case it is populated with the value User) or is a high-value capability that can be granted only by an administrator (in which case, the value is Admin).
The value property represents the value in the scope claim that a token will carry to signal the fact that the caller was granted this permission by the directory. That is what the app should look for in the incoming token to decide whether the caller should be allowed to exercise the function gated by this permission.
I’ll come back to this collection in Chapter 9.
requiredResourceAccess: What resources the app needs
This is one of the most powerful entries in the Application object, which can lead to utter despair when things go wrong:
"requiredResourceAccess": [ { "resourceAppId": "00000002-0000-0000-c000-000000000000", "resourceAccess": [ { "id": "311a71cc-e848-46a1-bdf8-97ff7156d8e6", "type": "Scope" } ] }
You can think of requiredResourceAccess as the client-side partner of oauth2Permissions. The requiredResourceAccess entry lists all the resources and permissions the application needs access to, referring to the entries each of those resources expose through their own oauth2Permissions entries. For each resource, requiredResourceAccess specifies:
- The appId of the requested resource, via the resourceAppId property
Which specific permissions it is after, via the resourceAccess collection, which contains
- The permission ID—the same ID the resource declared (in its own Application object) for the permission in its own corresponding oauth2Permission entry
- The type of access it intends to perform: possible values are Scope and Role.
In our sample, the resource we need access to is the directory itself, in the form of the Graph API—the identifier 00000002-0000-0000-c000-000000000000 is reserved for the Graph in all tenants. The permission we are requesting, of ID 311a71cc-e848-46a1-bdf8-97ff7156d8e6, corresponds to “sign in and access the user’s profile.” I know it doesn’t sound that easy to remember . . . but it is not supposed to be. The Azure portal or the Visual Studio project wizards normally take care of putting those values there for you when you select the human-readable counterparts in their UIs.
The type of access Scope determines that the app request the permission in delegated fashion; that is to say, as the identity of the user who’s doing the request. Whether an admin user is required for successfully obtaining this permission at run time, or a normal user can suffice, is determined by the Type declared in the corresponding oauth2Permission entry—found in the Application object of the resource exposing the permission. As you have seen in the preceding section, the possible values are User and Admin. If the permission declares the latter, only an administrator can consent to it.
A requiredResourceAccess entry with a Type of value Role indicates that the application requires that permission with its own application identity, regardless of which user identity is used to obtain the token (if any—there are ways for an app to get tokens with no users involved, and I’ll talk about that in the “Application permissions” section toward the end of this chapter). This option does require consent from an administrator.
Now here is a super important concept; put everything else down and read very carefully. In the current Azure AD model, one application must declare in advance all resources it needs access to, and all the associated permissions it requires. At the first request for a token for that app, that list will be presented to the user in its entirety, regardless of what resources are actually needed for that specific request. Once the user successfully grants consent, a ServicePrincipal will be provisioned, and that consent will be recorded in the target directory (you’ll see later how that happens in practice) for all the requested resources. This makes it possible to prompt the user for consent only once.
The side effect of this approach is that the list of consented permissions is static. If you decide to add a new permission request to your application after a customer of yours already consented to it in its own directory, your customer will not be able to obtain the new permission for your app in the customer’s own tenant until he or she revokes consent in its entirety and then grants it again. This can sometimes be painful. In version 2 of Azure AD, we are working hard to eliminate this constraint, but in version 1, that is the way it is today.
Figure 8-2 summarizes the main functional groups the Application object’s properties fall into. Sure, there are a lot of details to keep in mind, but at the end of the day, more often than not, this simple subdivision will help you to ignore the noise and zero in on the properties you need for your scenario.
Figure 8-2 A functional grouping of the properties of the Application object in Azure AD.
The ServicePrincipal object
In later sections you will study in detail how an app goes from one Application object in one tenant to one or more ServicePrincipals in one or more tenants. In this section, I’ve assumed that such provisioning has already happened and will focus on the properties of the resulting ServicePrincipal: what properties are copied as is from the Application, what doesn’t make it through, and what’s added that is unique.
Following is the ServicePrincipal for our sample app. It is deployed on the same tenant as the Application, but for our analysis that doesn’t matter. At first glance, it does look a lot like the Application itself, but it is in fact quite different.
{ "odata.type": "Microsoft.DirectoryServices.ServicePrincipal", "objectType": "ServicePrincipal", "objectId": "29f565fd-0889-43ff-aa7f-3e7c37fd95b4", "deletionTimestamp": null, "accountEnabled": true, "appDisplayName": "WebAppChapter5", "appId": "e8040965-f52a-4494-96ab-0ef07b591e3f", "appOwnerTenantId": "6c3d51dd-f0e5-4959-b4ea-a80c4e36fe5e", "appRoleAssignmentRequired": false, "appRoles": [], "displayName": "WebAppChapter5", "errorUrl": null, "homepage": "https://localhost:44300/", "keyCredentials": [], "logoutUrl": null, "oauth2Permissions": [ { "adminConsentDescription": "Allow the application to access WebAppChapter5 on behalf of the signed-in user.", "adminConsentDisplayName": "Access WebAppChapter5", "id": "00431d04-5334-4da6-8396-0e6f54631f10", "isEnabled": true, "type": "User", "userConsentDescription": "Allow the application to access WebAppChapter5 on your behalf.", "userConsentDisplayName": "Access WebAppChapter5", "value": "user_impersonation" } ], "passwordCredentials": [], "preferredTokenSigningKeyThumbprint": null, "publisherName": "Developer Tenant", "replyUrls": [], "samlMetadataUrl": null, "servicePrincipalNames": [ "https://localhost:44300/WebProjectChapter5", "e8040965-f52a-4494-96ab-0ef07b591e3f" ], "tags": [ "WindowsAzureActiveDirectoryIntegratedApp" ] }
I am sure you are not surprised to find objectId and deletionTimestamp here, too.
Notably missing are all the flags determining protocol behaviors at run time: availableToOtherTenants, groupMembershipClaims, oauth2AllowImplicitFlow, oauth2AllowUrlPathMatching, oauth2-RequirePostResponse, and publicClient. Other properties that don’t directly make it in the form of properties in ServicePrincipal are knownClientApplications and requiredResourceAccess, both of which are properties that influence the consent process and the very creation of this ServicePrincipal. As you will see later on, requiredResourceAccess gets recorded in a different form—one that makes it easier for the directory to track down who in the tenant has actually been granted the necessary permissions to use the app.
Properties that do transfer as is from the Application to its corresponding ServicePrincipal are the appId (containing the all-important client_id), various optional URLs (errorUrl, logoutUrl, samlMetadata-Url), the settings used when listing the app in some UX (displayName, homepage), the exposed appRoles and oauth2Permissions, and finally the credentials keyCredentials and passwordCredentials. The presence of the credentials in the ServicePrincipal has important implications: it means that your code can use the same credentials defined in the Application and those will work on every ServicePrincipal in every tenant.
Here’s a list of the brand-new properties:
- appOwnerTenantId This property carries the tenantId of the tenant where you’ll find the Application object that was used as a blueprint for creating this ServicePrincipal—in this case, developertenant.onmicrosoft.com. If you search Chapter 6 for the GUID value shown in our example’s ServicePrincipal, you’ll find it everywhere.
- publisherName Another property meant to be used for describing the app in user interactions, publisherName stores the display name of the tenant where the original Application was defined. This represents the organization that published the app.
- servicePrincipalNames This property holds all the identifiers that can be used for referring to this application in protocol flows: as you might have noticed in the sample, it contains the union of the values in the identifierUris collection and the appId value from the Application object. The former is used for OAuth2 and OpenID Connect flows, the latter for WS-Federation, SAML, or OAuth2 bearer token resource access requests.
- appRoleAssignmentRequired Administrators can decide to explicitly name the user accounts that they want to enable for the user of the app and gate the token issuance on this condition. If appRoleAssignmentRequired is set to true, only the token requests coming from explicitly assigned users will be fulfilled. I’ll talk more about this later in the chapter.
- tags This property is used mostly by the Azure portal to determine the type of application and how to present it in the administrative interface. Without going into fine detail, an empty tag collection results in the corresponding ServicePrincipal not being shown as one of the resources that can be requested by other applications.