Office 365: introduzione allo sviluppo – Microsoft Graph APIs e UWP

 

Premessa

Come tutti ben sapete, la piattaforma di Microsoft offre una serie di servizi che milioni di utenti ogni giorno utilizzano sia per il proprio lavoro, che nella vita privata.
Pensiamo ad esempio ad Outlook per le e-mail e il calendario, a OneDrive per lo storage e la condivisione di files e foto, o a SharePoint e a tutte le informazioni che gestisce nelle aziende.
Se guardiamo tutto questo dal punto di vista del programmatore, abbiamo un mondo di informazioni che è possibile sfruttare per lo sviluppo di applicazioni sempre più “connesse” alla produttività quotidiana di individui e società.

Ognuno di questi servizi (SharePoint, Exchange, OneDrive ecc..) però, è raggiungibile tramite un proprio end-point e questo, per lo sviluppatore, si traduce nel dover conoscere tutti questi end-point e, per ognuno di essi, di dover richiedere un token di autenticazione per poter accedere alle risorse, aumentando notevolmente la complessità del codice e lo sforzo richiesto per aggregare e/o far interagire le informazioni esposte dai diversi servizi.

Come anticipato nel precedente post, grazie alle Microsoft Graph APIs (precedentemente chiamate Office 365 Unified API) è possibile creare applicazioni (o aggiungere funzionalità ad applicazioni esistenti) in grado di accedere ai dati di Office 365 ed eseguire numerose operazioni in modo molto semplice e sicuro.

Un end-point “to rule them all”

Le Microsoft Graph APIs sono appunto un set di API REST che si posiziona al di sopra dei vari servizi, e che permette da un unico end-point ( https://graph.microsoft.com ) di accedere a tutti i livelli sottostanti.
Oltre all’enorme vantaggio di non dover conoscere i singoli end-point, questo approccio permette anche di autenticarsi una sola volta e di utilizzare un unico token per tutte le chiamate, semplificando enormemente lo sviluppo e l’interazione fra le diverse entità.
Grazie a queste API è infatti possibile accedere facilmente e in modo strutturato a entità come utenti, messaggi, gruppi, calendari, eventi, files e altro ancora.

image

Utilizzare le Graph API in una Universal Windows Platform App (UWP) – Impostazione del progetto

Dopo aver visto che cosa sono le Microsoft Graph APIs, vediamo come utilizzarle per connettersi ad un tenant Office 365 e recuperare le informazioni dell’utente in una applicazione universale per Windows 10 sviluppata in XAML/C# con Visual Studio 2015.

Lanciamo dunque Visual Studio 2015 e creiamo un nuovo progetto con il template Blank App (Universal Windows) che chiameremo ad esempio MyUniversalInfo

image

Per prima cosa occorre aggiungere il Connected Service di Office 365. Clicchiamo con il tasto destro sul progetto e selezioniamo Add—>Connected Service

image

Nella Wizard che viene avviato selezioniamo Office 365 APIs e clicchiamo su “Configure”

image

Nel passaggio successivo inseriamo il dominio di Office 365 (tipicamente nel formato [miodominio].onmicrosoft.com) che vogliamo utilizzare per lo sviluppo dell’applicazione e clicchiamo su Next

image 

Selezioniamo quindi l’opzione di creazione di una nuova Azure AD app e clicchiamo su Next

image

A questo punto è possibile selezionare i permessi che vogliamo dare all’applicazione. Nel nostro caso andremo a scegliere i permessi di lettura delle informazioni relative all’account.
Per fare ciò occorre abilitare le voci della sezione Users and Groups come evidenziato in figura. Dopo di che possiamo completare il processo cliccando su Finish

image

Visual Studio si occuperà di impostare il progetto scaricando i pacchetti da NuGet ed aggiungendo le risorse ed i servizi necessari.

image

Nel file App.xaml possiamo vedere che sono state aggiunte delle risorse contenenti i dati relativi al client ID, al tenant e all’autority di autenticazione di Azure AD

image

Arrivati a questo punto ci manca solo una cosa per avviare la configurazione lato server (dal portale di Azure), ovvero la Uri di reindirizzamento che AD deve utilizzare una volta completata l’autenticazione.
Questa Uri è univoca per l’applicazione e, per poterla ricavare comodamente da codice, iniziamo ad aggiungere una classe statica al progetto (dove inseriremo anche tutti i metodi che ci serviranno in seguito) che chiameremo OfficeHelper.
In questa classe aggiungiamo un metodo chiamato GetRedirectUri che restituisce l’url nel formato richiesto

 public static string GetRedirectUri()
 {
     return string.Format("ms-appx-web://microsoft.aad.brokerplugin/{0}", 
         WebAuthenticationBroker.GetCurrentApplicationCallbackUri().Host).ToUpper();
 }

Ora apriamo il file MainPage.xaml e iniziamo a predisporre l’interfaccia grafica aggiungendo qualche controllo che ci permetterà di visualizzare le informazioni che ricaveremo tramite le API.

Come abbiamo detto prima, in questo esempio utilizzeremo le Graph API per ricavare le informazioni del profilo dell’utente loggato, pertanto ci serviranno alcune caselle di testo ed un bottone per avviare la procedura. Oltre a questi controlli però, aggiungiamo anche una TextBox per visualizzare la redirect Uri che ci viene restituita dal metodo che abbiamo creato poco fa, in modo da poterla copiare per poi incollarla nell’apposito campo sul portale di Azure.

Modifichiamo dunque lo XAML della Grid della MainPage come mostrato qui di seguito:

 <Grid Background="{ThemeResource ApplicationPageBackgroundThemeBrush}">
     <Grid.RowDefinitions>
         <RowDefinition Height="70"/>
         <RowDefinition Height="100"/>
         <RowDefinition Height="*"/>
     </Grid.RowDefinitions>
     <TextBox x:Name="redirectUriText" Grid.Row="0" HorizontalAlignment="Left" Width="Auto"/>
     <Button x:Name="getInfoButton" Grid.Row="1" HorizontalAlignment="Center" Height="80" Width="200" Click="getInfoButton_Click">Get My Info</Button>
     <StackPanel Background="LightCyan" Grid.Row="2" Orientation="Vertical" Margin="20">
         <TextBlock x:Name="givennameText" Margin="10"/>
         <TextBlock x:Name="surnameText" Margin="10"/>
         <TextBlock x:Name="emailText" Margin="10"/>
     </StackPanel>
 </Grid>

Analizzando rapidamente il codice, possiamo notare una TextBox (utilizzata per semplicità, in modo tale che il contenuto sia selezionabile) chiamata redirectUriText che ci servirà per visualizzare la Uri. Inoltre è presente uno StackPanel che contiene 3 TextBlock (givennameText, surnameText ed emailText) nelle quali visualizzeremo le relative informazioni restituite dalla API.

Passiamo ora al code behind (MainPage.xaml.cs) e aggiungiamo il codice necessario per ricavare la Uri.

Nel metodo MainPage(), ovvero il costruttore della classe, è sufficiente aggiungere una riga di codice che richiami il metodo presente nella classe statica creata in precedenza e assegni il risultato alla proprietà Text della TextBox

 public MainPage()
 {
     this.InitializeComponent();
  
     //visualizzo l'uri da impostare su Azure per il redirect
     redirectUriText.Text = OfficeHelper.GetRedirectUri();
 }

Oltre a questa riga dobbiamo aggiungere l’handler dell’evento click del bottone (che implementeremo in seguito) per fare in modo che il progetto compili e possa essere eseguito

 private async void getInfoButton_Click(object sender, RoutedEventArgs e)
 {
 }

A questo punto abbiamo tutti i dati necessari per poter configurare i permessi della nostra app sulla directory di AD, pertanto eseguiamo l’applicazione e copiamo l’intera Url che viene visualizzata sulla text in alto

image

 

Configurazione di Azure AD

Per fare in modo che la nostra applicazione possa richiamare le API, è necessario che abbia i permessi per farlo e che sia quindi abilitata nella directory di AD tramite il portale di Azure.

Accediamo quindi al portale di Azure, selezioniamo ACTIVE DIRECTORY e clicchiamo sul tab APPLICATIONS

image      image

Aggiungiamo una nuova applicazione tramite il bottone ADD e, nel popup che viene visualizzato, selezioniamo la voce “Add an application my organization is developing

image

Inseriamo il nome dell’applicazione (ad esempio MyUniversalInfo-UWP) e selezioniamo il tipo di applicazione nativa

image

Nella sezione seguente ci viene richiesta la Redirect Uri, pertanto possiamo incollare il valore ricavato precedentemente dalla nostra applicazione

image

Una volta creata l’applicazione, dobbiamo prendere il Client Id generato ed impostarlo nel file App.xaml in Visual Studio

image

image

L’ultima cosa che dobbiamo fare lato Azure, è abilitare i permessi per l’app. Per fare ciò selezioniamo il tab CONFIGURE e, nella sezione “permissions to other applications” clicchiamo su Add application

image

aggiungiamo Microsoft Graph

image

e, nella tendina dei permessi, abilitiamo Sign in and read user profile. Infine salviamo .

image

Per il nostro esempio è sufficiente abilitare questo permesso, ma come potete vedere è possibile agire in modo granulare su una serie di parametri che ci permettono di impostare in modo preciso i servizi e le funzioni ai quali l’applicazione può accedere.

A questo punto siamo pronti per implementare nella nostra app i metodi per effettuare la login e leggere le informazioni dell’utente.

Utilizzo delle API da Visual Studio

Torniamo alla classe OfficeHelper.cs creata in precedenza e iniziamo ad aggiungere un po’ di stringhe che ci saranno utili nell’implementazione dei metodi di login e lettura delle info.

All’inizio della classe inseriamo il seguente codice:

 static string serviceEndpoint = "https://graph.microsoft.com/v1.0/";
 static string clientId = App.Current.Resources["ida:ClientId"].ToString();
  
 //autority da utilizzare se l'app lavora su uno specifico Tenant
 static string authority = string.Format("{0}{1}", App.Current.Resources["ida:AADInstance"].ToString(),
                                                   App.Current.Resources["ida:Domain"].ToString());
  
 //autority da utilizzare se l'app lavora su ogni Azure Tenant
 //static string authority = "organizations";
  
 static string ResourceUrl = "https://graph.microsoft.com/";

dove andiamo semplicemente a dichiarare alcune stringhe valorizzate con i dati presenti nelle risorse del progetto (clientId, AADInstance e Domain che abbiamo visto in precedenza nel file App.xaml) oltre all’url delle APIs.

Aggiungiamo poi un metodo GetTokenAsync per effettuare la login tramite l’utilizzo del controllo WebAccountProvider che ci permette di ricevere il token di autenticazione da utilizzare nelle chiamate e ci fornisce l’interfaccia utente per la login a “costo zero”

 /// <summary>
 /// Restituisce l'access Token per le Graph APIs
 /// </summary>
 public static async Task<string> GetTokenAsync()
 {
     WebAccount userAccount = null;
  
     WebAccountProvider aadAccountProvider = await WebAuthenticationCoreManager.FindAccountProviderAsync("https://login.microsoft.com", authority);
  
     WebTokenRequest webTokenRequest = new WebTokenRequest(aadAccountProvider, string.Empty, clientId, WebTokenRequestPromptType.ForceAuthentication);
     webTokenRequest.Properties.Add("resource", ResourceUrl);
  
     WebTokenRequestResult webTokenRequestResult = await WebAuthenticationCoreManager.RequestTokenAsync(webTokenRequest);
     if (webTokenRequestResult.ResponseStatus == WebTokenRequestStatus.Success)
     {
         WebTokenResponse webTokenResponse = webTokenRequestResult.ResponseData[0];
         userAccount = webTokenResponse.WebAccount;
         return webTokenResponse.Token;
     }
  
     return null;
 }

Questo metodo restituisce un Task<string> (e non semplicemente string ) in quanto si tratta di un metodo asincrono, ed utilizza alcune classi presenti nei namespaces:

using Windows.Security.Authentication.Web;

using Windows.Security.Authentication.Web.Core;

using Windows.Security.Credentials;

che vanno pertanto inclusi nella classe per far sì che il tutto compili correttamente.

L’ultimo metodo che ci resta da aggiungere è proprio quello che effettua la chiamata vera e propria alla API e ci restituisce un oggetto User che contiene le informazioni relative all’utente che si è autenticato.

 /// <summary>
 /// Restituisce i dati dell'utente
 /// </summary>
 public static async Task<Microsoft.Graph.User> GetMyInfoAsync()
 {
     //JObject jResult = null;
  
     try
     {
         HttpClient client = new HttpClient();
         var token = await OfficeHelper.GetTokenAsync();
         client.DefaultRequestHeaders.Add("Authorization", $"Bearer {token}");
  
         // Endpoint dell'utente loggato
         Uri usersEndpoint = new Uri($"{serviceEndpoint}me");
  
         HttpResponseMessage response = await client.GetAsync(usersEndpoint);
  
         if (response.IsSuccessStatusCode)
         {
             string responseContent = await response.Content.ReadAsStringAsync();
             return Newtonsoft.Json.JsonConvert.DeserializeObject<Microsoft.Graph.User>(responseContent);                    
         }
         else
         {
             var msg = new MessageDialog("Non è stato possibile recuperare i dati dell'utente. Risposta del server: " + response.StatusCode);
             await msg.ShowAsync();
             return null;
         }
     }
     catch (Exception e)
     {
         var msg = new MessageDialog("Si è verificato il seguente errore: " + e.Message);
         await msg.ShowAsync();
         return null;
     }
 }

Questo metodo utilizza la classe HttpClient per effettuare una semplice chiamata alla API “me” (che restituisce i dati dell’utente) costruendo l’Url tramite la nuova sintassi introdotta con C# 6. Se non vi è chiara questa sintassi, potete riscrivere ad esempio la riga

Uri usersEndpoint = new Uri($"{serviceEndpoint}me");

mediante il classico metodo Format della classe string in questo modo:

Uri usersEndpoint = new Uri(string.Format("{0}me",serviceEndpoint));

Inoltre va notato che il risultato JSON della chiamata viene deserializzato in un oggetto User. La classe User, insieme a molte altre classi, fa parte dell’SDK Microsoft.Graph ( attualmente in Preview per UWP ), che possiamo aggiungere al progetto direttamente tramite NuGet

image

e che ci mette a disposizione una serie di classi che ci facilitano la deserializzazione delle informazioni restituite dalle APIs permettendoci di lavorare con oggetti complessi senza dover implementare manualmente la logica.

L’ultima cosa che ci resta da fare è finire l’implementazione dell’evento click del bottone, aggiungendo il codice per richiamare il metodo GetMyInfoAsync e visualizzare le informazioni nelle TextBlock.

Torniamo quindi nel code behind della MainPage e completiamo il metodo getInfoButton_Click con il seguente codice

 private async void getInfoButton_Click(object sender, RoutedEventArgs e)
 {
     var userData = await OfficeHelper.GetMyInfoAsync();
  
     givennameText.Text = $"Nome: {userData.givenName}";
     surnameText.Text = $"Cognome {userData.surname}";
     emailText.Text = $"E-mail: {userData.mail}";
 }

Con quest’ultimo passaggio abbiamo completato il nostro lavoro e siamo finalmente pronti per testare la nostra app!

Lanciando l’applicazione e cliccando sul bottone Get my Info viene visualizzata la finestra di autenticazione dove possiamo eseguire la login inserendo utente e password del nostro account

image

Una volta inserite le credenziali e cliccato sul bottone Sign In, ci verrà mostrata una finestra con le informazioni relative ai permessi richiesti dalla nostra app, in base alla configurazione impostata precedentemente sul portale di Azure.

image

Una volta cliccato su “Accetto”, il processo di autenticazione sarà completo e la nostra app sarà in grado di richiamare la API e di mostrare a video le nostre informazioni ricevute.

image

 

Conclusione e link utili

In questo posto abbiamo visto come utilizzare le Microsoft Graph APIs in una applicazione per la Universal Windows Platform.

Il progetto demo completo è disponibile su GitHub all’indirizzo:

https://github.com/mamarche/MyUniversalInfo

Gli SDK Microsoft.Graph per le diverse piattaforme li potete trovare qui:

https://graph.microsoft.io/code-samples-and-sdks

Qui trovate un utile sito che permette di testare le varie APIs:

https://graphexplorer2.azurewebsites.net/

e qui la documentazione:

https://graph.microsoft.io/docs