From time to time, I get asked this question by a few different customers especially when they encounter the error “AADSTS7000218: The request body must contain the following parameter: ‘client_assertion’ or ‘client_secret'” when authenticating to Azure AD. The error is related to the following Default client type setting in the Authentication blade of a registered application:
By default the setting is set to No (confidential client). Changing to ‘Yes’ converts the default client type to public client. In the application manifest file, this setting is “allowPublicClient” which can be set to true for public client and false or null for confidential client.
This setting is not about the Identity Provider (Azure AD)’s security feature. It is about the client application’s design flow and the environment the application is used in. Changing the type does not cause Azure AD to provide any more or less security protection for the application than the other setting. It only changes what Azure AD expects from the client application during authentication. A confidential client is expected to provide a secret (or assertion) when authenticating to Azure AD while a public client does not have to provide this parameter.
The client type is rooted in the OAuth2 specification…
According to the OAuth2 specification, there are 2 client types based on their ability to authenticate securely to the Authorization Server (Azure AD):
- Confidential client
- Public client
Jacob Jenkov’s tutorial site has a pretty good description of these two types:
A confidential client is an application that is capable of keeping a client password confidential to the world. This client password is assigned to the client app by the authorization server. This password is used to identify the client to the authorization server, to avoid fraud. An example of a confidential client could be a web app, where no one but the administrator can get access to the server, and see the client password.
To elaborate more on the case of a web app acting as a confidential client using the OAuth2 Authorization Code grant flow for authentication, there are 2 parts to this grant flow:
- The first part happens in the browser making a request to the authorize endpoint for the user to enter his/her login credential. One can examine the traffic here to obtain the client ID of the web app. An authorization code is obtained in this step upon successful login.
- The second part involves the browser sending the authorization code to the web app. The web app then makes a request to the token endpoint with the received authorization code and its secret to get an access token. This token acquisition request happens in a backend server, a secure environment where the users do not have access to to see the secret. Without the secret a malicious app can’t easily fake the identity of this web app to get a token even though it might have managed to steal the authorization code.
Does changing the default type to public mean Azure AD will always treat my application as a public client?
No. A distributed application can be both a confidential and a public client since it may have confidential type components capable of authenticating securely to Azure AD while other components acting as a public client running on a user device. Azure AD uses the redirect URL (if it exists) in the authentication request to see which reply URL platform it’s registered under to determine the application type. Only reply URLs registered in the Web platform are for confidential client (well…almost with the exception of Single Page Application using Implicit Grant flow) while other platform types are for public client.
In certain OAuth2 flows, for instance ROPC, Device Code flow, and SAML Bearer Assertion, there is no redirect URL present in the authenticating request. In these cases Azure AD uses this default type setting to determine the client application type.
OAuth2 Grant types and the client profile type
Although the OAuth2 grant types are not strictly tied to the client types, there are plenty of documentation about the scenarios and environment appropriate for each OAuth2 grant flow. The Azure AD Authentication flows documentation has a good table describing each OAuth2 Grant type and when/where it should be used. I am expanding on this table to provide info about the appropriate client profile types.
|Flow||Description||Used in||Profile Type|
|Authorization Code (with PKCE)|
Used in apps that are installed on a device to gain access to protected resources, such as web APIs. Enables you to add sign-in and API access to your mobile and desktop apps.
|Desktop Apps, mobile apps, web apps, Single Page Applications (SPA)||confidential type for web apps, and public for others|
Allows you to access web-hosted resources by using the identity of an application. Commonly used for server-to-server interactions that must run in the background, without immediate interaction with a user.
|Device Code||Allows users to sign in to input-constrained devices such as a smart TV, IoT device, or printer.||Desktop/mobile apps||public|
|On-behalf-of||An application invokes a service or web API, which in turn needs to call another service or web API. The idea is to propagate the delegated user identity and permissions through the request chain.||Web APIs||confidential|
|ROPC or Username/Password||Allows an application to sign in the user by directly handling their password. This flow isn’t recommended.||Desktop/mobile apps||public|
|SAML Assertion Grant (used in Integrated Windows Authentication||Allows applications on domain or Azure Active Directory (Azure AD) joined computers to acquire a token silently (without any UI interaction from the user).||Desktop/mobile apps||public|
Using the same OAuth2 client type concept, MSAL.NET defines
IPublicClientApplication interfaces for public client applications and
IConfidentialClientApplication for confidential client applications. Both client application types share the same set of properties defined in the base interface IClientApplicationBase. The only difference between the two is that the IConfidentialClientApplication interface also has a ClientSecret property to hold an application secret. Each MSAL client app type supports different OAuth2 grant flows for acquiring a token.
Public client application:
|supported OAuth2 Grant flow||AcquireToken method|
|ROPC (for managed account)||AcquireTokenByUsernamePassword|
|SAML Assertion Grant flow (for federated account)||AcquireTokenByIntegratedWindowsAuth (for domain and AAD joined machines) and|
AcquireTokenByUsernamePassword other environment
|Device Code flow (for devices without a browser)||AcquireTokenWithDeviceCode|
Confidential client application
|OAuth2 Grant type||AcquireToken method|
|Authorization Code (only for the 2nd leg typically used on a web app to exchange an Authorization Code for an Access Token)||AcquireTokenByAuthorizationCode|
|On-Behalf-Of (aka OBO flow used for one web API calling another downstream web API)||AcquireTokenOnBehalfOf|
Besides changing the default client type, can I do anything else to avoid the AADSTS7000218 error?
If your application manages the underlying HTTP requests to Azure AD for authentication, you can supply either a client secret or client assertion parameter. However if you are using a library (such as MSAL) to acquire the token then you will have to check if the library has the option to provide secret or assertion for that particular OAuth2 flow. For applications using MSAL.Net to instantiate a Public Client to acquire a token one will have to change the default client type since by definition a public client can’t hold any type of secret.