Skip to content

msal net 4

Jean-Marc Prieur edited this page May 31, 2019 · 14 revisions

MSAL.NET 4.0 will be released

Note

This is a preview of the release documentation for MSAL 4.0 it's not completely up to date and might still change

We are excited to announce that three weeks after MSAL.NET GA-ed, we are now releasing a first incremental update bringing features you've been asking for:

Unfortunately, the asynchronous token cache serialization introduced a breaking change, so we'll have to bump-up the major version of MSAL. In practice, there is little code so you should not be impacted at all, or too much. As we were taking a breaking change we've decided to take other breaking changes as well on Telemetry (which, we are pretty sure won't impact you)

ADFS 2019

You can now connect directly to ADFS 2019. This is especially important if you intend to write an app working with Azure Stack

To connect directly to ADFS, you'll use the existing WithAdfsAuthority Builder method:

var app = PublicClientApplicationBuilder.Create(clientId)
                                        .WithAdfsAuthority("https://somesite.contoso.com/adfs/")
                                        .Build();

You can then use the AcquireTokenXX methods as usual.

For more details see ADFS support

Asynchronous token cache serialization

Until MSAL.NET 3.1, when you wanted to customize token cache serialization, you had to provide synchronous methods. This means that the whole process was blocked when storage was happening, which could be damageable for performance, for instance of Web Apps or Web APIs using a SQL token cache. Indeed, it's a frequent use case to persist the Token Cache in a distributed manner. Several among you have asked to have BeforeAccess/AfterAccess with an async signature, since most of the time the implementation is doing some IO, which can take time.

The ITokenCache interface now contains three new methods to set asynchronous callbacks: SetAfterAccessAsync, SetBeforeAccessAsync, SetBeforeWriteAsync

public interface ITokenCache 
{
 ...       
 void SetAfterAccessAsync(Func<TokenCacheNotificationArgs, Task> afterAccess);
 void SetBeforeAccessAsync(Func<TokenCacheNotificationArgs, Task> beforeAccess);
 void SetBeforeWriteAsync(Func<TokenCacheNotificationArgs, Task> beforeWrite);
 ...
}

.NET Core now support interactive authentication

User experience

Given that .NET Core does not provide a Web browser control, until MSAL.NET 3.1, the interactive token acquisition was not supported. From this version, you can now use AcquireTokenInteractive with MSAL.NET. The experience for the end user will be the following

  • The default browser for the operating system will be launched (a new tab in an existing browser can be opened)
  • The user will then go through the sign-in and consent (if needed) in this browser/tab
  • when the interaction is done, the browser will display will just display that the authentication is successful. The sentence is currently Authentication complete. You can return to the application. Feel free to close this browser tab. It's not yet customizable (See below).

Note that the experience is what it is, and in particular, when the interactive authentication has happened, the end user sees that the page which was displayed is localhost:someport?code=XYZTqlsdkfslkhskgh The code is the authorization code. Even if the code was copied, it would not be usable as MSAL.NET uses the PKCE to protect the authorization code flow.

Even if this experience is not as neat as with embedded web view used on the .NET Framework platform, it has the advantage of enabling SSO with Web applications on the platform, which is a great plus. Chances are that your users won't even need to sign-in!

How to enable it?

App registration

  • You'll need to register "http://localhost" as a Public client (mobile & desktop) redirect URI for your application. In that case, Azure AD accepts any http://localhost:port. This is used by MSAL which finds an empty port, serves an HTML page and listen to this port to get the authentication code.
  • alternatively you can specify a localhost URL with a port if you don't want to let MSAL.NET choose a port.

.NET Code

The code is almost the same as if you were writing .NET Framework code, except that:

Here is the complete code for a .NET Core console application using this feature:

using Microsoft.Identity.Client;
using System;
using System.Linq;
using System.Threading.Tasks;

namespace ConsoleApp1
{
 class Program
 {
  static async Task Main(string[] args)
  {
   string[] scopes = new[] { "user.read" };
   string clientId = "e9f70606-879c-4f0b-87cd-2754fccc4f44";
   var app = PublicClientApplicationBuilder.Create(clientId)
                                           .WithRedirectUri("http://localhost")
                                           .Build();
   var accounts = await app.GetAccountsAsync();
   AuthenticationResult result;
   try
   {
    result = await app.AcquireTokenSilent(scopes, accounts.FirstOrDefault())
                      .ExecuteAsync();
   }
   catch (MsalUiRequiredException)
   {
    result = await app.AcquireTokenInteractive(scopes)
                      .ExecuteAsync();
   }

   Console.WriteLine($"Hello {result.Account.Username}");
  }
 }
}

This kind of code used to throw a PlatformNotSupportedException before MSAL 3.1, with an explicit message telling you that on .NET Core interactive authentication was not supported, and advising to use Device Code Flow. This now works with the user experience described above.

Error handling

As a support for this new feature, MSALError gets four additional strings used in MsalClientExceptions when things don't work as expected.

public static class MsalError 
{
 ...
 public const string InvalidAuthorizationUri = "invalid_authorization_uri";
 public const string LinuxXdgOpen = "linux_xdg_open_failed";
 public const string LoopbackRedirectUri = "loopback_redirect_uri";
 public const string LoopbackResponseUriMisatch = "loopback_response_uri_mismatch";
 ...
}
Error Description
LoopbackRedirectUri This error happens when you forgot to add the .WithRedirectUri("http://localhost") modifier when building the application. An MsalClientException is then thrown by MSAL.NET with the following error message 'Only loopback redirect uri is supported, but urn:ietf:wg:oauth:2.0:oob was found. Configure http://localhost or http://localhost:port both during app registration and when you create the PublicClientApplication object. See https://aka.ms/msal-net-os-browser for details'.
LinuxXdgOpen On Linux, this MsalClientException occurs when MSAL.NET is unable to open a web page specified at the redirect URI or selected port) using xdg-open. The inner exception provides more details. Possible causes for this error are that xdg-open is not installed or it cannot find a way to open an url. As a first mitigation, the end user needs to make sure they can open a web page by invoking from a terminal: xdg-open https://www.bing.com
InvalidAuthorizationUri and LoopbackResponseUriMisatch An MSALClientException with one of these ErrorCode is thrown when the response from the Microsoft identity platform v2.0 authorize endpoint is not what MSAL.NET expected, and therefore MSAL.NET cannot extract the authorization code. The best is to look at the inner exception for details

More to come

This is a start. In a next version to come we'll add additional customization so that you can provide URLs to have your browser navigate in case of success and failure;

Telemetry

[Breaking change] Replacing TelemetryCallback by TelemetryConfig

Until MSAL.NET 3.0.8, you could subscribe to telemetry by adding a telemetry callback .WithTelemetry(), and then sending to your telemetry pipeline of choice a list of events (which themselves were dictionaries of name, values)

From MSAL.NET 4.0, if you want to add telemetry to your application, you need to create a class implementing ITelemetryConfig. MSAL.NET provides such a class (TraceTelemetryConfig) which does not send telemetry anywhere, but uses System.Trace.TraceInformation to trace the telemetry events. You could take it from there and add trace listeners to send telemetry.

public interface ITelemetryConfig
{
 TelemetryAudienceType AudienceType { get; }
 Action<ITelemetryEventPayload> DispatchAction { get; }
 string SessionId { get; }
}

public class TraceTelemetryConfig : ITelemetryConfig 
{
 public TraceTelemetryConfig();
 public IEnumerable<string> AllowedScopes { get; }
 public TelemetryAudienceType AudienceType { get; }
 public Action<ITelemetryEventPayload> DispatchAction { get; }
 public string SessionId { get; }
}

You initialize this config object with:

  • the audience (pre-production or production)
  • a callback that will process the ITelemetryEventPayload which is a set of typed dictionary containing telemetry values by their name. The allowed types are bool, long, int, and string
public enum TelemetryAudienceType 
{
 PreProduction = 0,
 Production = 1,
}

public interface ITelemetryEventPayload 
{
 IReadOnlyDictionary<string, bool> BoolValues { get; }
 IReadOnlyDictionary<string, long> Int64Values { get; }
 IReadOnlyDictionary<string, int> IntValues { get; }
 string Name { get; }
 IReadOnlyDictionary<string, string> StringValues { get; }
 string ToJsonString();
}

Finally, you create you app by passing the telemetry config

var app = PublicClientApplication.Create(clientId)
                                 .WithTelemetry(telemetryConfig)
                                 .Build();

Getting started with MSAL.NET

Acquiring tokens

Web Apps / Web APIs / daemon apps

Desktop/Mobile apps

Advanced topics

FAQ

Other resources

Clone this wiki locally