Avvertimento
Azure Active Directory Authentication Library (ADAL) è stato deprecato. Anche se le app esistenti che usano ADAL continueranno a funzionare, Microsoft non rilascerà più le correzioni di sicurezza in ADAL. Usare il Libreria di Autenticazione Microsoft (MSAL) per evitare di mettere a rischio la sicurezza dell'app.
Questo articolo descrive come eseguire la migrazione di un'applicazione client pubblica da Azure Active Directory Authentication Library per .NET (ADAL.NET) a Libreria di Autenticazione Microsoft per .NET (MSAL.NET). Le applicazioni client pubbliche sono applicazioni desktop e per dispositivi mobili, inclusi progetti nativi (Win32) e macchine virtuali Windows che chiamano un altro servizio o API per conto di un utente Microsoft Entra ID. Per altre informazioni sulle applicazioni client pubbliche, vedere Flussi di autenticazione e scenari di applicazione.
Passaggi per la migrazione
Trovare il codice usando ADAL.NET nell'app.
Il codice che utilizza ADAL in un'applicazione client pubblica istanzia AuthenticationContext e chiama un override di AcquireTokenAsync con i parametri seguenti:
- Una
resourceId stringa. Questa variabile è l'URI ID app dell'API Web che si vuole chiamare.
- Un
clientId che è l'identificatore della tua applicazione, noto anche come App ID.
Dopo aver rilevato che sono presenti applicazioni che usano ADAL.NET, installare il pacchetto NuGet MSAL.NET (Microsoft.Identity.Client) e aggiornare i riferimenti alla libreria di progetti. Per altre informazioni, vedere Introduzione a NuGet.
Aggiornare il codice in base allo scenario dell'applicazione client pubblica. Alcuni passaggi sono comuni e si applicano a tutti gli scenari client pubblici. Altri passaggi sono univoci per ogni scenario.
Gli scenari client pubblici sono:
-
Gestione account Web, l'approccio di autenticazione preferito per le applicazioni Windows, usando il componente broker di autenticazione.
-
Autenticazione interattiva, in cui l'utente visualizza un'interfaccia basata sul Web per completare il processo di accesso.
-
Autenticazione integrata di Windows (IWA), in cui l'utente accede usando la stessa identità con cui ha effettuato l'accesso a Windows in un dominio Windows (per dispositivi aggiunti a un dominio o a Microsoft Entra ID).
-
Nome utente/password, in cui si verifica l'accesso fornendo una credenziale nome utente/password.
Microsoft non consiglia il flusso di nome utente e password perché l'applicazione chiederà direttamente a un utente la password, ovvero un modello non sicuro.
-
Flusso di codice del dispositivo, in cui un dispositivo con funzionalità di esperienza utente limitate mostra al consumer un codice del dispositivo per completare il flusso di autenticazione in un dispositivo alternativo.
Gli scenari interattivi sono la posizione in cui l'applicazione client pubblica mostra un'interfaccia utente di accesso ospitata in un browser e l'utente deve accedere in modo interattivo.
Scoprire se il codice usa scenari interattivi
Il codice ADAL per l'app, in un'applicazione client pubblica che usa l'autenticazione interattiva, istanzia AuthenticationContext e include una chiamata a AcquireTokenAsync con i parametri seguenti.
- Un
clientId che è un GUID che rappresenta la registrazione della tua applicazione.
- Un
resourceUrl che indica la risorsa per cui si richiede il token.
- URI che rappresenta l'URL di risposta.
- Un oggetto
PlatformParameters.
Aggiornare il codice per scenari interattivi
I passaggi seguenti per l'aggiornamento del codice si applicano a tutti gli scenari client riservati:
- Aggiungi il namespace MSAL.NET nel codice sorgente:
using Microsoft.Identity.Client;.
- Anziché creare un'istanza di
AuthenticationContext, usare PublicClientApplicationBuilder.Create per creare un'istanza di IPublicClientApplication.
- Anziché la stringa
resourceId, MSAL.NET usa gli scope. Poiché le applicazioni che usano ADAL.NET sono pre-autorizzate, è sempre possibile usare gli ambiti seguenti: new string[] { $"{resourceId}/.default" }.
- Sostituire la chiamata a
AuthenticationContext.AcquireTokenAsync con una chiamata a IPublicClientApplication.AcquireTokenXXX, dove XXX dipende dallo scenario.
In questo caso, sostituiamo la chiamata a AuthenticationContext.AcquireTokenAsync con una chiamata a IPublicClientApplication.AcquireTokenInteractive.
Ecco un confronto tra CODICE ADAL.NET e MSAL.NET per scenari interattivi:
var ac = new AuthenticationContext("https://login.microsoftonline.com/<tenantId>");
AuthenticationResult result;
result = await ac.AcquireTokenAsync("<clientId>",
"https://resourceUrl",
new Uri("https://ClientReplyUrl"),
new PlatformParameters(PromptBehavior.Auto));
var scopes = new[] { "User.Read" };
BrokerOptions options = new BrokerOptions(BrokerOptions.OperatingSystems.Windows);
options.Title = "My Awesome Application";
IPublicClientApplication app =
PublicClientApplicationBuilder.Create("YOUR_CLIENT_ID")
.WithDefaultRedirectUri()
.WithParentActivityOrWindow(GetConsoleOrTerminalWindow)
.WithBroker(options)
.Build();
AuthenticationResult result = null;
// Try to use the previously signed-in account from the cache
IEnumerable<IAccount> accounts = await app.GetAccountsAsync();
IAccount existingAccount = accounts.FirstOrDefault();
try
{
if (existingAccount != null)
{
result = await app.AcquireTokenSilent(scopes, existingAccount).ExecuteAsync();
}
// Next, try to sign in silently with the account that the user is signed into Windows
else
{
result = await app.AcquireTokenSilent(scopes, PublicClientApplication.OperatingSystemAccount)
.ExecuteAsync();
}
}
// Can't get a token silently, go interactive
catch (MsalUiRequiredException ex)
{
result = await app.AcquireTokenInteractive(scopes).ExecuteAsync();
}
Il codice MSAL illustrato in precedenza usa WAM (Web Account Manager) che è l'approccio consigliato per autenticare gli utenti in Windows. Se si vuole usare l'autenticazione interattiva senza WAM, vedere Autenticazione interattiva.
Per altri requisiti per configurare l'applicazione per l'uso di WAM, vedere il documento Using MSAL.NET with Web Account Manager (WAM).
L'autenticazione integrata di Windows consente all'applicazione client pubblica di consentire a un utente di accedere utilizzando la stessa identità con cui ha effettuato l'accesso a un dominio Windows (per dispositivi aggiunti a un dominio o aggiunti a Microsoft Entra).
Verificare se il codice usa l'autenticazione integrata Windows
Il codice ADAL per l'app usa scenari di autenticazione di Windows integrati se contiene una chiamata a AcquireTokenAsync disponibile come metodo di estensione della AuthenticationContextIntegratedAuthExtensions classe, con i parametri seguenti:
- Un oggetto
resource che rappresenta la risorsa per cui si sta richiedendo il token
-
clientId che è un GUID che rappresenta la registrazione dell'applicazione
- Oggetto
UserCredential che rappresenta l'utente per cui si sta tentando di richiedere il token.
Aggiornare il codice per gli scenari di autenticazione integrata Windows
I passaggi seguenti per l'aggiornamento del codice si applicano a tutti gli scenari client riservati:
- Aggiungi il namespace MSAL.NET nel codice sorgente:
using Microsoft.Identity.Client;.
- Anziché creare un'istanza di
AuthenticationContext, usare PublicClientApplicationBuilder.Create per creare un'istanza di IPublicClientApplication.
- Anziché la stringa
resourceId, MSAL.NET usa gli scope. Poiché le applicazioni che usano ADAL.NET sono pre-autorizzate, è sempre possibile usare gli ambiti seguenti: new string[] { $"{resourceId}/.default" }.
- Sostituire la chiamata a
AuthenticationContext.AcquireTokenAsync con una chiamata a IPublicClientApplication.AcquireTokenXXX, dove XXX dipende dallo scenario.
In questo caso, sostituiamo la chiamata a AuthenticationContext.AcquireTokenAsync con una chiamata a IPublicClientApplication.AcquireTokenByIntegratedWindowsAuth.
Di seguito è riportato un confronto tra codice ADAL.NET e MSAL.NET per scenari di autenticazione Windows integrati:
var ac = new AuthenticationContext("https://login.microsoftonline.com/<tenantId>");
AuthenticationResult result;
result = await context.AcquireTokenAsync(resource, clientId,
new UserCredential("john@contoso.com"));
string authority = "https://login.microsoftonline.com/contoso.com";
string[] scopes = new string[] { "user.read" };
IPublicClientApplication app = PublicClientApplicationBuilder
.Create(clientId)
.WithAuthority(authority)
.Build();
var accounts = await app.GetAccountsAsync();
AuthenticationResult result = null;
if (accounts.Any())
{
result = await app.AcquireTokenSilent(scopes, accounts.FirstOrDefault())
.ExecuteAsync();
}
else
{
try
{
result = await app.AcquireTokenByIntegratedWindowsAuth(scopes)
.ExecuteAsync(CancellationToken.None);
}
catch (MsalUiRequiredException ex)
{
// MsalUiRequiredException: AADSTS65001: The user or administrator has not consented to use the application
// with ID '{appId}' named '{appName}'.Send an interactive authorization request for this user and resource.
// you need to get user consent first. This can be done, if you are not using .NET Core (which does not have any Web UI)
// by doing (once only) an AcquireToken interactive.
// If you are using .NET core or don't want to do an AcquireTokenInteractive, you might want to suggest the user to navigate
// to a URL to consent: https://login.microsoftonline.com/common/oauth2/v2.0/authorize?client_id={clientId}&response_type=code&scope=user.read
// AADSTS50079: The user is required to use multi-factor authentication.
// There is no mitigation - if MFA is configured for your tenant and Azure AD decides to enforce it,
// you need to fallback to an interactive flows such as AcquireTokenInteractive or AcquireTokenByDeviceCode
}
catch (MsalServiceException ex)
{
// Kind of errors you could have (in ex.Message)
// MsalServiceException: AADSTS90010: The grant type is not supported over the /common or /consumers endpoints. Please use the /organizations or tenant-specific endpoint.
// you used common.
// Mitigation: as explained in the message from Azure AD, the authority needs to be tenanted or otherwise organizations
// MsalServiceException: AADSTS70002: The request body must contain the following parameter: 'client_secret or client_assertion'.
// Explanation: this can happen if your application was not registered as a public client application in Azure AD
// Mitigation: in the Azure portal, edit the manifest for your application and set the `allowPublicClient` to `true`
}
catch (MsalClientException ex)
{
// Error Code: unknown_user Message: Could not identify logged in user
// Explanation: the library was unable to query the current Windows logged-in user or this user is not AD or Azure AD
// joined (work-place joined users are not supported).
// Mitigation: Implement your own logic to fetch the username (e.g. john@contoso.com) and use the
// AcquireTokenByIntegratedWindowsAuth form that takes in the username
// Error Code: integrated_windows_auth_not_supported_managed_user
// Explanation: This method relies on a protocol exposed by Active Directory (AD). If a user was created in Azure
// Active Directory without AD backing ("managed" user), this method will fail. Users created in AD and backed by
// Azure AD ("federated" users) can benefit from this non-interactive method of authentication.
// Mitigation: Use interactive authentication
}
}
Console.WriteLine(result.Account.Username);
}
L'autenticazione con nome utente e password è la posizione in cui si verifica l'accesso fornendo una credenziale di nome utente e password direttamente all'applicazione.
Avvertimento
Microsoft non consiglia questo flusso perché presenta rischi di sicurezza non presenti in altri flussi. Se si usa il flusso di nome utente e password nell'ambiente di produzione, è consigliabile passare ad altre alternative più sicure descritte in questo articolo.
Verificare se il codice usa l'autenticazione con nome utente e password
Il codice ADAL per l'app usa scenari di autenticazione con password utente se contiene una chiamata a AcquireTokenAsync disponibile come metodo di estensione della AuthenticationContextIntegratedAuthExtensions classe, con i parametri seguenti:
- Un
resource che rappresenta la risorsa per cui si richiede il token.
- Un
clientId che è un GUID che rappresenta la registrazione della tua applicazione.
- Oggetto
UserPasswordCredential che contiene il nome utente e la password per l'utente per cui si sta tentando di richiedere il token.
Aggiornare il codice per gli scenari di autenticazione della password con nome utente
In questo caso, sostituiamo la chiamata a AuthenticationContext.AcquireTokenAsync con una chiamata a IPublicClientApplication.AcquireTokenByUsernamePassword.
Ecco un confronto tra ADAL.NET e codice MSAL.NET per gli scenari di password del nome utente:
I passaggi seguenti per l'aggiornamento del codice si applicano a tutti gli scenari client riservati:
- Aggiungi il namespace MSAL.NET nel codice sorgente:
using Microsoft.Identity.Client;.
- Anziché creare un'istanza di
AuthenticationContext, usare PublicClientApplicationBuilder.Create per creare un'istanza di IPublicClientApplication.
- Anziché la stringa
resourceId, MSAL.NET usa gli scope. Poiché le applicazioni che usano ADAL.NET sono pre-autorizzate, è sempre possibile usare gli ambiti seguenti: new string[] { $"{resourceId}/.default" }.
- Sostituire la chiamata a
AuthenticationContext.AcquireTokenAsync con una chiamata a IPublicClientApplication.AcquireTokenXXX, dove XXX dipende dallo scenario.
var ac = new AuthenticationContext("https://login.microsoftonline.com/<tenantId>");
AuthenticationResult result;
result = await context.AcquireTokenAsync(
resource, clientId,
new UserPasswordCredential("john@contoso.com", johnsPassword));
string authority = "https://login.microsoftonline.com/contoso.com";
string[] scopes = new string[] { "user.read" };
IPublicClientApplication app;
app = PublicClientApplicationBuilder.Create(clientId)
.WithAuthority(authority)
.Build();
var accounts = await app.GetAccountsAsync();
AuthenticationResult result = null;
if (accounts.Any())
{
result = await app.AcquireTokenSilent(scopes, accounts.FirstOrDefault())
.ExecuteAsync();
}
else
{
try
{
var securePassword = new SecureString();
foreach (char c in "dummy") // you should fetch the password
securePassword.AppendChar(c); // keystroke by keystroke
result = await app.AcquireTokenByUsernamePassword(scopes,
"joe@contoso.com",
securePassword)
.ExecuteAsync();
}
catch(MsalException)
{
// See details below
}
}
Console.WriteLine(result.Account.Username);
L'autenticazione tramite flusso del codice del dispositivo si verifica quando un dispositivo con interfaccia utente limitata mostra un codice del dispositivo per completare il flusso di autenticazione su un altro dispositivo.
Scopri se il tuo codice usa l'autenticazione con flusso del codice del dispositivo
Il codice ADAL per l'app usa scenari di flusso del codice del dispositivo se contiene una chiamata a AuthenticationContext.AcquireTokenByDeviceCodeAsync con i parametri seguenti:
- Un'istanza dell'oggetto
DeviceCodeResult, creata con il resourceID della risorsa per la quale si richiede un token, e un clientId, ovvero il GUID che rappresenta l'applicazione.
Aggiornare il codice per gli scenari del flusso di codice del dispositivo
I passaggi seguenti per l'aggiornamento del codice si applicano a tutti gli scenari client riservati:
- Aggiungi il namespace MSAL.NET nel codice sorgente:
using Microsoft.Identity.Client;.
- Anziché creare un'istanza di
AuthenticationContext, usare PublicClientApplicationBuilder.Create per creare un'istanza di IPublicClientApplication.
- Anziché la stringa
resourceId, MSAL.NET usa gli scope. Poiché le applicazioni che usano ADAL.NET sono pre-autorizzate, è sempre possibile usare gli ambiti seguenti: new string[] { $"{resourceId}/.default" }.
- Sostituire la chiamata a
AuthenticationContext.AcquireTokenAsync con una chiamata a IPublicClientApplication.AcquireTokenXXX, dove XXX dipende dallo scenario.
In questo caso, sostituiamo la chiamata a AuthenticationContext.AcquireTokenAsync con una chiamata a IPublicClientApplication.AcquireTokenWithDeviceCode.
Ecco un confronto tra ADAL.NET e codice MSAL.NET per gli scenari del flusso di codice del dispositivo:
static async Task<AuthenticationResult> GetTokenViaCode(AuthenticationContext ctx)
{
AuthenticationResult result = null;
try
{
result = await ac.AcquireTokenSilentAsync(resource, clientId);
}
catch (AdalException adalException)
{
if (adalException.ErrorCode == AdalError.FailedToAcquireTokenSilently
|| adalException.ErrorCode == AdalError.InteractionRequired)
{
try
{
DeviceCodeResult codeResult = await ctx.AcquireDeviceCodeAsync(resource, clientId);
Console.WriteLine("You need to sign in.");
Console.WriteLine("Message: " + codeResult.Message + "\n");
result = await ctx.AcquireTokenByDeviceCodeAsync(codeResult);
}
catch (Exception exc)
{
Console.WriteLine("Something went wrong.");
Console.WriteLine("Message: " + exc.Message + "\n");
}
}
return result;
}
private const string ClientId = "<client_guid>";
private const string Authority = "https://login.microsoftonline.com/contoso.com";
private readonly string[] scopes = new string[] { "user.read" };
static async Task<AuthenticationResult> GetATokenForGraph()
{
IPublicClientApplication pca = PublicClientApplicationBuilder
.Create(ClientId)
.WithAuthority(Authority)
.WithDefaultRedirectUri()
.Build();
var accounts = await pca.GetAccountsAsync();
// All AcquireToken* methods store the tokens in the cache, so check the cache first
try
{
return await pca.AcquireTokenSilent(scopes, accounts.FirstOrDefault())
.ExecuteAsync();
}
catch (MsalUiRequiredException ex)
{
// No token found in the cache or Azure AD insists that a form interactive auth is required (e.g. the tenant admin turned on MFA)
// If you want to provide a more complex user experience, check out ex.Classification
return await AcquireByDeviceCodeAsync(pca);
}
}
private static async Task<AuthenticationResult> AcquireByDeviceCodeAsync(IPublicClientApplication pca)
{
try
{
var result = await pca.AcquireTokenWithDeviceCode(scopes,
deviceCodeResult =>
{
// This will print the message on the console which tells the user where to go sign-in using
// a separate browser and the code to enter once they sign in.
// The AcquireTokenWithDeviceCode() method will poll the server after firing this
// device code callback to look for the successful login of the user via that browser.
// This background polling (whose interval and timeout data is also provided as fields in the
// deviceCodeCallback class) will occur until:
// * The user has successfully logged in via browser and entered the proper code
// * The timeout specified by the server for the lifetime of this code (typically ~15 minutes) has been reached
// * The developing application calls the Cancel() method on a CancellationToken sent into the method.
// If this occurs, an OperationCanceledException will be thrown (see catch below for more details).
Console.WriteLine(deviceCodeResult.Message);
return Task.FromResult(0);
}).ExecuteAsync();
Console.WriteLine(result.Account.Username);
return result;
}
// TODO: handle or throw all these exceptions depending on your app
catch (MsalServiceException ex)
{
// Kind of errors you could have (in ex.Message)
// AADSTS50059: No tenant-identifying information found in either the request or implied by any provided credentials.
// Mitigation: as explained in the message from Azure AD, the authoriy needs to be tenanted. you have probably created
// your public client application with the following authorities:
// https://login.microsoftonline.com/common or https://login.microsoftonline.com/organizations
// AADSTS90133: Device Code flow is not supported under /common or /consumers endpoint.
// Mitigation: as explained in the message from Azure AD, the authority needs to be tenanted
// AADSTS90002: Tenant <tenantId or domain you used in the authority> not found. This may happen if there are
// no active subscriptions for the tenant. Check with your subscription administrator.
// Mitigation: if you have an active subscription for the tenant this might be that you have a typo in the
// tenantId (GUID) or tenant domain name.
}
catch (OperationCanceledException ex)
{
// If you use a CancellationToken, and call the Cancel() method on it, then this *may* be triggered
// to indicate that the operation was cancelled.
// See https://learn.microsoft.com/dotnet/standard/threading/cancellation-in-managed-threads
// for more detailed information on how C# supports cancellation in managed threads.
}
catch (MsalClientException ex)
{
// Possible cause - verification code expired before contacting the server
// This exception will occur if the user does not manage to sign-in before a time out (15 mins) and the
// call to `AcquireTokenWithDeviceCode` is not cancelled in between
}
}
Vantaggi di MSAL
Informazioni sui vantaggi della libreria MSAL nell'articolo Eseguire la migrazione delle applicazioni al Libreria di Autenticazione Microsoft (MSAL).
Importanza della registrazione con MSAL
Tra le numerose funzionalità, Libreria di Autenticazione Microsoft (MSAL) offre funzionalità di registrazione predefinite affidabili. L'abilitazione della registrazione nelle applicazioni garantisce una linea di vista diretta su eventuali problemi di autenticazione e può entrambi diagnosticarli più facilmente per la propria applicazione e aiutare il team MSAL a risolvere rapidamente i potenziali problemi. È consigliabile abilitare la registrazione per le applicazioni quando vengono distribuite in qualsiasi scenario di produzione.
Troubleshooting
Le seguenti informazioni sulla risoluzione dei problemi fanno due presupposti:
- Il codice ADAL.NET funzionava.
- È stata eseguita la migrazione a MSAL mantenendo lo stesso ID client.
Se viene visualizzata un'eccezione con il messaggio seguente:
AADSTS90002: Tenant 'aaaabbbb-0000-cccc-1111-dddd2222eeee' not found. This may happen if there are no active
subscriptions for the tenant. Check to make sure you have the correct tenant ID. Check with your subscription
administrator.
È possibile risolvere l'eccezione attenendosi alla procedura seguente:
- Verificare di usare la versione più recente di MSAL.NET.
- Verificare che l'host dell'autorità impostato durante la compilazione dell'applicazione client riservata e l'host dell'autorità usato con ADAL siano simili. In particolare, è lo stesso cloud (Azure per enti pubblici, Microsoft Azure gestito da 21Vianet o Azure Germania)?
Passaggi successivi