Sign in users and call the Microsoft Graph from an Android app

In this tutorial, you'll learn how to integrate an Android app into the Microsoft identity platform. Specifically, your app will sign in a user, get an access token to call the Microsoft Graph API, and make a request to the Microsoft Graph API.

When you've completed the guide, your application will accept sign-ins of personal Microsoft accounts (including outlook.com, live.com, and others) and work or school accounts from any company or organization that uses Azure Active Directory.

How this tutorial works

Shows how the sample app generated by this tutorial works

The app in this sample will sign in users and get data on their behalf. This data will be accessed through a protected API (Microsoft Graph API in this case) that requires authorization.

More specifically:

  • Your app will sign in the user either through a browser or the Microsoft Authenticator and Intune Company Portal.
  • The end user will accept the permissions your application has requested.
  • Your app will be issued an access token for the Microsoft Graph API.
  • The access token will be included in the HTTP request to the web API.
  • Process the Microsoft Graph response.

This sample uses the Microsoft Authentication library for Android (MSAL) to implement Auth. MSAL will automatically renew tokens, deliver SSO between other apps on the device, and manage the Account(s).

Prerequisites

  • This guided setup uses Android Studio.
  • Android 16 or later is required (19+ is recommended).

Library

This guide uses the following authentication library:

Library Description
com.microsoft.identity.client Microsoft Authentication Library (MSAL)

Set up your project

This tutorial will create a new project. If you want to download the completed tutorial instead, download the code.

Create a new project

  1. Open Android Studio, and select either Start a new Android Studio project.
    • If Android Studio is already open, select File > New > New Project.
  2. Leave Empty Activity as it is, select Next.
  3. Name your application, set the Minimum API level to API 19 or newer, hit Finish.
  4. In your app/build.gradle, set the targetedSdkVersion to 27.

Register your application

You can register your application in either of two ways, as described in the next two sections.

Register your app

  1. Go to the Azure portal > Select New registration.
  2. Enter a Name for your app > Register. Do not set a Redirect URI at this stage.
  3. In the Manage section, go to Authentication > Add a platform > Android
    • Enter your project's Package Name. If you downloaded the code, this value is com.azuresamples.msalandroidapp.
    • Enter your debug/development signature hash. Use the KeyTool command in the portal to generate the Signature Hash.
  4. Hit Configure and store the MSAL Configuration for later.

Build your app

Configure your Android app

  1. Right click res > New > Folder > Raw Resources Folder

  2. In app > res > raw, create a new JSON file called auth_config.json and paste your MSAL Configuration. See MSAL Configuration for more info.

  3. In app > manifests > AndroidManifest.xml, add the BrowserTabActivity activity below. This entry allows Microsoft to call back to your application after it completes the authentication:

    <!--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>
    

    Note, the Signature Hash used should NOT be URL encoded in the AndroidManifest.xml.

  4. Inside the AndroidManifest.xml and just above the <application> tag, add the following permissions:

    <uses-permission android:name="android.permission.INTERNET" />
    <uses-permission android:name="android.permission.ACCESS_NETWORK_STATE" />
    
  5. In the BrowserTabActivity, replace the Package Name and Signature Hash with the values registered in the Azure portal.

Create the app's UI

  1. Go to res > layout, and then open activity_main.xml.

  2. Change the activity layout from android.support.constraint.ConstraintLayout or other to LinearLayout.

  3. Add the android:orientation="vertical" property to the LinearLayout node.

  4. Paste the following code into the LinearLayout node, replacing the current content:

    <TextView
        android:text="Welcome, "
        android:textColor="#3f3f3f"
        android:textSize="50px"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:layout_marginLeft="10dp"
        android:layout_marginTop="15dp"
        android:id="@+id/welcome"
        android:visibility="invisible"/>
    
    <Button
        android:id="@+id/callGraph"
        android:text="Call Microsoft Graph"
        android:textColor="#FFFFFF"
        android:background="#00a1f1"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:layout_marginTop="200dp"
        android:textAllCaps="false" />
    
    <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"/>
    
    <LinearLayout
        android:layout_width="match_parent"
        android:layout_height="0dip"
        android:layout_weight="1"
        android:gravity="center|bottom"
        android:orientation="vertical" >
    
        <Button
            android:text="Sign Out"
            android:layout_width="match_parent"
            android:layout_height="wrap_content"
            android:layout_marginBottom="15dp"
            android:textColor="#FFFFFF"
            android:background="#00a1f1"
            android:textAllCaps="false"
            android:id="@+id/clearCache"
            android:visibility="invisible" />
    </LinearLayout>
    

Add MSAL to your project

  1. In Android Studio, select Gradle Scripts > build.gradle (Module: app).

  2. Under Dependencies, paste the following code:

    implementation 'com.android.volley:volley:1.1.1'
    implementation 'com.microsoft.identity.client:msal:0.3.+'
    

Use MSAL

The next few sections will be making changes inside the MainAcitivty.java. We'll be walking through each step needed to add and use MSAL in your app.

Required imports

Add the following imports to your project:

import android.app.Activity;
import android.content.Intent;
import android.util.Log;
import android.view.View;
import android.widget.Button;
import android.widget.TextView;
import android.widget.Toast;
import com.android.volley.*;
import com.android.volley.toolbox.JsonObjectRequest;
import com.android.volley.toolbox.Volley;
import org.json.JSONObject;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import com.microsoft.identity.client.*;
import com.microsoft.identity.client.exception.*;

Instantiating MSAL

Inside the MainActivity class, we'll need to instantiate MSAL along with a few configurations about what are app will do including the scopes and web API we want to access.

Copy the following variables inside the MainActivity:

final static String SCOPES [] = {"https://graph.microsoft.com/User.Read"};
final static String MSGRAPH_URL = "https://graph.microsoft.com/v1.0/me";

/* UI & Debugging Variables */
private static final String TAG = MainActivity.class.getSimpleName();
Button callGraphButton;
Button signOutButton;

/* Azure AD Variables */
private PublicClientApplication sampleApp;
private IAuthenticationResult authResult;

Now to instantiate MSAL, copy the following code inside the onCreate(...) method:

super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);

callGraphButton = (Button) findViewById(R.id.callGraph);
signOutButton = (Button) findViewById(R.id.clearCache);

callGraphButton.setOnClickListener(new View.OnClickListener() {
    public void onClick(View v) {
        onCallGraphClicked();
    }
});

signOutButton.setOnClickListener(new View.OnClickListener() {
    public void onClick(View v) {
        onSignOutClicked();
    }
});

/* Configure your sample app and save state for this activity */
sampleApp = new PublicClientApplication(
        this.getApplicationContext(),
        R.raw.auth_config);

/* Attempt to get a user and acquireTokenSilent */
sampleApp.getAccounts(new PublicClientApplication.AccountsLoadedCallback() {
    @Override
    public void onAccountsLoaded(final List<IAccount> accounts) {
        if (!accounts.isEmpty()) {
            /* This sample doesn't support multi-account scenarios, use the first account */
            sampleApp.acquireTokenSilentAsync(SCOPES, accounts.get(0), getAuthSilentCallback());
        } else {
            /* No accounts */
        }
    }
});

The above block of code attempts to sign in users silently when they open your application through getAccounts(...) and, if successful, acquireTokenSilentAsync(...). In the next few sections, we'll implement the callback handler for the case there are no signed in accounts.

Use MSAL to get Tokens

Now, we can implement the app's UI processing logic and getting tokens interactively through MSAL.

MSAL exposes two primary methods for getting tokens: acquireTokenSilentAsync and acquireToken.

acquireTokenSilentAsync signs in a user and get tokens without any user interaction if an account is present. If it succeeds, MSAL will handoff the tokens to your app, if it fails it will generate a MsalUiRequiredException. If this exception is generated or you want your user to have an interactive sign in experience (credentials, mfa, or other conditional access policies may or may not be required), then you can use acquireToken.

acquireToken will always show UI when attempting to sign in the user and get tokens; however, it might use session cookies in the browser or an account in the Microsoft authenticator to give an interactive-SSO experience.

To begin, create the following three UI methods inside the MainActivity class:

/* Set the UI for successful token acquisition data */
private void updateSuccessUI() {
    callGraphButton.setVisibility(View.INVISIBLE);
    signOutButton.setVisibility(View.VISIBLE);
    findViewById(R.id.welcome).setVisibility(View.VISIBLE);
    ((TextView) findViewById(R.id.welcome)).setText("Welcome, " +
            authResult.getAccount().getUsername());
    findViewById(R.id.graphData).setVisibility(View.VISIBLE);
}

/* Set the UI for signed out account */
private void updateSignedOutUI() {
    callGraphButton.setVisibility(View.VISIBLE);
    signOutButton.setVisibility(View.INVISIBLE);
    findViewById(R.id.welcome).setVisibility(View.INVISIBLE);
    findViewById(R.id.graphData).setVisibility(View.INVISIBLE);
    ((TextView) findViewById(R.id.graphData)).setText("No Data");

    Toast.makeText(getBaseContext(), "Signed Out!", Toast.LENGTH_SHORT)
            .show();
}

/* Use MSAL to acquireToken for the end-user
 * Callback will call Graph api w/ access token & update UI
 */
private void onCallGraphClicked() {
    sampleApp.acquireToken(getActivity(), SCOPES, getAuthInteractiveCallback());
}

Next, add a method to get the current activity and process silent & interactive callbacks:

public Activity getActivity() {
    return this;
}

/* Callback used in for silent acquireToken calls.
 * Looks if tokens are in the cache (refreshes if necessary and if we don't forceRefresh)
 * else errors that we need to do an interactive request.
 */
private AuthenticationCallback getAuthSilentCallback() {
    return new AuthenticationCallback() {

        @Override
        public void onSuccess(IAuthenticationResult authenticationResult) {
            /* Successfully got a token, call graph now */
            Log.d(TAG, "Successfully authenticated");

            /* Store the authResult */
            authResult = authenticationResult;

            /* call graph */
            callGraphAPI();

            /* update the UI to post call graph state */
            updateSuccessUI();
        }

        @Override
        public void onError(MsalException exception) {
            /* Failed to acquireToken */
            Log.d(TAG, "Authentication failed: " + exception.toString());

            if (exception instanceof MsalClientException) {
                /* Exception inside MSAL, more info inside the exception */
            } else if (exception instanceof MsalServiceException) {
                /* Exception when communicating with the STS, likely config issue */
            } else if (exception instanceof MsalUiRequiredException) {
                /* Tokens expired or no session, retry with interactive */
            }
        }

        @Override
        public void onCancel() {
            /* User cancelled the authentication */
            Log.d(TAG, "User cancelled login.");
        }
    };
}

/* Callback used for interactive request.  If succeeds we use the access
 * token to call the Microsoft Graph. Does not check cache
 */
private AuthenticationCallback getAuthInteractiveCallback() {
    return new AuthenticationCallback() {

        @Override
        public void onSuccess(IAuthenticationResult authenticationResult) {
            /* Successfully got a token, call graph now */
            Log.d(TAG, "Successfully authenticated");
            Log.d(TAG, "ID Token: " + authenticationResult.getIdToken());

            /* Store the auth result */
            authResult = authenticationResult;

            /* call graph */
            callGraphAPI();

            /* update the UI to post call graph state */
            updateSuccessUI();
        }

        @Override
        public void onError(MsalException exception) {
            /* Failed to acquireToken */
            Log.d(TAG, "Authentication failed: " + exception.toString());

            if (exception instanceof MsalClientException) {
                /* Exception inside MSAL, more info inside the exception */
            } else if (exception instanceof MsalServiceException) {
                /* Exception when communicating with the STS, likely config issue */
            }
        }

        @Override
        public void onCancel() {
            /* User cancelled the authentication */
            Log.d(TAG, "User cancelled login.");
        }
    };
}

Use MSAL for Sign-out

Next up, we'll add support for sign-out to our app.

It's important to note, sign-out with MSAL removes all known information about a user from this application, but the user will still have an active session on their device. If the user attempts to sign in again they may see interaction, but may not need to reenter their credentials due to the device session being active.

To add sign-out, copy the following method into your app that cycles through all accounts and removes them:

/* Clears an account's tokens from the cache.
 * Logically similar to "sign out" but only signs out of this app.
 * User will get interactive SSO if trying to sign back-in.
 */
private void onSignOutClicked() {
    /* Attempt to get a user and acquireTokenSilent
     * If this fails we do an interactive request
     */
    sampleApp.getAccounts(new PublicClientApplication.AccountsLoadedCallback() {
        @Override
        public void onAccountsLoaded(final List<IAccount> accounts) {

            if (accounts.isEmpty()) {
                /* No accounts to remove */

            } else {
                for (final IAccount account : accounts) {
                    sampleApp.removeAccount(
                            account,
                            new PublicClientApplication.AccountsRemovedCallback() {
                        @Override
                        public void onAccountsRemoved(Boolean isSuccess) {
                            if (isSuccess) {
                                /* successfully removed account */
                            } else {
                                /* failed to remove account */
                            }
                        }
                    });
                }
            }

            updateSignedOutUI();
        }
    });
}

Call the Microsoft Graph API

Once we have successfully gotten a token, we can make a request to the Microsoft Graph API. The access token will be inside the AuthenticationResult inside the auth callback's onSuccess(...) method. To construct an authorized request, your app will need to add the access token to the HTTP header:

header key value
Authorization Bearer

To do so in code, add the following two methods to your app to call graph and update the UI:

    /* Use Volley to make an HTTP request to the /me endpoint from MS Graph using an access token */
private void callGraphAPI() {
    Log.d(TAG, "Starting volley request to graph");

    /* Make sure we have a token to send to graph */
    if (authResult.getAccessToken() == null) {return;}

    RequestQueue queue = Volley.newRequestQueue(this);
    JSONObject parameters = new JSONObject();

    try {
        parameters.put("key", "value");
    } catch (Exception e) {
        Log.d(TAG, "Failed to put parameters: " + e.toString());
    }
    JsonObjectRequest request = new JsonObjectRequest(Request.Method.GET, MSGRAPH_URL,
            parameters,new Response.Listener<JSONObject>() {
        @Override
        public void onResponse(JSONObject response) {
            /* Successfully called graph, process data and send to UI */
            Log.d(TAG, "Response: " + response.toString());

            updateGraphUI(response);
        }
    }, new Response.ErrorListener() {
        @Override
        public void onErrorResponse(VolleyError error) {
            Log.d(TAG, "Error: " + error.toString());
        }
    }) {
        @Override
        public Map<String, String> getHeaders() {
            Map<String, String> headers = new HashMap<>();
            headers.put("Authorization", "Bearer " + authResult.getAccessToken());
            return headers;
        }
    };

    Log.d(TAG, "Adding HTTP GET to Queue, Request: " + request.toString());

    request.setRetryPolicy(new DefaultRetryPolicy(
            3000,
            DefaultRetryPolicy.DEFAULT_MAX_RETRIES,
            DefaultRetryPolicy.DEFAULT_BACKOFF_MULT));
    queue.add(request);
}

/* Sets the graph response */
private void updateGraphUI(JSONObject graphResponse) {
    TextView graphText = findViewById(R.id.graphData);
    graphText.setText(graphResponse.toString());
}

Learn more about the Microsoft Graph API!

Multi-account applications

This app is built for a single account scenario. MSAL supports multi-account scenarios as well, but it requires some additional work from apps. You will need to create UI to help user's select which account they want to use for each action that requires tokens. Alternatively, your app can implement a heuristic to select which account to use via the getAccounts(...) method.

Test your app

Run locally

If you have followed the code above, try to build and deploy the app to a test device or emulator. You should be able to sign in and get tokens for Azure AD or personal Microsoft accounts! After a user signing in, this app will display the data returned from the Microsoft Graph /me endpoint.

If you have any issues, feel free to open an issue on this doc or in the MSAL library and let us know.

The first time any user signs into your app, they will be prompted by Microsoft identity to consent to the permissions requested. While most users are capable of consenting, some Azure AD tenants have disabled user consent - requiring admins to consent on behalf of all users. To support this scenario, be sure to register your app's scopes in the Azure portal.

Help and Support

Had any trouble with this tutorial or with the Microsoft identity platform? See Help and support