-
Notifications
You must be signed in to change notification settings - Fork 331
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:
-
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:
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.
View of the application interfaces.
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 anAppTokenCache
for tokens which are for the app itself (used exclusively by theAcquireTokenForClient
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.
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 typeX509Certificate2
) - 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.
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.
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();
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();
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
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();
The following class diagram details the application options. These options can all be present in a configuration file.
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)
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
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.
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
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.
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 theAadAuthorityAudience
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
).
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.
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
(orTenantID
="consumers")
The redirect URI is the URI where the identity provider will send the security tokens back.
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
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
- 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.
- For a daemon apps you don't need to specify a redirectURI.
The other options enable logging and troubleshooting. See the Logging page for more details on how to use them.
See the reference documentation for ApplicationOptions and ConfidentialClientApplicationOptions for more details about each property of these classes.
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:
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 |
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) |
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.
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();
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.
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.
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();
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);
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.
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:
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.
- Home
- Why use MSAL.NET
- Is MSAL.NET right for me
- Scenarios
- Register your app with AAD
- Client applications
- Acquiring tokens
- MSAL samples
- Known Issues
- AcquireTokenInteractive
- WAM - the Windows broker
- .NET Core
- Xamarin Docs
- UWP
- Custom Browser
- Applying an AAD B2C policy
- Integrated Windows Authentication for domain or AAD joined machines
- Username / Password
- Device Code Flow for devices without a Web browser
- ADFS support
- Acquiring a token for the app
- Acquiring a token on behalf of a user in Web APIs
- Acquiring a token by authorization code in Web Apps
- High Availability
- Token cache serialization
- Logging
- Exceptions in MSAL
- Provide your own Httpclient and proxy
- Extensibility Points
- Clearing the cache
- Client Credentials Multi-Tenant guidance
- Performance perspectives
- Differences between ADAL.NET and MSAL.NET Apps
- PowerShell support
- Testing apps that use MSAL
- Experimental Features
- Proof of Possession (PoP) tokens
- Using in Azure functions
- Extract info from WWW-Authenticate headers
- SPA Authorization Code