Click here to Skip to main content
65,938 articles
CodeProject is changing. Read more.
Articles
(untagged)

Creating an MSAL Authentication Provider

0.00/5 (No votes)
17 Nov 2021 1  
In this article, we’ll create an authentication provider and look at how to use the Graph API client from a Spring Boot web application.
Here we’ll create an authentication provider that lets the Graph SDK that lets our Java app call the Graph API on behalf of the user. This article will focus on creating the authentication provider as part of a Spring Boot web app using the Microsoft Authentication Library for Java and Azure AD.

The Microsoft Graph API provides access to a rich collection of data relating to users and their documents, emails, contacts, meetings, and much more. All of this data is made available through an HTTP API and the JSON objects it returns. But parsing raw JSON is error-prone. Plus, it often involves tedious traversals through maps — creating classes to represent the returned objects would be incredibly time-consuming.

In our first series, we created a basic Graph application that read the signed-in user’s calendar data. There, we called the Graph API directly to keep our introduction simple. But when we’re building more complex Graph applications, we’d much rather use a refined, type-checked method to access the API.

Fortunately, Microsoft provides typed libraries with class mapping to the entities exposed by the Graph API. Using libraries like those in the Microsoft Graph SDK for Java allows our app to navigate the Graph API in a type-safe and IDE-friendly manner.

First, though, we’ll need to create an authentication provider that lets our Java app call the Graph API on behalf of the user. So, in this article, we’ll create an authentication provider and look at how to use the Graph API client from a Spring Boot web application.

Add the Graph Client Dependency

The first step is to add the Graph client dependency. This is provided through the Microsoft Graph Maven dependency.

XML
<dependency>
<groupId>com.microsoft.graph</groupId>
<artifactId>microsoft-graph</artifactId>
<version>5.4.0</version>
</dependency>

Query the Graph API

To use the Graph API client, we call GraphServiceClient, which exposes a builder interface to all the available endpoints. Here, we call the me endpoint, which returns information about the currently logged-in user:

Java
GraphServiceClient.builder
        .authenticationProvider (new SomeAuthenticationProviderGoesHere)
        .buildClient
        .me
        .buildRequest
        .get;

Note that we’ve used a placeholder class when calling the authenticationProvider method.

To call the Graph API, we need to pass a valid access token. This token is generated by an implementation of the IAuthenticationProvider interface and passed to the Graph client via the authenticationProvider method. The SDK provides many such implementations out of the box but doesn’t provide an implementation for Java applications requiring an on-behalf-of (OBO) token typically generated by an OAuth2 resource server.

Fortunately, we can create our own authentication provider.

Generate OBO Tokens

An OBO token is generated by passing the JSON Web Token (JWT) received by a resource server to an OAuth2 authorization server as part of a specially formatted HTTP request. The Microsoft documentation provides a detailed explanation of the HTTP request.

In addition to raw HTTP calls, the OBO flow can be completed using the classes available in the MSAL library. We can see an example of the OBO flow implemented in the AADOBOOAuth2AuthorizedClientProvider class. Using this existing code as inspiration, we can generate our own authentication provider called OboAuthenticationProvider. To follow along, have a look at the complete code for this class, in the following package:

Java
package com.matthewcasperson.onenotebackend.providers;

Let’s break this code down.

Our class extends the BaseAuthenticationProvider class. This in turn implements the IAuthenticationProvider interface.

Java
public class OboAuthenticationProvider extends BaseAuthenticationProvider {

We require details of the Azure AD application to complete the request, including the client ID, client secret, tenant ID, and the scopes we’re requesting access to:

Java
private final String tenantId;
private final String clientId;
private final String clientSecret;
private final Set<String> scopes;

public OboAuthenticationProvider(
    final Set<String> scopes,
    final String tenantId,
    final String clientId,
    final String clientSecret) {
  this.scopes = scopes;
  this.tenantId = tenantId;
  this.clientId = clientId;
  this.clientSecret = clientSecret;
}

The getAuthorizationTokenAsync method returns a future containing null if no token is required for the supplied URL or if the OBO token returned from the authorization server:

Java
@Nonnull
@Override
public CompletableFuture<String> getAuthorizationTokenAsync(@Nonnull final URL url) {

Not all requests require a token. The shouldAuthenticateRequestWithUrl method will return false if the requested URL doesn’t require a token. In this case, we return null to the caller.

Java
if (!shouldAuthenticateRequestWithUrl(url)) {
  return CompletableFuture.completedFuture(null);
}

For requests that do require a token, we start by reading the access token that was passed to the resource server by the caller.

Java
final AbstractOAuth2TokenAuthenticationToken oauth2Token =
    (AbstractOAuth2TokenAuthenticationToken) SecurityContextHolder
        .getContext()
        .getAuthentication();

final String accessToken = oauth2Token.getToken().getTokenValue();

The parameters to pass to the authorization server are captured by the OnBehalfOfParameters class:

Java
final OnBehalfOfParameters parameters = OnBehalfOfParameters
    .builder(scopes, new UserAssertion(accessToken))
    .build();

These parameters are then used to acquire a token from a ConfidentialClientApplication, and the resulting token is returned to the caller:

Java
  return createApp()
      .map(a -> a.acquireToken(parameters).thenApply(IAuthenticationResult::accessToken))
      .orElse(CompletableFuture.failedFuture(new Exception("Failed to generate obo token.")));
}

We create a ConfidentialClientApplication with the createApp method.

Java
private Optional<ConfidentialClientApplication> createApp() {

The client requires an authority value — which is based on the tenant ID — as well as the client secret:

Java
final String authority = "https://login.microsoftonline.com/" + tenantId;
final IClientSecret clientCredential = ClientCredentialFactory.createFromSecret(clientSecret);

We then create a ConfidentialClientApplication through the builder interface.

Java
try {
      return Optional
          .of(ConfidentialClientApplication.builder(clientId, clientCredential)
              .authority(authority)
              .build());
    } catch (final MalformedURLException e) {
      LOGGER.error("Failed to create ConfidentialClientApplication", e);
    }
    return Optional.empty();
  }

Access the Azure AD Application Details

To create a new instance of the OboAuthenticationProvider class, we need access to the Azure AD application client ID, client secret, and tenant ID.

In a typical Azure AD-enabled Spring application, these values are defined in the application.properties file or application.yml file. Here’s an example of an application.yml:

azure:
  activedirectory:
    client-id: ${CLIENT_ID}
    client-secret: ${CLIENT_SECRET}
    app-id-uri: ${APP_URI}
    tenant-id: ${TENANT_ID}

We can inject an instance of AADAuthenticationProperties into our Spring beans to access these properties:

Java
@Autowired
AADAuthenticationProperties azureAd;

We then use the AADAuthenticationProperties getters to access the required properties, allowing us to create a new instance of the OboAuthenticationProvider class.

Java
GraphServiceClient.builder()
        .authenticationProvider(new OboAuthenticationProvider(
            Set.of("https://graph.microsoft.com/user.read"),
            azureAd.getTenantId(),
            azureAd.getClientId(),
            azureAd.getClientSecret()))
        .buildClient()
        .me()
        .buildRequest()
        .get();

And with that, we now can query the Graph API from our Spring resource server through the Graph API client.

Conclusion

In this article, we learned how to add the Microsoft Graph API client to our Spring application, and how to build an authentication provider that generates OBO tokens from the access token passed in by a caller. This gives us the foundation we need to create resource servers acting as a gateway between our front-end application and the Graph API.

In the second article of this series, we’ll use the Graph API client to consume OneNote documents through a microservice that allows them to be converted into Markdown format.

License

This article has no explicit license attached to it but may contain usage terms in the article text or the download files themselves. If in doubt please contact the author via the discussion board below.

A list of licenses authors might use can be found here