Using MS Graph and MSAL to fetch MS Teams data in Java (or .NET)
Microsoft continuously updates the MS Graph library, and now also released the MSAL library (as a replacement for the older ADAL library) for authenticating against AAD. Read here how to combine latest version of both libraries to make MS Graph requests and get information about your MS Teams installation from a back-end worker application.
Introduction
System integrators, such as ourselves, often end up writing back-end worker applications (inter)connecting different APIs and exchange data. One of these APIs that is particulary interesting is the MS Graph API, providing access to all information in your Microsoft 365 environment (this includes Office365). Access to this API is controlled via the Microsoft Identity Platform that is part of Azure, which has been updated to v2. To know more about back-end applications vs. user type of applications, see this article.

Though all of these APIs are accessible via raw HTTP requests (REST/JSON), Microsoft also provides helpfull SDKs to make life of the developer easier, develop applications faster, and for almost all popular programming languages. In this article we'll use Java, but, most of the SDKs are similar in usage, so this article equally applies for .NET and other languages as well.

To access the MS Identity Platform and get tokens for your application, you can use the latest SDK available is known as the MSAL (Microsoft Access) library, the replacement of the older ADAL (Active Directory Access Library). However, at the time of writing only very little examples are available covering both technologies (especially in Java), and, the problems that come with it.

Register your application
Before you can access MS Graph using a back-end worker application (a windows service or Linux deamon), or any other type of application for that matter, you must get a valid access token. To get this token, you must first register this application in Azure Active Directory so it gets and ID and you can set permissions for the app.

Note: this step is required whether you use MSAL or ADAL, your application must be known in Azure before you can continue.
To register your app in the MS Identity Platform, login to your Azure tenant, go to applicable directory (typically you have only one, which is usually accessible via the menu item Azure Active Directory). Go to the section "App registrations" and create a new application. Provide a meaningfull name and set the supported account types. Since we will use a deamon application, we do not need to set the redirect URI for application. When the registration completes, open the details of the application and in particular make sure to get the "Application (client) ID" and the "Directory (tenant) ID".

Note: if you have the intention to create an MS Teams bot, it is easier to create an Azure Bot Channel registration. During this process, a new application will be registered as well.
Setting application permissions
Once the application is registered, you will have to give it permissions to access the MS Graph API and define the scopes it has access to.
In the application overview, click on your registered application and then click the "View API permissions" button. Select the Microsoft Graph permissions that your application needs. Don't know what you need? No problem, you can easily find out the permissions you need by looking up the function in the MS Graph Reference information. Each function lists the permissions/scopes you need.
Which permissions to choose?
Don't know what permissions you need to select? No problem, you can easily find out the permissions you need by looking up the function in the MS Graph Reference information that you will call in your application. Each function lists the permissions/scopes you need. For example, if you want to list all users in your directory, you can find the below information that lists all the reaquired permissions that an application needs.
Granting consent
Once the permissions are set, the administrator has to give consent for this application to use that particular set of permissions. In most articles you'll find an explanation this has to be done by calling a specific URL, however, in the latest update of the Azure administration information, this process can be launced directly from within the interface.
Ready for development
When all of the above steps completed succesfully, we have everything ready to start building our application and use the MS Graph API. Calling the MS Graph API, whether it is via the HTTP JSON/REST directory or via the SDKs is a two step process:
- Authentication/authorization against the MS Identity Platform for your application. This results in an access token.
- Call the MS Graph function, providing the access token, and process the result.

Including the JAVA SDKs
Our application uses Gradle, but if you use Maven you probably have no problem to translate the below lines to your situation. We include two libraries. The first is the MSAL SDK for Java, the second is the MS Graph SDK for Java. As mentioned before, the SDKs provide an easy library (object model) to access the APIs and avoid you have to implement the REST API yourself.

Note: we use the 1.+ version, but, this might not be advised in production as MS upgrades the libraries quite often and there are sometimes breaking changes in the different releases. Therefore it is advised that when your development is finished, you freeze the version at that time.
Creating a ConfidentialClientApplication instance

The first step is preparing for authentication. To do this, we'll use the ConfidentialClientApplication class of the MSAL SDK. More information about this can be found here, but, in short a confidential client application simply means you "trust" where the application is running and you have the option to safely store credentials for that application. In case of Windows service or Linux deamon, this should be the case (else, your server has been compromised). We use the fluent builder to create this object.

Some important remarks / things I figured out from different sources (MSDN, SO, ...):
- In general, you'll find the URL to use is with the "common" part in it, like in my commented lines. This didn't work for me and results in an Authorization_IdentityNotFound exception when calling MS Graph. The solution is to replace the common part of the URL with the tenant ID of your registered application (see above)
- Admittedly, it might be better to have the whole URL from the configuration
- Keep your application object alive! If you read the documentation, you'll find out that tokens are cached. For other types of applications you have to cache tokens yourself, but, for a confidential client application with fixed client credentials, the application object itself will cache the token.

When the application object is built, you can call the acquireToken functions to get a new access token. However, in our scenario we do this later on in the application.
String clientId = config.getString("callmanager.teams.clientid");
String clientSecret = config.getString("callmanager.teams.clientsecret");
String tenantId = config.getString("callmanager.teams.tenantid");

app = ConfidentialClientApplication
          .builder(clientId, ClientCredentialFactory.createFromSecret(clientSecret))
          //This first line leads to unkown application for tenant exception, use tenant ID !!
          //.authority("https://login.microsoftonline.com/common/oauth2/token") 
          .authority("https://login.microsoftonline.com/" + tenantId + "/oauth2/token")
          .build();
Creating a GraphServiceClient instance

Second step is to use the MS Graph SDK to create a GraphServiceClient instance (the object returned by the builder is actually an interface IGraphServiceClient). The most confusing part was the authentication provider, which expects an instance IAuthenticationProvider and which I assume would be provided by the MSAL library in some way or another.
graphClient = GraphServiceClient
          .builder()
          .authenticationProvider(this)
          .buildClient();
The IAuthenticationnProvider interface

Eventually, after going through some (equivalent .NET) examples, the IAuthenticationProvider is a simple interface with one function (called authenticateRequest) that the graphclient calls each times it makes a request, so the most easy way to get this is to implement the interface in one of your classes that holds these objects (typically "this"). This one function allows you to set the token for the request you are about to make. As you will know by now, we'll fetch token using our ConfidentialClientApplication instance.

Note: In hindsight, this makes sense since the two SDKs, MSAL en MS Graph, are "independent" from each other. The user can choose the authentication library he pleases, and even more, the MS Graph SDK was released before the latest MSAL SDK.

When fetching an access token, one must always provides the scope(s) for which he wants access. In a user client application where the user has to sign in, this is used (amongst other things) to ask the user to give consent to the application for these particular pieces of information about himself. Since we have a back-end application no user is involved and our permissions are already set in the identity platform. The scope .default can be used then to get an access token that contains all permissions that were granted for this application (see above). Though theoretically the scopes your application needs could differ from the ones that have been granted in Azure, this makes most of the time not much sense. Either way, the scopes you request must always then be a subset of the granted ones, else your token request will fail.
  // This scope definition is used when fetching an access token and means the token should contain
  // all permissions that are enabled for this application
  final static String GRAPH_DEFAULT_SCOPE = "https://graph.microsoft.com/.default";

  /*
   * The authenticatRequest function is part of the IAuthenticationProvider interface that is passed on to the graph client 
   * instance. This will be called each time a request is made to the graph api.
   * Note that the ConfidentialClientApplication object will cache the tokens itself, so we do not have to do it (it doesn't 
   * make sense to request a new token for each request)
   */
  @Override
  public void authenticateRequest(IHttpRequest request)
  {
    // When we ask a token, we must provide the scope(s) for which we want this. Our default
    // scope will return all permissions in the token that are set in Azure
    ClientCredentialParameters clientCredentialParam = ClientCredentialParameters
                        .builder(Collections.singleton(GRAPH_DEFAULT_SCOPE))
                        .build();

    // Handle future completion locally
    CompletableFuture<IAuthenticationResult> future = app.acquireToken(clientCredentialParam);
    BiConsumer<IAuthenticationResult, Throwable> processAuthResult = (res, ex) -> 
    {
      if (ex != null)
      {
        logger.error("Exception while getting token", ex);
      }
      else
      {
        logger.debug("Fetched token succesfully");
        
        //Add authorization header with bearer token to the graph api request
        request.addHeader("Authorization", "Bearer " + res.accessToken());
      }
    };
    future.whenCompleteAsync(processAuthResult);
    future.join();
  }
Calling the MS Graph API functions

When all of this has been set up, we can use the IGraphServiceClient interface to fetch the information about our users, teams, etc... for example, suppose we want to get a list of all users defined in Office 365 (and there IDs, because you'll definitly need this later on to work with teams, e.g. to get the presence of a user). The most simple statement to fetch this list is shown below, but, when you got this point, the SDKs will be pretty trivial to use!
IUserCollectionPage users = graphClient.users().buildRequest().get();

...

//Note: this requires teams permissions !!
ITeamCollectionPage teams = graphClient.teams().buildRequest().get();
Written By Wim Van Houts, January 2020
contact wim.van.houts@xqting.be