Tutorial: Conectar usuários e chamar a API do Microsoft Graph de um aplicativo Android
Neste tutorial, você criará um aplicativo Android que se integra à plataforma de identidade da Microsoft para conectar usuários e obter um token de acesso para chamar a API do Microsoft Graph.
Após concluir este tutorial, seu aplicativo aceitará conexões de contas Microsoft pessoais (incluindo outlook.com, live.com e outras), bem como contas corporativas ou de estudante de qualquer empresa ou organização que utilize o Azure Active Directory.
Neste tutorial:
- Criar um projeto de aplicativo Android no Android Studio
- Registrar o aplicativo no portal do Azure
- Adicionar código para entrada e saída do usuário
- Adicionar código para chamar a API do Microsoft Graph
- Testar o aplicativo
Pré-requisitos
- Android Studio 3.5+
Como este tutorial funciona
O aplicativo deste tutorial conectará usuários e obterá dados no nome deles. Esses dados serão acessados por meio de uma API protegida (API do Microsoft Graph) que exige autorização e é protegida pela plataforma de identidade da Microsoft.
Mais especificamente:
- Seu aplicativo conectará o usuário por meio de um navegador ou do Microsoft Authenticator e pelo Portal da Empresa do Intune.
- O usuário final aceitará as permissões solicitadas por seu aplicativo.
- Será emitido um token de acesso para seu aplicativo para a API do Microsoft Graph.
- O token de acesso será incluído na solicitação HTTP para API Web.
- Processe a resposta do Microsoft Graph.
Esta amostra usa a MSAL (Biblioteca de Autenticação da Microsoft) para Android para implementar a Autenticação: com.microsoft.identity.client.
A MSAL automaticamente renovará tokens, fornecerá o SSO (logon único) entre outros aplicativos no dispositivo e ajudará a gerenciar as contas.
Este tutorial demonstra exemplos simplificados de como trabalhar com a MSAL para Android. Para simplificar, ele usa apenas o modo de conta única. Para explorar cenários mais complexos, confira um exemplo de código funcional concluído no GitHub.
Criar um projeto
Se você ainda não tiver um aplicativo Android, siga estas etapas para configurar um novo projeto.
- Abra o Android Studio e selecione Iniciar um novo projeto do Android Studio.
- Selecione Atividade Básica e, em seguida, Avançar.
- Nome do seu aplicativo.
- Guarde o nome do pacote. Você o inserirá mais tarde no portal do Azure.
- Altere a linguagem de Kotlin para Java.
- Defina o Nível mínimo da API como API 19 ou superior e clique em Concluir.
- Na exibição do projeto, escolha Projeto na lista suspensa para exibir os arquivos do projeto que são e que não são a fonte, abra app/build.gradle e defina
targetSdkVersioncomo28.
Integrar à Biblioteca de Autenticação da Microsoft
Registre seu aplicativo
Entre no portal do Azure.
Se você tem acesso a vários locatários, use o filtro Diretórios + assinaturas
no menu superior para mudar para o locatário no qual você deseja registrar o aplicativo.Pesquise Azure Active Directory e selecione-o.
Em Gerenciar, selecione Registros de aplicativo>Novo registro.
Insira um Nome para seu aplicativo. Os usuários do seu aplicativo podem ver esse nome e você pode alterá-lo mais tarde.
Selecione Registrar.
Em Gerenciar, selecione Autenticação>Adicionar uma plataforma>Android.
Insira o nome do pacote do seu projeto. Se você baixou o código, esse valor é
com.azuresamples.msalandroidapp.Em Hash de assinatura, que é uma seção da página Configure seu aplicativo Android, selecione Gerar um Hash de assinatura de desenvolvimento e copie o comando KeyTool para usar em sua plataforma.
KeyTool.exe é instalado como parte do JDK (Java Development Kit). Você também precisará instalar a ferramenta OpenSSL para executar o comando KeyTool. Veja a documentação do Android sobre como gerar uma chave para obter mais informações.
Insira o Hash de assinatura gerado por KeyTool.
Selecione Configurar e salve a Configuração da MSAL exibida na página Configuração do Android, de modo que você possa inseri-la quando configurar o aplicativo mais tarde.
Selecione Concluído.
Configurar seu aplicativo
No painel do projeto do Android Studio, navegue até app\src\main\res.
Clique com o botão direito do mouse em res e escolha Novo>Diretório. Insira
rawcomo o nome do novo diretório e clique em OK.Em app>src>main>res>raw, crie um arquivo JSON chamado
auth_config_single_account.jsone cole a Configuração da MSAL salva anteriormente.Abaixo do URI de Redirecionamento, cole:
"account_mode" : "SINGLE",Seu arquivo de configuração deve ser semelhante a este exemplo:
{ "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" } } ] }Este tutorial demonstra apenas como configurar um aplicativo no modo de conta única. Exiba a documentação para obter mais informações sobre o modo de conta única versus várias contas e como configurar seu aplicativo
Em app>src>main>AndroidManifest.xml, adicione a atividade
BrowserTabActivityabaixo ao corpo do aplicativo. Essa entrada permite que a Microsoft faça uma chamada de retorno ao aplicativo após concluir a autenticação:<!--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>Substitua o nome do pacote que você registrou no portal do Azure para o valor
android:host=. Substitua o hash da chave que você registrou no portal do Azure para o valorandroid:path=. O hash de assinatura não deve ser codificado por URL. Verifique se há um/inicial no começo do seu hash de assinatura.O "Nome do Pacote" pelo qual você substituirá o valor de
android:hostdeve ser semelhante a:com.azuresamples.msalandroidapp. O "Hash de Assinatura" pelo qual você substituirá o valor deandroid:pathdeve ser semelhante a:/1wIqXSqBj7w+h11ZifsnqwgyKrY=.Você também poderá encontrar esses valores na folha Autenticação do registro do aplicativo. Observe que o URI de redirecionamento será semelhante a:
msauth://com.azuresamples.msalandroidapp/1wIqXSqBj7w%2Bh11ZifsnqwgyKrY%3D. Embora o hash de assinatura seja codificado por URL ao final desse valor, o hash de assinatura não deve ser codificado por URL no valor deandroid:path.
Usar a MSAL
Adicionar MSAL ao seu projeto
Na janela do projeto do Android Studio, navegue até app>build.gradle e adicione o seguinte:
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") }
Importações necessárias
Adicione o seguinte à parte superior do 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.*;
Instanciar o PublicClientApplication
Inicializar variáveis
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
Dentro da classe MainActivity, consulte o método onCreate() a seguir para instanciar a MSAL usando o 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
Ouça os botões e chame os métodos ou erros de log correspondentes.
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());
}
});
}
Importante
A saída com a MSAL remove todas as informações conhecidas sobre um usuário deste aplicativo, mas o usuário ainda terá uma sessão ativa em seu dispositivo. Se o usuário tentar entrar novamente, ele poderá ver a interface do usuário de entrada, mas talvez não precise inserir novamente suas credenciais, pois a sessão do dispositivo ainda estará ativa.
getAuthInteractiveCallback
Retorno de chamada usado para solicitações interativas.
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
Retorno de chamada usado para solicitações silenciosas
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);
}
};
}
Chamar a API do Microsoft Graph
O código a seguir demonstra como chamar o GraphAPI usando o Graph SDK.
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);
}
});
}
Adicionar interface do usuário
Atividade
Se você quiser modelar a interface do usuário com base nesse tutorial, os métodos a seguir fornecerão um guia para atualizar o texto e ouvir os botões.
updateUI
Habilitar/Desabilitar botões com base no estado de entrada e definir textos.
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étodo para atualizar textos na interface do usuário a fim de refletir a saída.
private void performOperationOnSignOut() {
final String signOutText = "Signed Out.";
currentUserTextView.setText("");
Toast.makeText(getApplicationContext(), signOutText, Toast.LENGTH_SHORT)
.show();
}
Layout
Arquivo activity_main.xml de exemplo para exibir botões e caixas de texto.
<?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>
Testar seu aplicativo
Executar localmente
Compilar e implantar o aplicativo em um emulador ou dispositivo de teste. Você deve conseguir conectar e obter tokens para o Azure AD ou contas Microsoft pessoais.
Após entrar, este aplicativo exibirá os dados retornados do ponto de extremidade /me do Microsoft Graph.
PR 4
Consentimento
Na primeira vez que um usuário entrar no seu aplicativo, ele será solicitado pela identidade da Microsoft a consentir com as permissões solicitadas. Alguns locatários do Azure AD desabilitaram o consentimento do usuário, o que exige que os administradores forneçam consentimento em nome de todos os usuários. Para dar suporte a esse cenário, você precisará criar seu próprio locatário ou receber o consentimento do administrador.
Limpar os recursos
Quando não for mais necessário, exclua o objeto de aplicativo criado na etapa Registrar seu aplicativo.
Ajuda e suporte
Se precisar de ajuda, quiser relatar um problema ou desejar saber mais sobre as opções de suporte, confira Ajuda e suporte para desenvolvedores.
Próximas etapas
Saiba mais sobre como criar aplicativos móveis que chamam APIs Web protegidas em nossa série de cenários com várias partes.