Skip to content

Client Applications

Jean-Marc Prieur edited this page May 17, 2021 · 40 revisions

Public Client and Confidential Client applications

Contrary to ADAL.NET (which proposes the notion of AuthenticationContext, which is a connection to Azure AD), MSAL.NET, proposes a clean separation between public client applications, and confidential client applications:

  • Confidential client applications are applications which run on servers (Web Apps, Web API, or even service/daemon applications). They are considered difficult to access, and therefore capable of keeping an application secret. Confidential clients are able to hold configuration time secrets. Each instance of the client has a distinct configuration (including clientId and secret). These values are difficult for end users to extract. A web app is the most common confidential client. The clientId is exposed through the web browser, but the secret is passed only in the back channel and never directly exposed.

    Confidential client apps:

    image image image

  • On the contrary public client applications are applications which run on devices (phones for instance) or desktop machines. They are not trusted to safely keep application secrets, and therefore access Web APIs in the name of the user only (they only support public client flows). Public clients are unable to hold configuration time secrets, and as a result have no client secret.

    Public client applications:

    image image image

MSAL.NET defines IPublicClientApplication interfaces for public client applications and IConfidentialClientApplication for confidential client applications, as well as a base interface IClientApplicationBase for the contract common to both types of applications.

image

View of the application interfaces.

High level commonalities and differences

It's immediately obvious that:

  • Both kinds of applications maintain a UserTokenCache and can acquire a token silently (in cases where the token is already in the token cache). Confidential client applications also have an AppTokenCache for tokens which are for the app itself (used exclusively by the AcquireTokenForClient method)
  • Both manage user accounts (get the accounts from the user token cache (GetAccountsAsync), get an account from its identifier (GetAccountAsync), or remove an account (RemoveAccountAsync))
  • Public client applications have four ways of acquiring a token (four flows), whereas confidential client applications have three (+ one method to compute the URL of the identity provider authorize endpoint). For more details see Scenarios and Acquiring tokens

If you used ADAL in the past, you might notice that, contrary to ADAL.NET's Authentication context, in MSAL.NET the clientID (also named applicationID or appId) is passed once at the construction of the Application, and no longer needs to be repeated when acquiring a token. This is the case both for a public and a confidential client applications. Constructors of confidential client applications are also passed client credentials: the secret they share with the identity provider.

Instantiating an Application

Pre-requisites

Before instantiating your app, you'll need to register it. You will therefore know:

  • Its clientID (a string representing a GUID)
  • The identity provider URL (named the instance) and the sign-in audience for your application. These two parameters are collectively known as the authority.
  • Possibly the TenantID in the case you are writing a line of business application (just for your organization, also named single-tenant application)
  • In case it's a confidential client app, its application secret (clientSecret string) or certificate (of type X509Certificate2)
  • For web apps, and sometimes for public client apps (in particular when your app needs to use a broker), you'll have also set the redirectUri where the identity provider will contact back your application with the security tokens.

Using the builders to instantiate an app

With MSAL.NET, the recommended way to instantiate an application is by using the application builders: PublicClientApplicationBuilder and ConfidentialClientApplicationBuilder. They offer a powerful mechanism to configure the application either from the code, or from a configuration file, or even by mixing both approaches.

Instantiating a public client application from code

The following code instantiates a Public client application, signing-in users in the Microsoft Azure public cloud, with their work and school accounts, or their personal Microsoft accounts.

IPublicClientApplication app = PublicClientApplicationBuilder.Create(clientId)
    .Build();

Instantiating a confidential client application from code

In the same way, the following code instantiates a confidential application (a Web app located at https://myapp.azurewebsites.net) handling tokens from users in the Microsoft Azure public cloud, with their work and school accounts, or their personal Microsoft accounts. The application is identified with the identity provider by sharing a client secret:

string redirectUri = "https://myapp.azurewebsites.net";
IConfidentialClientApplication app = ConfidentialClientApplicationBuilder.Create(clientId)
    .WithClientSecret(clientSecret)
    .WithRedirectUri(redirectUri )
    .Build();

As you might know, in production, rather than using a client secret, you might want to share with Azure AD a certificate. The code would then be the following:

IConfidentialClientApplication app = ConfidentialClientApplicationBuilder.Create(clientId)
    .WithCertificate(certificate)
    .WithRedirectUri(redirectUri )
    .Build();

Instantiating a public client application from configuration options

The following code instantiates a Public client application from a configuration object, which could be filled-in programmatically or read from a configuration file

PublicClientApplicationOptions options = GetOptions(); // your own method
IPublicClientApplication app = PublicClientApplicationBuilder.CreateWithApplicationOptions(options)
    .Build();

For a full example see Sample code: Instantiation of a public client application with configuration Options below

Instantiating a confidential client application from configuration options

The same kind of pattern applies to confidential client applications. You can also add other parameters using .WithXXX modifiers (here a certificate).

ConfidentialClientApplicationOptions options = GetOptions(); // your own method
IConfidentialClientApplication app = ConfidentialClientApplicationBuilder.CreateWithApplicationOptions(options)
    .WithCertificate(certificate)
    .Build();

Possible options

The following class diagram details the application options. These options can all be present in a configuration file.

image

The application options can be grouped into:

  • registration options, including:
    • Authority: Identity provider instance and sign-in audience for the application, possibly the tenant ID
    • redirect URI
    • client secret for a confidential client application (which you've seen already)
  • Logging options (log level, control of the PII, name of the component using the library)

Authority: Instance + audience

The Authority can be:

  • an Azure Active directory Cloud authority
  • an Azure AD B2C authority. See B2C specifics
  • an ADFS authority (coming soon. See ADFS support

image

Azure Active directory Cloud authorities have two parts:

  • the identity provider instance
  • the sign-in audience for the application Both can be concatenated and provided as the authority URL. In versions of MSAL.NET prior to MSAL 3.x, you had to compose the authority yourself depending on the cloud you wanted to target, and the sign-in audience.

Cloud Instance

The instance is used to specify if your application is signing users from the Microsoft Azure public cloud, or from national or sovereign clouds. It can be specified:

  • either using the AzureCloudInstance enumeration. This is the simplest

  • or by passing the URL to the sovereign cloud instance as the Instance member, if you know it.

If both Instance and AzureCloudInstance are specified, MSAL.NET will throw an explicit exception.

If you don't specify an instance, your app will target the AzureCloudInstance.AzurePublic, that is the instance of URL https://login.onmicrosoftonline.com

Application audience

It depends on your business needs

The sign-in audience depends on the business needs for your application:

  • If you are a line of business (LOB) developer, you'll probably produce a single tenant application, which will be used only in your organization. In that case, you need to specify what this organization is, either by its tenant ID (ID of your Azure Active Directory), or a domain name associated with this Azure Active Directory.
  • If you are an ISV, you might want to sign-in users with their work and school accounts in any organization, or some organizations (multi-tenant app), but you might also want to have users sign-in with their Microsoft personal account.
How to specify the audience in your code/configuration

You'll specify the audience by using:

  • either the AadAuthorityAudience enumeration. Again, this is the simplest.

  • or the TenantId, which can be:

    • a GUID, (that's the ID of your Azure Active Directory), for single tenant applications
    • a domain name associated with your Azure Active Directory (still in the case of single tenant application)
  • You can still use, as a TenantId one of the placeholders in place of the AadAuthorityAudience enumeration:

    • organizations for a multi tenant application
    • consumers to sign-in users only with their personal accounts
    • common to sign-in users with their work and school account or Microsoft personal account

If you specify both AadAuthorityAudience and TenantId, MSAL.NET will throw a meaningful exception.

If you don't specify an audience your app will target the AadAuthorityAudience.AzureAdAndPersonalMicrosoftAccount (that is common).

Implications of signing-in guest users on /common endpoint

Please note that if you sign-in guest users at the /common endpoint, they will be directed to their home tenant for signing-in. So, if your multi-tenant app cares about applying tenant specific conditional access policies, group assignments or app roles to be applied to the guest users, the app should sign-in the guest user on the tenanted endpoint (https://login.microsoftonline.com/{tenantId}) instead of the /common endpoint.

Effective audience

Note that the effective audience for your application will be the minimum (if there is an intersecton) of the audience you set in your application and the audience specified in the application registration. Indeed, the application registration experience (App Registration Preview) lets you specify the audience (supported account types) for the application. See Quickstart: Register an application with the Microsoft identity platform (Preview) for more details about it.

Note that today, the only way to get an application signing-in users with only Microsoft personal accounts is to set:

  • The app registration audience to "Work and school accounts and personal accounts" and,
  • The audience in your code / configuration to AadAuthorityAudience.PersonalMicrosoftAccount (or TenantID ="consumers")

Redirect URI

The redirect URI is the URI where the identity provider will send the security tokens back.

Redirect URI for public client applications

For public client application developers using MSAL.NET:

  • You don't need to pass the RedirectUri as it's automatically computed by MSAL.NET. This RedirectUri is set to the following values depending on the platform:

  • https://login.microsoftonline.com/common/oauth2/nativeclient for all the Windows platforms

  • msal{ClientId}://auth for Xamarin Android and iOS

However, the redirect URI needs to be configured in the App Registration Preview

image

It is possible to override the Redirect Uri using the RedirectUri property, for instance if you use brokers. Here are some examples of redirect URIs in that case:

  • RedirectUriOnAndroid = "msauth-5a434691-ccb2-4fd1-b97b-b64bcfbc03fc://com.microsoft.identity.client.sample";
  • RedirectUriOnIos = $"msauth.{Bundle.ID}://auth";

For details see Android specifics and iOS specifics

Redirect URI for confidential client applications
Web Apps
  • For web apps, the redirect URI (or Reply URI), is the URI at which Azure AD will contact back the application with the token. This can be the URL of the Web application / Web API if the confidential is one of these. Tip: This redirect URI needs to be registered in the app registration. This is especially important when you deploy an application that you have initially tested locally; you then need to add the reply URL of the deployed application in the application registration portal.
Daemon apps
  • For a daemon apps you don't need to specify a redirectURI.

Logging

The other options enable logging and troubleshooting. See the Logging page for more details on how to use them.

Reference

See the reference documentation for ApplicationOptions and ConfidentialClientApplicationOptions for more details about each property of these classes.

Builder modifiers

You've seen, in the code snippets using builders, that we have applied a number of .With methods (.WithCertificate, .WithRedirectUri). The following picture shows all the .With methods that can be applied on the application builders:

image

Modifiers common to public and confidential client applications

The modifiers you can set on a public client or confidential client application builder are:

Parameter Description
.WithAuthority() 7 overrides Sets the application default authority to an Azure AD authority, with the possibility of choosing the Azure Cloud, the audience, the tenant (tenant ID or domain name), or providing directly the authority URI.
.WithAdfsAuthority(string) Sets the application default authority to be an ADFS authority
.WithB2CAuthority(string) Sets the application default authority to be an Azure AD B2C authority.
.WithClientId(string) overrides the clientID
.WithComponent(string) Sets the name of the library using MSAL.NET (for telemetry reasons). See Telemetry
.WithDebugLoggingCallback() If called, the application will call Debug.Write simply enabling debugging traces. See Logging
.WithExtraQueryParameters(IDictionary<string,string> eqp) Set the application level extra query parameters that will be sent in all authentication request. This is overridable at each token acquisition method level (with the same .WithExtraQueryParameters pattern).
.WithHttpClientFactory(IMsalHttpClientFactory httpClientFactory) Enables advanced scenarios such as configuring for an HTTP proxy, or to force MSAL to use a particular HttpClient (for instance in ASP.NET Core web apps/APIs)
.WithLogging() If called, the application will call a callback with debugging traces. See Logging
.WithRedirectUri(string redirectUri) Overrides the default redirect Uri. In the case of public client applications, this will be useful for scenarios involving the broker
.WithTelemetry(TelemetryCallback telemetryCallback) Sets the delegate used to send telemetry.
.WithTenantId(string tenantId) Overrides the tenant ID, or the tenant description

Modifiers specific to Xamarin.iOS applications

The modifiers you can set on a public client application builder on Xamarin.iOS are:

Parameter Description
.WithIosKeychainSecurityGroup() Xamarin.iOS only: Sets the iOS key chain security group (for the cache persistence)

Modifiers specific to confidential client applications

The modifiers you can set on a confidential client application builder are:

Parameter Description
.WithCertificate(X509Certificate2 certificate) Sets the certificate identifying the application with Azure AD
.WithClientSecret(string clientSecret) Sets the client secret (app password) identifying the application with Azure AD

These modifiers are mutually exclusive. If you provide both, MSAL will throw a meaningful exception.

Example of usage of modifiers

Let's assume that your application is a line of business application which is only for your organization, then you can write:

IPublicClientApplication app;
app = PublicClientApplicationBuilder.Create(clientId)
        .WithAadAuthority(AzureCloudInstance.AzurePublic, tenantId)
        .Build();

Where it becomes interesting is that programming for national clouds has now simplified. If you want your application to be a multi-tenant application in a national or sovereign cloud, you could write for instance, write:

IPublicClientApplication app;
app = PublicClientApplicationBuilder.Create(clientId)
        .WithAadAuthority(AzureCloudInstance.AzureUsGovernment, AadAuthorityAudience.AzureAdMultipleOrgs)
        .Build();

There is also an override for ADFS (Support for ADFS 2019 is coming soon)

IPublicClientApplication app;
app = PublicClientApplicationBuilder.Create(clientId)
        .WithAdfsAuthority("https://consoso.com/adfs")
        .Build();

Finally, if you are an Azure AD B2C developer, you can specify your tenant like this:

IPublicClientApplication app;
app = PublicClientApplicationBuilder.Create(clientId)
        .WithB2CAuthority("https://fabrikamb2c.b2clogin.com/tfp/{tenant}/{PolicySignInSignUp}")
        .Build();

Sample code: Instantiation of a public client application with configuration Options

Imagine a .NET Core console application which has the following appsettings.json configuration file:

{
  "Authentication": {
    "AzureCloudInstance": "AzurePublic",
    "AadAuthorityAudience": "AzureAdMultipleOrgs",
    "ClientId": "ebe2ab4d-12b3-4446-8480-5c3828d04c50"
  },

  "WebAPI": {
    "MicrosoftGraphBaseEndpoint": "https://graph.microsoft.com"
  }
}

You have very little code to read this file using the .NET provided configuration framework;

    public class SampleConfiguration
    {
        /// <summary>
        /// Authentication options
        /// </summary>
        public PublicClientApplicationOptions PublicClientApplicationOptions { get; set; }

        /// <summary>
        /// Base URL for Microsoft Graph (it varies depending on whether the application is ran
        /// in Microsoft Azure public clouds or national / sovereign clouds
        /// </summary>
        public string MicrosoftGraphBaseEndpoint { get; set; }

        /// <summary>
        /// Reads the configuration from a json file
        /// </summary>
        /// <param name="path">Path to the configuration json file</param>
        /// <returns>SampleConfiguration as read from the json file</returns>
        public static SampleConfiguration ReadFromJsonFile(string path)
        {
            // .NET configuration
            IConfigurationRoot Configuration;
            var builder = new ConfigurationBuilder()
             .SetBasePath(Directory.GetCurrentDirectory())
            .AddJsonFile(path);
            Configuration = builder.Build();

            // Read the auth and graph endpoint config
            SampleConfiguration config = new SampleConfiguration()
            {
                PublicClientApplicationOptions = new PublicClientApplicationOptions()
            };
            Configuration.Bind("Authentication", config.PublicClientApplicationOptions);
            config.MicrosoftGraphBaseEndpoint = Configuration.GetValue<string>("WebAPI:MicrosoftGraphBaseEndpoint");
            return config;
        }
    }

Now, to create your application, you will just need to write the following code:

SampleConfiguration config = SampleConfiguration.ReadFromJsonFile("appsettings.json");
var app = PublicClientApplicationBuilder.CreateWithApplicationOptions(config.PublicClientApplicationOptions)
           .Build();

and of course, you understand that before the .Build(), you can override your configuration with calls to .WithXXX as seen previously.

Sample code - case of ASP.NET Core applications

The case of an ASP.NET Core application is similar, except that this time you will use ConfidentialClientApplicationOptions, which holds client secrets, in addition to the options you've seen so far, and ASP.NET Core will probably handle the configuration for you and provide you directly with the data structure

In designing MSAL.NET 3.x, we made sure that the name of the properties of the Options in MSAL match the name of the properties of the AzureADOptions in ASP.NET Core so that you don't need to write any glue code.

Configuring the application straight from the configuration file

ASP.NET Core applications propose to describe the application configuration in appsettings.json files like the following:

{
  "AzureAd": {
    "Instance": "https://login.microsoftonline.com/",
    "Domain": "[Enter the domain of your tenant, e.g. contoso.onmicrosoft.com]",
    "TenantId": "[Enter 'common', or 'organizations' or the Tenant Id (Obtained from the Azure portal. Select 'Endpoints' from the 'App registrations' blade and use the GUID in any of the URLs), e.g. da41245a5-11b3-996c-00a8-4d99re19f292]",
    "ClientId": "[Enter the Client Id (Application ID obtained from the Azure portal), e.g. ba74781c2-53c2-442a-97c2-3d60re42f403]",
    "CallbackPath": "/signin-oidc",
    "SignedOutCallbackPath ": "/signout-callback-oidc",

    "ClientSecret": "[Copy the client secret added to the app from the Azure portal]"
  },
  "Logging": {
    "LogLevel": {
      "Default": "Warning"
    }
  },
  "AllowedHosts": "*"
}

MSAL.NET, enables you to benefit from this configuration file and configure your Confidential client application.

Then in the class where you want to benefit from the configuration, you need to declare a ConfidentialClientApplicationOptions and bind the configuration read from whatever source (including the appconfig.json file) to the instance of

private ConfidentialClientApplicationOptions _applicationOptions;
_applicationOptions = new ConfidentialClientApplicationOptions();
configuration.Bind("AzureAD", _applicationOptions);

This enables the content of the "AzureAD" section of the appsettings.json to be bound to the corresponding properties of the ConfidentialClientApplicationOptions

From there, you can build a ConfidentialClientApplication

IConfidentialClientApplication app;
app = ConfidentialClientApplicationBuilder.CreateWithApplicationOptions(_applicationOptions)
        .Build();

Adding runtime configuration

Now, in a Confidential client application, you usually have a cache per user, therefore you will need to get the cache associated with the user, and inform the application builder that you want to use it. In the same way, you might have a dynamically computed redirectUri. In this case the code is the following:

IConfidentialClientApplication app;
var request = httpContext.Request;
var currentUri = UriHelper.BuildAbsolute(request.Scheme, request.Host, request.PathBase, _azureAdOptions.CallbackPath ?? string.Empty);
app = ConfidentialClientApplicationBuilder.CreateWithApplicationOptions(_applicationOptions)
       .WithRedirectUri(currentUri)
       .Build();
TokenCache userTokenCache = _tokenCacheProvider.SerializeCache(app.UserTokenCache,httpContext, claimsPrincipal);

Invalid Client

This error indicates that a configuration issue has occurred. These can be addressed in the application registration portal. The second part of the error message gives details on this error.

Common Invalid Client Errors

Original exception: AADSTS7000218: The request body must contain the following parameter: 'client_assertion' or 'client_secret'. It means that you have not specifically enabled a public client application in the Azure portal and are attempting to do an authentication flow that is only available on public clients, such as Username/Password, Integrated Windows Auth, or Device code flow.

In the AAD Portal, in the App Registration for this client, you will see this in the Portal, under the Authentication blade,

Select "Yes" to enable a Public Client: image

Original exception: AADSTS700030: Invalid certificate - subject name in certificate is not authorized. SubjectNames/SubjectAlternativeNames (up to 10) in token certificate are: graph.contoso.net

This means that the subject name configured in the application does not match the certificate used.

Getting started with MSAL.NET

Acquiring tokens

Desktop/Mobile apps

Web Apps / Web APIs / daemon apps

Advanced topics

News

FAQ

Other resources

Clone this wiki locally