Azure AD B2C: creare un'app desktop di WindowsAzure AD B2C: Build a Windows desktop app

Azure Active Directory (Azure AD) B2C consente di aggiungere funzionalità avanzate di gestione delle identità self-service all'app desktop in pochi brevi passaggi.By using Azure Active Directory (Azure AD) B2C, you can add powerful self-service identity management features to your desktop app in a few short steps. Questo articolo descrive come creare un'app Windows Presentation Foundation (WPF) .NET "To do list" con funzionalità di gestione dell'iscrizione, dell'accesso e del profilo utente.This article will show you how to create a .NET Windows Presentation Foundation (WPF) "to-do list" app that includes user sign-up, sign-in, and profile management. L'app includerà il supporto per l'iscrizione e l'accesso tramite un nome utente o un indirizzo di posta elettronica.The app will include support for sign-up and sign-in by using a user name or email. L'app includerà anche il supporto per l'iscrizione e l'accesso tramite account di social networking quali Facebook e Google.It will also include support for sign-up and sign-in by using social accounts such as Facebook and Google.

Ottenere una directory di Azure AD B2CGet an Azure AD B2C directory

Prima di poter usare Azure AD B2C, è necessario creare una directory, o tenant.Before you can use Azure AD B2C, you must create a directory, or tenant. Una directory è un contenitore per utenti, app, gruppi e così via.A directory is a container for all of your users, apps, groups, and more. Se non è già stato fatto, creare una directory B2C prima di proseguire con questa guida.If you don't have one already, create a B2C directory before you continue in this guide.

Creare un'applicazioneCreate an application

Successivamente, è necessario creare un'app nella directory B2C.Next, you need to create an app in your B2C directory. In questo modo Azure AD acquisisce le informazioni necessarie per comunicare in modo sicuro con l'app.This gives Azure AD information that it needs to securely communicate with your app. Per creare un'app, seguire questa procedura.To create an app, follow these instructions. Assicurarsi di:Be sure to:

  • Includere un client nativo nell'applicazione.Include a native client in the application.
  • Copiare l'URI di reindirizzamento urn:ietf:wg:oauth:2.0:oob.Copy the Redirect URI urn:ietf:wg:oauth:2.0:oob. Si tratta dell'URL predefinito per questo esempio di codice.It is the default URL for this code sample.
  • Copiare l' ID applicazione assegnato all'app.Copy the Application ID that is assigned to your app. Sarà necessario più avanti.You will need it later.

Importante

Non è possibile usare le applicazioni registrate nella scheda Applicazioni del portale di gestione di Azure per questa operazione.You cannot use applications registered in the Applications tab on the classic Azure Management Portal for this.

Creare i criteriCreate your policies

In Azure AD B2C ogni esperienza utente è definita da criterispecifici.In Azure AD B2C, every user experience is defined by a policy. Questo esempio di codice contiene tre esperienze di identità: iscrizione, accesso e modifica del profilo.This code sample contains three identity experiences: sign up, sign in, and edit profile. È necessario creare i criteri per ogni tipo, come descritto nell' articolo di riferimento sui criteri.You need to create a policy for each type, as described in the policy reference article. Durante la creazione dei tre criteri, assicurarsi di:When you create the three policies, be sure to:

  • Scegliere Iscrizione ID utente o Iscrizione posta elettronica nel pannello dei provider di identità.Choose either User ID sign-up or Email sign-up in the identity providers blade.
  • Scegliere Nome visualizzato e altri attributi nei criteri di iscrizione.Choose Display name and other sign-up attributes in your sign-up policy.
  • Scegliere le attestazioni Nome visualizzato e ID oggetto come attestazioni dell'applicazione in tutti i criteri.Choose Display name and Object ID claims as application claims for every policy. È consentito scegliere anche altre attestazioni.You can choose other claims as well.
  • Copiare il Nome di ogni criterio dopo averlo creato.Copy the Name of each policy after you create it. Dovrebbero mostrare il prefisso b2c_1_.It should have the prefix b2c_1_. I nomi dei criteri saranno necessari in un secondo momento.You'll need these policy names later.

Nota

In Azure AD B2C, il nome del criterio verrà preceduto da b2c_1_, come b2c_1_sign_up.In Azure AD B2C, your policy's name will be prefixed with b2c_1_, like b2c_1_sign_up. Si è liberi di utilizzare i criteri in tutte le app, sia client che server.You are free to use your policies across all of your apps, both client and server. Se sono stati creati in precedenza criteri in un altra procedura B2C, non è necessario procedere nuovamente.If you've previously created policies in another B2C walk-through, there is no need to do so again. È possibile riutilizzare i criteri creati in precedenza nel portale se corrispondono ai requisiti dell'applicazione.You may reuse the policies you've previously created in the portal if they match the requirements of the application.

Dopo aver creato i tre criteri, è possibile passare alla compilazione dell'app.After you have successfully created the three policies, you're ready to build your app.

Scaricare il codiceDownload the code

Il codice per questa esercitazione è disponibile in GitHub.The code for this tutorial is maintained on GitHub. Per compilare l'esempio passo dopo passo, è possibile scaricare un progetto bozza come file ZIP.To build the sample as you go, you can download a skeleton project as a .zip file. È anche possibile clonare la struttura:You can also clone the skeleton:

git clone --branch skeleton https://github.com/AzureADQuickStarts/B2C-NativeClient-DotNet.git

L'app completata è anche disponibile come file ZIP o nel ramo complete dello stesso repository.The completed app is also available as a .zip file or on the complete branch of the same repository.

Dopo aver scaricato il codice di esempio, aprire il file SLN di Visual Studio per iniziare.After you download the sample code, open the Visual Studio .sln file to get started. Il progetto TaskClient è l'applicazione desktop WPF con cui interagisce l'utente.The TaskClient project is the WPF desktop application that the user interacts with. Ai fini di questa esercitazione, chiama un'API Web dell'attività back-end, ospitata in Azure, che archivia l'elenco attività di ogni utente.For the purposes of this tutorial, it calls a back-end task web API, hosted in Azure, that stores each user's to-do list. Non è necessario compilare l'API Web, perché ne è già in esecuzione una.You do not need to build the web API, we already have it running for you.

Per informazioni sull'autenticazione sicura delle richieste da parte di un'API Web con Azure AD B2C, vedere l' articolo di introduzione all'API Web.To learn how a web API securely authenticates requests by using Azure AD B2C, check out the web API getting started article.

Eseguire i criteriExecute policies

L'app comunica con Azure AD B2C inviando messaggi di autenticazione che specificano i criteri che devono essere eseguiti come parte della richiesta HTTP.Your app communicates with Azure AD B2C by sending authentication messages that specify the policy they want to execute as part of the HTTP request. Per le applicazioni desktop .NET, è possibile usare l'anteprima di Microsoft Authentication Library (MSAL) per inviare messaggi di autenticazione OAuth 2.0, eseguire i criteri e ottenere token per chiamare le API Web.For .NET desktop applications, you can use the preview Microsoft Authentication Library (MSAL) to send OAuth 2.0 authentication messages, execute policies, and get tokens that call web APIs.

Installare MSALInstall MSAL

Aggiungere MSAL al progetto TaskClient usando la console di Gestione pacchetti di Visual Studio.Add MSAL to the TaskClient project by using the Visual Studio Package Manager Console.

PM> Install-Package Microsoft.Identity.Client -IncludePrerelease

Immettere le informazioni B2CEnter your B2C details

Aprire il file Globals.cs e sostituire i valori della proprietà con i propri.Open the file Globals.cs and replace each of the property values with your own. Questa classe viene usata in tutto il progetto TaskClient per fare riferimento ai valori usati comunemente.This class is used throughout TaskClient to reference commonly used values.

public static class Globals
{
    ...

    // TODO: Replace these five default with your own configuration values
    public static string tenant = "fabrikamb2c.onmicrosoft.com";
    public static string clientId = "90c0fe63-bcf2-44d5-8fb7-b8bbc0b29dc6";
    public static string signInPolicy = "b2c_1_sign_in";
    public static string signUpPolicy = "b2c_1_sign_up";
    public static string editProfilePolicy = "b2c_1_edit_profile";

    ...
}

Nota

Il nome del tenant B2C è il dominio immesso durante la creazione del tenant e viene visualizzato nel pannello della directory nel portale di Azure.Your B2C tenant's name is the domain that you entered during tenant creation, and is displayed on the directory blade in the Azure portal. In genere termina con il suffisso .onmicrosoft.com, ad esempio, contosob2c.onmicrosoft.com.It usually ends with the suffix .onmicrosoft.com, for instance, contosob2c.onmicrosoft.com.

Creare PublicClientApplicationCreate the PublicClientApplication

La classe primaria di MSAL è PublicClientApplication.The primary class of MSAL is PublicClientApplication. Questa classe rappresenta l'applicazione nel sistema Azure Active Directory B2C.This class represents your application in the Azure AD B2C system. All'avvio dell'app, creare un'istanza di PublicClientApplication in MainWindow.xaml.cs.When the app initalizes, create an instance of PublicClientApplication in MainWindow.xaml.cs. Questa istanza può essere usata in tutta la finestra.This can be used throughout the window.

protected async override void OnInitialized(EventArgs e)
{
    base.OnInitialized(e);

    pca = new PublicClientApplication(Globals.clientId)
    {
        // MSAL implements an in-memory cache by default.  Since we want tokens to persist when the user closes the app,
        // we've extended the MSAL TokenCache and created a simple FileCache in this app.
        UserTokenCache = new FileCache(),
    };

    ...

Avvia un flusso di registrazioneInitiate a sign-up flow

Quando l'utente sceglie di iscriversi, avviare un flusso di iscrizione che usa i criteri di iscrizione creati.When a user opts to signs up, you want to initiate a sign-up flow that uses the sign-up policy you created. Con MSAL è sufficiente chiamare pca.AcquireTokenAsync(...).By using MSAL, you just call pca.AcquireTokenAsync(...). I parametri passati a AcquireTokenAsync(...) determinano quale token si riceve, i criteri usati nella richiesta di autenticazione e altro ancora.The parameters you pass to AcquireTokenAsync(...) determine which token you receive, the policy used in the authentication request, and more.

private async void SignUp(object sender, RoutedEventArgs e)
{
    AuthenticationResult result = null;
    try
    {
        // Use the app's clientId here as the scope parameter, indicating that
        // you want a token to the your app's backend web API (represented by
        // the cloud hosted task API).  Use the UiOptions.ForceLogin flag to
        // indicate to MSAL that it should show a sign-up UI no matter what.
        result = await pca.AcquireTokenAsync(new string[] { Globals.clientId },
                string.Empty, UiOptions.ForceLogin, null, null, Globals.authority,
                Globals.signUpPolicy);

        // Upon success, indicate in the app that the user is signed in.
        SignInButton.Visibility = Visibility.Collapsed;
        SignUpButton.Visibility = Visibility.Collapsed;
        EditProfileButton.Visibility = Visibility.Visible;
        SignOutButton.Visibility = Visibility.Visible;

        // When the request completes successfully, you can get user
        // information from the AuthenticationResult
        UsernameLabel.Content = result.User.Name;

        // After the sign up successfully completes, display the user's To-Do List
        GetTodoList();
    }

    // Handle any exeptions that occurred during execution of the policy.
    catch (MsalException ex)
    {
        if (ex.ErrorCode != "authentication_canceled")
        {
            // An unexpected error occurred.
            string message = ex.Message;
            if (ex.InnerException != null)
            {
                message += "Inner Exception : " + ex.InnerException.Message;
            }

            MessageBox.Show(message);
        }

        return;
    }
}

Avvia un flusso di accessoInitiate a sign-in flow

È possibile avviare un flusso di accesso allo stesso modo in cui si avvia un flusso di iscrizione.You can initiate a sign-in flow in the same way that you initiate a sign-up flow. Quando l'utente accede, eseguire la stessa chiamata a MSAL, questa volta usando i propri criteri di accesso:When a user signs in, make the same call to MSAL, this time by using your sign-in policy:

private async void SignIn(object sender = null, RoutedEventArgs args = null)
{
    AuthenticationResult result = null;
    try
    {
        result = await pca.AcquireTokenAsync(new string[] { Globals.clientId },
                    string.Empty, UiOptions.ForceLogin, null, null, Globals.authority,
                    Globals.signInPolicy);
        ...

Avviare un flusso di modifica del profiloInitiate an edit-profile flow

Anche in questo caso è possibile eseguire i criteri di modifica del profilo in modo analogo:Again, you can execute an edit-profile policy in the same fashion:

private async void EditProfile(object sender, RoutedEventArgs e)
{
    AuthenticationResult result = null;
    try
    {
        result = await pca.AcquireTokenAsync(new string[] { Globals.clientId },
                    string.Empty, UiOptions.ForceLogin, null, null, Globals.authority,
                    Globals.editProfilePolicy);

In tutti questi casi, MSAL restituisce un token in AuthenticationResult oppure genera un'eccezione.In all of these cases, MSAL either returns a token in AuthenticationResult or throws an exception. Ogni volta che si ottiene un token da MSAL, è possibile usare l'oggetto AuthenticationResult.User per aggiornare i dati utente nell'app, ad esempio l'interfaccia utente.Each time you get a token from MSAL, you can use the AuthenticationResult.User object to update the user data in the app, such as the UI. ADAL inoltre memorizza nella cache il token per l'uso in altre parti dell'applicazione.ADAL also caches the token for use in other parts of the application.

Verificare la presenza di token all'avvio dell'appCheck for tokens on app start

È possibile usare MSAL anche per tenere traccia dello stato di accesso dell'utente.You can also use MSAL to keep track of the user's sign-in state. In questa app si vuole che l'utente rimanga connesso anche dopo che ha chiuso e riaperto l'app.In this app, we want the user to remain signed in even after they close the app & re-open it. Nuovamente all'interno dell'override OnInitialized, usare il metodo AcquireTokenSilent di MSAL per verificare la disponibilità di token memorizzati nella cache:Back inside the OnInitialized override, use MSAL's AcquireTokenSilent method to check for cached tokens:

AuthenticationResult result = null;
try
{
    // If the user has has a token cached with any policy, we'll display them as signed-in.
    TokenCacheItem tci = pca.UserTokenCache.ReadItems(Globals.clientId).Where(i => i.Scope.Contains(Globals.clientId) && !string.IsNullOrEmpty(i.Policy)).FirstOrDefault();
    string existingPolicy = tci == null ? null : tci.Policy;
    result = await pca.AcquireTokenSilentAsync(new string[] { Globals.clientId }, string.Empty, Globals.authority, existingPolicy, false);

    SignInButton.Visibility = Visibility.Collapsed;
    SignUpButton.Visibility = Visibility.Collapsed;
    EditProfileButton.Visibility = Visibility.Visible;
    SignOutButton.Visibility = Visibility.Visible;
    UsernameLabel.Content = result.User.Name;
    GetTodoList();
}
catch (MsalException ex)
{
    if (ex.ErrorCode == "failed_to_acquire_token_silently")
    {
        // There are no tokens in the cache.  Proceed without calling the To Do list service.
    }
    else
    {
        // An unexpected error occurred.
        string message = ex.Message;
        if (ex.InnerException != null)
        {
            message += "Inner Exception : " + ex.InnerException.Message;
        }
        MessageBox.Show(message);
    }
    return;
}

Chiamare l'API delle attivitàCall the task API

Finora si è usato MSAL per eseguire i criteri e ottenere i token.You have now used MSAL to execute policies and get tokens. Quando si vuole usare uno di questi token per chiamare l'API dell'attività, è possibile usare di nuovo il metodo AcquireTokenSilent di MSAL per verificare la disponibilità di token memorizzati nella cache:When you want to use one these tokens to call the task API, you can again use MSAL's AcquireTokenSilent method to check for cached tokens:

private async void GetTodoList()
{
    AuthenticationResult result = null;
    try
    {
        // Here we want to check for a cached token, independent of whatever policy was used to acquire it.
        TokenCacheItem tci = pca.UserTokenCache.ReadItems(Globals.clientId).Where(i => i.Scope.Contains(Globals.clientId) && !string.IsNullOrEmpty(i.Policy)).FirstOrDefault();
        string existingPolicy = tci == null ? null : tci.Policy;

        // Use AcquireTokenSilent to indicate that MSAL should throw an exception if a token cannot be acquired
        result = await pca.AcquireTokenSilentAsync(new string[] { Globals.clientId }, string.Empty, Globals.authority, existingPolicy, false);

    }
    // If a token could not be acquired silently, we'll catch the exception and show the user a message.
    catch (MsalException ex)
    {
        // There is no access token in the cache, so prompt the user to sign-in.
        if (ex.ErrorCode == "failed_to_acquire_token_silently")
        {
            MessageBox.Show("Please sign up or sign in first");
            SignInButton.Visibility = Visibility.Visible;
            SignUpButton.Visibility = Visibility.Visible;
            EditProfileButton.Visibility = Visibility.Collapsed;
            SignOutButton.Visibility = Visibility.Collapsed;
            UsernameLabel.Content = string.Empty;
        }
        else
        {
            // An unexpected error occurred.
            string message = ex.Message;
            if (ex.InnerException != null)
            {
                message += "Inner Exception : " + ex.InnerException.Message;
            }
            MessageBox.Show(message);
        }

        return;
    }
    ...

Quando la chiamata a AcquireTokenSilentAsync(...) riesce e viene trovato un token nella cache, è possibile aggiungerlo all'intestazione Authorization della richiesta HTTP.When the call to AcquireTokenSilentAsync(...) succeeds and a token is found in the cache, you can add the token to the Authorization header of the HTTP request. L'API Web dell'attività userà questa intestazione per autenticare la richiesta di lettura dell'elenco attività dell'utente:The task web API will use this header to authenticate the request to read the user's to-do list:

    ...
    // Once the token has been returned by MSAL, add it to the http authorization header, before making the call to access the To Do list service.
    httpClient.DefaultRequestHeaders.Authorization = new AuthenticationHeaderValue("Bearer", result.Token);

    // Call the To Do list service.
    HttpResponseMessage response = await httpClient.GetAsync(Globals.taskServiceUrl + "/api/tasks");
    ...

Disconnettere l'utente dall'appSign the user out

Infine, è possibile usare la libreria MSAL per terminare la sessione dell'utente nell'app quando l'utente seleziona Esci. Quando si usa MSAL, questa operazione viene eseguita cancellando tutti i token dalla relativa cache:Finally, you can use MSAL to end a user's session with the app when the user selects Sign out. When using MSAL, this is accomplished by clearing all of the tokens from the token cache:

private void SignOut(object sender, RoutedEventArgs e)
{
    // Clear any remnants of the user's session.
    pca.UserTokenCache.Clear(Globals.clientId);

    // This is a helper method that clears browser cookies in the browser control that MSAL uses, it is not part of MSAL.
    ClearCookies();

    // Update the UI to show the user as signed out.
    TaskList.ItemsSource = string.Empty;
    SignInButton.Visibility = Visibility.Visible;
    SignUpButton.Visibility = Visibility.Visible;
    EditProfileButton.Visibility = Visibility.Collapsed;
    SignOutButton.Visibility = Visibility.Collapsed;
    return;
}

Eseguire l'app di esempioRun the sample app

Infine, compilare ed eseguire l'esempio.Finally, build and run the sample. Effettuare l'iscrizione all'app usando un indirizzo di posta elettronica o un nome utente.Sign up for the app by using an email address or user name. Disconnettersi e accedere nuovamente con lo stesso account utente.Sign out and sign back in as the same user. Modificare il profilo dell'utente.Edit that user's profile. Disconnettersi ed eseguire l'iscrizione usando un account utente diverso.Sign out and sign up by using a different user.

Aggiungere i provider di identità per i social networkAdd social IDPs

L'app supporta attualmente solo l'iscrizione e l'accesso dell'utente con account locali.Currently, the app supports only user sign-up and sign-in that use local accounts. Si tratta di account archiviati nella directory B2C che usano un nome utente e una password.These are accounts stored in your B2C directory that use a user name and password. Usando Azure AD B2C, è possibile aggiungere il supporto per altri provider di identità (IDP) senza modificare il codice.By using Azure AD B2C, you can add support for other identity providers (IDPs) without changing any of your code.

Per aggiungere provider di identità per i social media all'applicazione, seguire le istruzioni dettagliate fornite in questi articoli.To add social IDPs to your app, begin by following the detailed instructions in these articles. Per ogni provider di identità che si vuole supportare, è necessario registrare un'applicazione nel relativo sistema e ottenere un ID client.For each IDP you want to support, you need to register an application in that system and obtain a client ID.

Dopo aver aggiunto i provider di identità alla directory B2C, è necessario modificare ognuno dei tre criteri per includere i nuovi provider di identità, come descritto nell' articolo di riferimento sui criteri.After you add the identity providers to your B2C directory, you need to edit each of your three policies to include the new IDPs, as described in the policy reference article. Dopo aver salvato i criteri, eseguire nuovamente l'app.After you save your policies, run the app again. I nuovi provider di identità dovrebbero essere stati aggiunti tra le opzioni di accesso e iscrizione in ognuna delle esperienze per l'identità.You should see the new IDPs added as sign-in and sign-up options in each of your identity experiences.

Provare a usare i criteri e osservare gli effetti sull'app di esempio.You can experiment with your policies and observe the effects on your sample app. Aggiungere o rimuovere provider di identità, manipolare le attestazioni dell'applicazione o modificare gli attributi per l'iscrizione.Add or remove IDPs, manipulate application claims, or change sign-up attributes. Fare alcune prove fino a quando non risulta chiaro in che modo sono collegati i criteri, le richieste di autenticazione e MSAL.Experiment until you can see how policies, authentication requests, and MSAL tie together.

Come riferimento, l'esempio completo è disponibile come file con estensione zip.For reference, the completed sample is provided as a .zip file. È anche possibile clonarlo da GitHub:You can also clone it from GitHub:

git clone --branch complete https://github.com/AzureADQuickStarts/B2C-NativeClient-DotNet.git