-
Notifications
You must be signed in to change notification settings - Fork 343
msal net 4
See Microsoft Authentication Library for .NET for updated documentation.
We are excited to announce that one month after MSAL.NET GA-ed, we are now releasing a first incremental update bringing features you've been asking for:
- ADFS 2019 support
- Asynchronous token cache serialization
- Interactive token acquisition on .NET Core (through the OS browser), including on Linux and Mac
- Fixes of bugs that you raised
- Moving directly from MSAL 2.x?
Unfortunately, the asynchronous token cache serialization introduced a breaking change, so we bumped-up the major version of MSAL. In practice, you should not be impacted at all, or very little (See what's the impact). 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). See changes in the interface to use to send telemetry.
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
Until MSAL.NET 3.1, when you wanted to customize token cache serialization, you had to provide synchronous methods. This meant 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);
...
}
For an example of usage of the async serialization see the code for the MSALAppSessionTokenCacheProvider
class in the ASP.NET Core Web app tutorial: the PR proposing the change is PR 107 of that sample.
In order to enable the async methods you need to use to subscribe to cache events, we have rewritten the non-async ones by calling the async ones. While doing that we splatted the responsibility of the ITokenCache
interface between
ITokenCache
which now contains the methods to subscribe to the cache serialization events, and a new interface ITokenCacheSerializer
which exposes the methods that you need to use in the cache serialization events, in order to serialize/deserialize the cache
public interface ITokenCacheSerializer
{
void DeserializeAdalV3(byte[] adalV3State);
void DeserializeMsalV2(byte[] msalV2State);
void DeserializeMsalV3(byte[] msalV3State, bool shouldClearExistingCache=false);
byte[] SerializeAdalV3();
byte[] SerializeMsalV2();
byte[] SerializeMsalV3();
}
And now, the TokenCache
member of the TokenCacheNotificationArgs
is of type ITokenCacheSerializer
, which means you can only use these methods from the events themselves where we are sure that the right synchronization will happen.
In practice, even if this is a binary breaking change, you should be able to build your code without changing anything provided you were only using the serialization and deserialization methods using the args.TokenCache instance provided in the token cache serialization events.
For instance the following sample did not require any change ms-identity-dotnet-desktop-msgraph
If, however, you had kept a reference on the ITokenCache
from app.UserTokenCache
or app.AppTokenCache
, and used this one in the serialization events, you will now get an explicit exception advising you to visit this article:
NotImplementedException: This is removed in MSAL.NET v4. Read more: https://aka.ms/msal-net-4x-cache-breaking-change
Microsoft.Identity.Client.TokenCache.DeserializeMsalV3(byte[] msalV3State, bool shouldClearExistingCache) in TokenCache.MigrationAid.cs, line 299
In that case what you need to do is use the args.TokenCache.SerializeMsalV3
and not the instance of token cache you kept (probably as a member variable, which you can now remove).
public static void AfterAccessNotification(TokenCacheNotificationArgs args)
{
// if the access operation resulted in a cache update
if (args.HasStateChanged)
{
lock (FileLock)
{
// reflect changes in the persistent store
File.WriteAllBytes(CacheFilePath,
ProtectedData.Protect(args.TokenCache.SerializeMsalV3(),
null,
DataProtectionScope.CurrentUser)
);
}
}
}
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 just 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!
- 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.
The code is almost the same as if you were writing .NET Framework code, except that:
- This is .NET Core
- You need to specify a RedirectUri set to "http://localhost" (or "http://localhost:port" if you registered a URL with a port)
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.
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 |
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;
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 arebool
,long
,int
, andstring
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();
This release also contains a number of bug fixes:
- In confidential client applications, MSAL.NET was not returning a URL in the
GetAuthorizationRequestUrl()
flow. See MSAL.NET issues 1193 and 1184 - MSAL.NET now correctly handles the X509 cert on .NET Core. MSAL issue 1139
- MSAL.NET now resolves the
TeamID
in the Keychain Access Group for the default configuration. Keychain sharing groups should be prefixed with the TeamID. Now, if the developer does not explicitly set the keychain access group through theWithIosKeychainSecurityGroup
api, MSAL.NET will use the default "com.microsoft.adalcache", appended with the TeamID. Previously the TeamID was not included.
If you are moving directly to MSAL.NET 4.0.0 from MSAL 2.x, nice reminder that you can learn about the breaking changes from 2.x to 3.x in in MSAL 3.x released
- 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
- Maui Docs
- 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