Tutorial: Inscreva-se nos utilizadores e ligue para o Microsoft Graph API a partir de uma aplicação Android

Neste tutorial, você constrói uma app Android que se integra com o plataforma de identidades da Microsoft para assinar nos utilizadores e obter um token de acesso para ligar para o Microsoft Graph API.

Quando tiver concluído este tutorial, a sua aplicação aceitará inscrições de contas pessoais da Microsoft (incluindo outlook.com, live.com e outras) bem como contas de trabalho ou escola de qualquer empresa ou organização que utilize Azure Ative Directory.

Neste tutorial:

  • Criar um projeto de aplicações Android em Android Studio
  • Registe a aplicação no portal do Azure
  • Adicione código para suportar a inscrição do utilizador e a sua s inscrição
  • Adicione código para ligar para o Microsoft Graph API
  • Testar a aplicação

Pré-requisitos

  • Android Studio 3.5+

Como funciona este tutorial

Shows how the sample app generated by this tutorial works

A aplicação neste tutorial irá iniciar snuários nos utilizadores e obter dados em seu nome. Estes dados serão acedidos através de uma API protegida (Microsoft Graph API) que requer autorização e está protegida pelo plataforma de identidades da Microsoft.

Mais especificamente:

  • A sua aplicação irá iniciar sôms no utilizador através de um browser ou da Microsoft Authenticator e Portal da Empresa do Intune.
  • O utilizador final aceitará as permissões que a sua aplicação solicitou.
  • A sua aplicação será emitida um token de acesso para o microsoft Graph API.
  • O token de acesso será incluído no pedido HTTP para a API web.
  • Processe a resposta Graph Microsoft.

Esta amostra utiliza a Biblioteca de Autenticação do Microsoft para Android (MSAL) para implementar a autenticação: com.microsoft.identity.client.

A MSAL renovará automaticamente os tokens, entregará um único sign-on (SSO) entre outras aplicações no dispositivo e gerirá a Conta.s.

Este tutorial demonstra exemplos simplificados de trabalhar com a MSAL para Android. Para simplificar, utiliza apenas o Modo Conta Única. Para explorar cenários mais complexos, consulte uma amostra de código de trabalho completa na GitHub.

Criar um projeto

Se ainda não tem uma aplicação Android, siga estes passos para a criação de um novo projeto.

  1. Abra Android Studio e selecione Iniciar um novo projeto de Android Studio.
  2. Selecione A Atividade Básica e selecione Seguinte.
  3. Dê um nome à aplicação.
  4. Guarde o nome do pacote. Vai introduzi-lo mais tarde no portal do Azure.
  5. Mude a língua de Kotlin para Java.
  6. Desa estatua o nível de API mínimo para API 19 ou superior, e clique em Terminar.
  7. Na visão do projeto, escolha Project no dropdown para exibir ficheiros de projetos de origem e não fonte, abrir app/build.gradle e definir targetSdkVersion para 28.

Integrar-se na Biblioteca de Autenticação da Microsoft

Registar a aplicação

  1. Inicie sessão no portal do Azure.

  2. Se tiver acesso a vários inquilinos, utilize o filtro Diretório + subscrições no menu superior para mudar para o inquilino no qual pretende registar a inscrição.

  3. Procure e selecione Azure Active Directory.

  4. Em Gestão, selecione Registos de aplicações>Novo registo.

  5. Insira um Nome para a sua inscrição. Os utilizadores da sua aplicação podem ver este nome, e pode alterá-lo mais tarde.

  6. Selecione Registar.

  7. Em Gestão, selecione Autenticação>Adicione um Android plataforma>.

  8. Insira o nome do pacote do seu projeto. Se descarregou o código, este valor é com.azuresamples.msalandroidapp.

  9. Na secção hash Signature da página de aplicação Configure o seu Android, selecione Gerar um Hash de assinatura de desenvolvimento e copie o comando KeyTool para utilizar para a sua plataforma.

    KeyTool.exe é instalado como parte do Kit de Desenvolvimento de Java (JDK). Também tem de instalar a ferramenta OpenSSL para executar o comando KeyTool. Consulte a documentação Android sobre a geração de uma chave para mais informações.

  10. Introduza o Hash de assinatura gerado pelo KeyTool.

  11. Selecione Configurar e guarde a Configuração MSAL que aparece na página de configuração Android para que possa introduzi-la quando configurar a sua aplicação mais tarde.

  12. Selecione Concluído.

Configurar a aplicação

  1. No painel de projetos da Android Studio, navegue para app\src\main\res.

  2. Clique com o botão direito e escolha Novo>Diretório. Insira raw como o novo nome do diretório e clique em OK.

  3. Na app>src>main>res>raw, crie um novo ficheiro JSON chamado auth_config_single_account.json e cole a Configuração MSAL que guardou anteriormente.

    Abaixo do redirecionamento URI, pasta:

      "account_mode" : "SINGLE",
    

    O seu ficheiro config deve assemelhar-se 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 apenas demonstra como configurar uma aplicação no modo Conta Única. Consulte a documentação para obter mais informações sobre o modo de conta única vs. múltipla e configurar a sua aplicação

  4. Na app>src>main>AndroidManifest.xml, adicione a BrowserTabActivity atividade abaixo ao corpo de aplicação. Esta entrada permite que a Microsoft volte a ligar para a sua aplicação depois de completar 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 registou no portal do Azure pelo android:host= valor. Substitua o hash da chave que registou no portal do Azure para o valor android:path=. O Hash assinatura não deve ser codificado por URL. Verifique se aparece uma barra / no início do Hash de Assinatura.

    O "Nome do Pacote" por que substituirá o android:host valor deve ser semelhante a: com.azuresamples.msalandroidapp. O "Hash de assinatura" por que substituirá o seu android:path valor deve ser semelhante a: /1wIqXSqBj7w+h11ZifsnqwgyKrY=.

    Poderá também encontrar estes valores na folha de autenticação do registo da sua aplicação. Note que o seu URI de redirecionamento será semelhante a: msauth://com.azuresamples.msalandroidapp/1wIqXSqBj7w%2Bh11ZifsnqwgyKrY%3D. Embora o Hash de Assinatura esteja codificado por URL no final deste valor, o Hash de Assinatura não deve ser codificado por URL no seu android:path valor.

Utilizar MSAL

Adicione MSAL ao seu projeto

  1. Na janela do projeto Android Studio, navegue para 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")
    }
    

    Mais sobre o Microsoft Graph SDK

Importações Exigidas

Adicione o seguinte ao topo da app>src>main>java>com.exemplo (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.*;

Instantiate 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

No interior da MainActivity classe, consulte o seguinte método onCreate() para instantaneaizar o MSAL utilizando 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);
                }
            });
}

loadAcount

//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);
        }
    });
}

inicializeUI

Ouça os botões e os métodos de chamada ou faça login em conformidade.

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 assinatura com a MSAL remove todas as informações conhecidas sobre um utilizador da aplicação, mas o utilizador ainda terá uma sessão ativa no seu dispositivo. Se o utilizador tentar entrar novamente, poderá ver uI de inscrição, mas pode não precisar de reentrar nas suas credenciais porque a sessão do dispositivo ainda está ativa.

getAuthInteractiveCallback

Chamada usada para pedidos interativos.

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.");
        }
    };
}

obterAuthSilentCallback

Chamada usada para pedidos silenciosos

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 o Microsoft Graph API

O código a seguir demonstra como ligar para o GraphAPI utilizando 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 UI

Atividade

Se quiser modelo a sua UI deste tutorial, os seguintes métodos fornecem um guia para atualizar texto e ouvir botões.

updateUI

Ativar/desativar os botões com base no estado de inscrição e no texto definido.

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 texto em UI para refletir a assinatura.

private void performOperationOnSignOut() {
    final String signOutText = "Signed Out.";
    currentUserTextView.setText("");
    Toast.makeText(getApplicationContext(), signOutText, Toast.LENGTH_SHORT)
            .show();
}

Layout

Ficheiro de amostra activity_main.xml 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 a aplicação

Executar localmente

Construa e implemente a aplicação num dispositivo de teste ou emulador. Você deve ser capaz de iniciar sôms e obter fichas para contas Azure AD ou pessoais da Microsoft.

Depois de iniciar sposição, a aplicação apresentará os dados devolvidos do ponto final Graph /me Microsoft. PR 4

A primeira vez que qualquer utilizador entrar na sua aplicação, será solicitado pela identidade da Microsoft para consentir com as permissões solicitadas. Alguns inquilinos Azure AD têm o consentimento do utilizador desativado, o que requer que os administradores consintam em nome de todos os utilizadores. Para apoiar este cenário, você precisará criar o seu próprio inquilino ou receber o consentimento administrativo.

Limpar os recursos

Quando já não for necessário, elimine o objeto da aplicação que criou no Passo de Inscrição da sua aplicação .

Ajuda e suporte

Se precisar de ajuda, quer relatar um problema ou quer aprender sobre as suas opções de suporte, consulte ajuda e apoio aos desenvolvedores.

Passos seguintes

Saiba mais sobre a construção de aplicações móveis que chamem APIs web protegidas na nossa série de cenários multi-partes.