Tutoriel : Connecter des utilisateurs et appeler l’API Microsoft Graph à partir d’une application Android
Dans ce tutoriel, vous allez créer une application Android qui s’intègre à la plateforme d’identité Microsoft pour connecter des utilisateurs et obtenir un jeton d’accès pour appeler l’API Microsoft Graph.
À la fin de ce tutoriel, votre application acceptera les connexions de comptes Microsoft personnels (notamment outlook.com, live.com et d’autres) ainsi que de comptes professionnels ou scolaires de toute entreprise ou organisation utilisant Azure Active Directory.
Dans ce tutoriel :
- Créer un projet d’application Android dans Android Studio
- Inscrire l'application sur le portail Azure
- Ajouter du code pour prendre en charge la connexion et la déconnexion des utilisateurs
- Ajouter du code pour appeler l’API Microsoft Graph.
- Test de l'application
Prérequis
- Android Studio 3.5+
Fonctionnement de ce tutoriel
L’application utilisée dans ce tutoriel est destinée à connecter des utilisateurs et à obtenir des données au nom de ces derniers. Ces données sont accessibles par le biais d’une API protégée (l’API Microsoft Graph) qui nécessite une autorisation et est protégée par la plateforme d’identités Microsoft.
Plus précisément :
- Votre application connectera l’utilisateur via un navigateur ou Microsoft Authenticator et le portail d’entreprise Intune.
- L’utilisateur final acceptera les autorisations que votre application a demandées.
- Votre application émet un jeton d’accès pour l’API Microsoft Graph.
- Le jeton d’accès est inclus dans la requête HTTP adressée à l’API web.
- La réponse Microsoft Graph est traitée.
Cet exemple utilise la bibliothèque d’authentification Microsoft pour Android (MSAL) pour implémenter l’authentification : com.microsoft.identity.client.
MSAL renouvelle automatiquement les jetons, fournit une authentification unique (SSO) entre les autres applications de l’appareil et gère les comptes.
Ce tutoriel présente des exemples simplifiés montrant comment utiliser MSAL pour Android. Par souci de simplicité, il utilise uniquement le mode monocompte. Pour explorer des scénarios plus complexes, consultez cet exemple de code fonctionnel complet sur GitHub.
Création d’un projet
Si vous n’avez pas encore d’application Android, effectuez les étapes suivantes pour configurer un nouveau projet.
- Ouvrez Android Studio, puis sélectionnez Démarrer un nouveau projet Android Studio.
- Sélectionnez Activité de base, puis Suivant.
- Donnez un nom à votre application.
- Enregistrez le nom du package. Vous allez l’entrer plus tard dans le portail Azure.
- Remplacez le langage Kotlin par Java.
- Affectez à Niveau d’API minimal la valeur API 19 ou une valeur supérieure, puis cliquez sur Terminer.
- Dans la vue du projet, dans la liste déroulante, choisissez Projet pour afficher les fichiers projet sources et non-sources, ouvrez app/build.gradle, puis affectez à
targetSdkVersion
la valeur28
.
Intégrer à l’aide de la bibliothèque d’authentification Microsoft
Inscrivez votre application
Connectez-vous au portail Azure.
Si vous avez accès à plusieurs locataires, utilisez le filtre Répertoires + abonnements
dans le menu du haut pour basculer vers le locataire dans lequel vous voulez inscrire l’application.
Recherchez et sélectionnez Azure Active Directory.
Sous Gérer, sélectionnez Inscriptions d’applications>Nouvelle inscription.
Entrez un nom pour votre application. Les utilisateurs de votre application peuvent voir ce nom, et vous pouvez le changer ultérieurement.
Sélectionnez Inscription.
Sous Gérer, sélectionnez Authentification>Ajouter une plateforme>Android.
Entrez le nom du package de votre projet. Si vous avez téléchargé le code, cette valeur est
com.azuresamples.msalandroidapp
.Dans la section Hachage de signature de la page Configurer votre application Android, sélectionnez Création d’un hachage de signature de développement, puis copiez la commande KeyTool à utiliser pour votre plateforme.
Keytool.exe est installé en même temps que le kit de développement Java (JDK). Vous devez également installer l’outil OpenSSL pour exécuter la commande KeyTool. Pour plus d’informations, reportez-vous à la documentation Android sur la génération d’une clé.
Entrez le Hachage de signature généré par KeyTool.
Sélectionnez Configurer et enregistrez la Configuration MSAL qui apparaît dans la page Configuration Android pour pouvoir l’entrer plus tard quand vous devrez configurer votre application.
Sélectionnez Terminé.
Configuration de votre application
Dans le volet de projet d’Android Studio, accédez à app\src\main\res.
Cliquez avec le bouton droit sur res, puis choisissez Nouveau>Répertoire. Entrez
raw
en tant que nouveau nom de répertoire, puis cliquez sur OK.Dans app>src>main>res>raw, créez un fichier JSON appelé
auth_config_single_account.json
, puis collez la configuration MSAL que vous avez enregistrée.Sous l’URI de redirection, collez :
"account_mode" : "SINGLE",
Votre fichier de configuration doit ressembler à l’exemple suivant :
{ "client_id" : "0984a7b6-bc13-4141-8b0d-8f767e136bb7", "authorization_user_agent" : "DEFAULT", "redirect_uri" : "msauth://com.azuresamples.msalandroidapp/1wIqXSqBj7w%2Bh11ZifsnqwgyKrY%3D", "broker_redirect_uri_registered" : true, "account_mode" : "SINGLE", "authorities" : [ { "type": "AAD", "audience": { "type": "AzureADandPersonalMicrosoftAccount", "tenant_id": "common" } } ] }
Ce tutoriel montre uniquement comment configurer une application en mode monocompte. Consultez la documentation pour plus d’informations sur le mode monocompte ou multicompte et la configuration de votre application.
Dans app>src>main>AndroidManifest.xml, ajoutez l’activité
BrowserTabActivity
ci-dessous au corps de l’application. Cette entrée permet à Microsoft de rappeler votre application une fois l’authentification terminée :<!--Intent filter to capture System Browser or Authenticator calling back to our app after sign-in--> <activity android:name="com.microsoft.identity.client.BrowserTabActivity"> <intent-filter> <action android:name="android.intent.action.VIEW" /> <category android:name="android.intent.category.DEFAULT" /> <category android:name="android.intent.category.BROWSABLE" /> <data android:scheme="msauth" android:host="Enter_the_Package_Name" android:path="/Enter_the_Signature_Hash" /> </intent-filter> </activity>
Remplacez le nom de package que vous avez inscrit dans le portail Azure pour la valeur
android:host=
. Remplacez le hachage de clé que vous avez inscrit dans le portail Azure pour la valeurandroid:path=
. Le code de hachage de la signature ne doit pas être encodé par URL. Assurez-vous de la présence d’un caractère/
de début dans votre hachage de signature.Le « nom du package » par lequel vous remplacez la valeur
android:host
doit ressembler à ceci :com.azuresamples.msalandroidapp
. Le « code de hachage de la signature » par lequel vous remplacez votre valeurandroid:path
doit ressembler à ce qui suit :/1wIqXSqBj7w+h11ZifsnqwgyKrY=
.Vous pouvez également trouver ces valeurs dans le panneau Authentification de l’inscription de votre application. Notez que votre URI de redirection doit ressembler à ce qui suit :
msauth://com.azuresamples.msalandroidapp/1wIqXSqBj7w%2Bh11ZifsnqwgyKrY%3D
. Le code de hachage de la signature est encodé avec une URL à la fin de cette valeur, mais il ne doit pas être encodé avec une URL dans votre valeurandroid:path
.
Utiliser MSAL
Ajouter MSAL à votre projet
Dans la fenêtre de projet Android Studio, accédez à app>build.gradle, puis ajoutez ce qui suit :
apply plugin: 'com.android.application' allprojects { repositories { mavenCentral() google() mavenLocal() maven { url 'https://pkgs.dev.azure.com/MicrosoftDeviceSDK/DuoSDK-Public/_packaging/Duo-SDK-Feed/maven/v1' } maven { name "vsts-maven-adal-android" url "https://identitydivision.pkgs.visualstudio.com/_packaging/AndroidADAL/maven/v1" credentials { username System.getenv("ENV_VSTS_MVN_ANDROIDADAL_USERNAME") != null ? System.getenv("ENV_VSTS_MVN_ANDROIDADAL_USERNAME") : project.findProperty("vstsUsername") password System.getenv("ENV_VSTS_MVN_ANDROIDADAL_ACCESSTOKEN") != null ? System.getenv("ENV_VSTS_MVN_ANDROIDADAL_ACCESSTOKEN") : project.findProperty("vstsMavenAccessToken") } } jcenter() } } dependencies{ implementation 'com.microsoft.identity.client:msal:2.+' implementation 'com.microsoft.graph:microsoft-graph:1.5.+' } packagingOptions{ exclude("META-INF/jersey-module-version") }
Importations nécessaires
Ajoutez le code suivant au début de app>src>main>java>com.example(yourapp)>MainActivity.java
import android.os.Bundle;
import android.util.Log;
import android.view.View;
import android.widget.Button;
import android.widget.TextView;
import android.widget.Toast;
import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
import androidx.appcompat.app.AppCompatActivity;
import com.google.gson.JsonObject;
import com.microsoft.graph.authentication.IAuthenticationProvider; //Imports the Graph sdk Auth interface
import com.microsoft.graph.concurrency.ICallback;
import com.microsoft.graph.core.ClientException;
import com.microsoft.graph.http.IHttpRequest;
import com.microsoft.graph.models.extensions.*;
import com.microsoft.graph.requests.extensions.GraphServiceClient;
import com.microsoft.identity.client.AuthenticationCallback; // Imports MSAL auth methods
import com.microsoft.identity.client.*;
import com.microsoft.identity.client.exception.*;
Instancier PublicClientApplication
Initialiser les variables
private final static String[] SCOPES = {"Files.Read"};
/* Azure AD v2 Configs */
final static String AUTHORITY = "https://login.microsoftonline.com/common";
private ISingleAccountPublicClientApplication mSingleAccountApp;
private static final String TAG = MainActivity.class.getSimpleName();
/* UI & Debugging Variables */
Button signInButton;
Button signOutButton;
Button callGraphApiInteractiveButton;
Button callGraphApiSilentButton;
TextView logTextView;
TextView currentUserTextView;
onCreate
Dans la classe MainActivity
, reportez-vous à la méthode onCreate() suivante pour instancier MSAL à l’aide de SingleAccountPublicClientApplication
.
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
initializeUI();
PublicClientApplication.createSingleAccountPublicClientApplication(getApplicationContext(),
R.raw.auth_config_single_account, new IPublicClientApplication.ISingleAccountApplicationCreatedListener() {
@Override
public void onCreated(ISingleAccountPublicClientApplication application) {
mSingleAccountApp = application;
loadAccount();
}
@Override
public void onError(MsalException exception) {
displayError(exception);
}
});
}
loadAccount
//When app comes to the foreground, load existing account to determine if user is signed in
private void loadAccount() {
if (mSingleAccountApp == null) {
return;
}
mSingleAccountApp.getCurrentAccountAsync(new ISingleAccountPublicClientApplication.CurrentAccountCallback() {
@Override
public void onAccountLoaded(@Nullable IAccount activeAccount) {
// You can use the account data to update your UI or your app database.
updateUI(activeAccount);
}
@Override
public void onAccountChanged(@Nullable IAccount priorAccount, @Nullable IAccount currentAccount) {
if (currentAccount == null) {
// Perform a cleanup task as the signed-in account changed.
performOperationOnSignOut();
}
}
@Override
public void onError(@NonNull MsalException exception) {
displayError(exception);
}
});
}
initializeUI
Soyez à l’écoute des boutons et appelez les méthodes ou consignez les erreurs en conséquence.
private void initializeUI(){
signInButton = findViewById(R.id.signIn);
callGraphApiSilentButton = findViewById(R.id.callGraphSilent);
callGraphApiInteractiveButton = findViewById(R.id.callGraphInteractive);
signOutButton = findViewById(R.id.clearCache);
logTextView = findViewById(R.id.txt_log);
currentUserTextView = findViewById(R.id.current_user);
//Sign in user
signInButton.setOnClickListener(new View.OnClickListener(){
public void onClick(View v) {
if (mSingleAccountApp == null) {
return;
}
mSingleAccountApp.signIn(MainActivity.this, null, SCOPES, getAuthInteractiveCallback());
}
});
//Sign out user
signOutButton.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
if (mSingleAccountApp == null){
return;
}
mSingleAccountApp.signOut(new ISingleAccountPublicClientApplication.SignOutCallback() {
@Override
public void onSignOut() {
updateUI(null);
performOperationOnSignOut();
}
@Override
public void onError(@NonNull MsalException exception){
displayError(exception);
}
});
}
});
//Interactive
callGraphApiInteractiveButton.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
if (mSingleAccountApp == null) {
return;
}
mSingleAccountApp.acquireToken(MainActivity.this, SCOPES, getAuthInteractiveCallback());
}
});
//Silent
callGraphApiSilentButton.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
if (mSingleAccountApp == null){
return;
}
mSingleAccountApp.acquireTokenSilentAsync(SCOPES, AUTHORITY, getAuthSilentCallback());
}
});
}
Important
La déconnexion de MSAL entraîne la suppression de toutes les informations connues sur un utilisateur dans l’application. Toutefois, l’utilisateur a toujours une session active sur son appareil. Si l’utilisateur tente de se reconnecter, il peut éventuellement voir l’IU de connexion, mais il n’est pas obligé de ressaisir ses informations d’identification, car la session sur l’appareil est toujours active.
getAuthInteractiveCallback
Rappel utilisé pour les demandes interactives
private AuthenticationCallback getAuthInteractiveCallback() {
return new AuthenticationCallback() {
@Override
public void onSuccess(IAuthenticationResult authenticationResult) {
/* Successfully got a token, use it to call a protected resource - MSGraph */
Log.d(TAG, "Successfully authenticated");
/* Update UI */
updateUI(authenticationResult.getAccount());
/* call graph */
callGraphAPI(authenticationResult);
}
@Override
public void onError(MsalException exception) {
/* Failed to acquireToken */
Log.d(TAG, "Authentication failed: " + exception.toString());
displayError(exception);
}
@Override
public void onCancel() {
/* User canceled the authentication */
Log.d(TAG, "User cancelled login.");
}
};
}
getAuthSilentCallback
Rappel utilisé pour les demandes silencieuses
private SilentAuthenticationCallback getAuthSilentCallback() {
return new SilentAuthenticationCallback() {
@Override
public void onSuccess(IAuthenticationResult authenticationResult) {
Log.d(TAG, "Successfully authenticated");
callGraphAPI(authenticationResult);
}
@Override
public void onError(MsalException exception) {
Log.d(TAG, "Authentication failed: " + exception.toString());
displayError(exception);
}
};
}
Appeler l’API Microsoft Graph
Le code suivant montre comment appeler l’API Graph à l’aide du kit SDK Graph.
callGraphAPI
private void callGraphAPI(IAuthenticationResult authenticationResult) {
final String accessToken = authenticationResult.getAccessToken();
IGraphServiceClient graphClient =
GraphServiceClient
.builder()
.authenticationProvider(new IAuthenticationProvider() {
@Override
public void authenticateRequest(IHttpRequest request) {
Log.d(TAG, "Authenticating request," + request.getRequestUrl());
request.addHeader("Authorization", "Bearer " + accessToken);
}
})
.buildClient();
graphClient
.me()
.drive()
.buildRequest()
.get(new ICallback<Drive>() {
@Override
public void success(final Drive drive) {
Log.d(TAG, "Found Drive " + drive.id);
displayGraphResult(drive.getRawObject());
}
@Override
public void failure(ClientException ex) {
displayError(ex);
}
});
}
Ajouter une IU
Activité
Si vous souhaitez modéliser votre interface utilisateur en dehors de ce tutoriel, les méthodes suivantes fournissent un guide pour la mise à jour du texte et l’écoute des boutons.
updateUI
Activez/désactivez les boutons en fonction de l’état de connexion et du texte défini.
private void updateUI(@Nullable final IAccount account) {
if (account != null) {
signInButton.setEnabled(false);
signOutButton.setEnabled(true);
callGraphApiInteractiveButton.setEnabled(true);
callGraphApiSilentButton.setEnabled(true);
currentUserTextView.setText(account.getUsername());
} else {
signInButton.setEnabled(true);
signOutButton.setEnabled(false);
callGraphApiInteractiveButton.setEnabled(false);
callGraphApiSilentButton.setEnabled(false);
currentUserTextView.setText("");
logTextView.setText("");
}
}
displayError
private void displayError(@NonNull final Exception exception) {
logTextView.setText(exception.toString());
}
displayGraphResult
private void displayGraphResult(@NonNull final JsonObject graphResponse) {
logTextView.setText(graphResponse.toString());
}
performOperationOnSignOut
Méthode permettant de mettre à jour le texte dans l’interface utilisateur pour refléter la déconnexion.
private void performOperationOnSignOut() {
final String signOutText = "Signed Out.";
currentUserTextView.setText("");
Toast.makeText(getApplicationContext(), signOutText, Toast.LENGTH_SHORT)
.show();
}
Mise en page
Exemple de fichier activity_main.xml
permettant d’afficher des boutons et des zones de texte.
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
android:id="@+id/activity_main"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:background="#FFFFFF"
android:orientation="vertical"
tools:context=".MainActivity">
<LinearLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:orientation="horizontal"
android:paddingTop="5dp"
android:paddingBottom="5dp"
android:weightSum="10">
<Button
android:id="@+id/signIn"
android:layout_width="0dp"
android:layout_height="wrap_content"
android:layout_weight="5"
android:gravity="center"
android:text="Sign In"/>
<Button
android:id="@+id/clearCache"
android:layout_width="0dp"
android:layout_height="wrap_content"
android:layout_weight="5"
android:gravity="center"
android:text="Sign Out"
android:enabled="false"/>
</LinearLayout>
<LinearLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:gravity="center"
android:orientation="horizontal">
<Button
android:id="@+id/callGraphInteractive"
android:layout_width="0dp"
android:layout_height="wrap_content"
android:layout_weight="5"
android:text="Get Graph Data Interactively"
android:enabled="false"/>
<Button
android:id="@+id/callGraphSilent"
android:layout_width="0dp"
android:layout_height="wrap_content"
android:layout_weight="5"
android:text="Get Graph Data Silently"
android:enabled="false"/>
</LinearLayout>
<TextView
android:text="Getting Graph Data..."
android:textColor="#3f3f3f"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginLeft="5dp"
android:id="@+id/graphData"
android:visibility="invisible"/>
<TextView
android:id="@+id/current_user"
android:layout_width="match_parent"
android:layout_height="0dp"
android:layout_marginTop="20dp"
android:layout_weight="0.8"
android:text="Account info goes here..." />
<TextView
android:id="@+id/txt_log"
android:layout_width="match_parent"
android:layout_height="0dp"
android:layout_marginTop="20dp"
android:layout_weight="0.8"
android:text="Output goes here..." />
</LinearLayout>
Test de l'application
Exécution locale
Générez et déployez l’application sur un appareil de test ou un émulateur. Vous devez pouvoir vous connecter et obtenir des jetons pour les comptes Azure AD ou les comptes personnels Microsoft.
Une fois que vous êtes connecté, l’application affiche les données retournées par le point de terminaison /me
Microsoft Graph.
PR 4
Consentement
La première fois qu’un utilisateur se connecte à votre application, Microsoft Identity l’invitera à accepter les autorisations demandées. Certains locataires Azure AD ont désactivé le consentement de l’utilisateur, ce qui oblige les administrateurs à donner leur consentement au nom de tous les utilisateurs. Pour prendre en charge ce scénario, vous devez créer votre propre locataire ou recevoir le consentement de l’administrateur.
Nettoyer les ressources
Quand vous n’en avez plus besoin, supprimez l’objet d’application que vous avez créé à l’étape Inscrivez votre application.
Aide et support
Si vous avez besoin d’aide, si vous souhaitez signaler un problème ou si vous voulez en savoir plus sur vos options de support, consultez Aide et support pour les développeurs.
Étapes suivantes
Apprenez-en davantage sur la création d’applications mobiles qui appellent des API web protégées dans notre série de scénarios en plusieurs parties.