Azure AD Authentication for a Java REST API Resource Server

There is a good supply of articles explaining the authentication flow of OAuth and Azure AD, like the one below, but they all have a similarity in that they end with “and then you call the API” (see black rectangle). But what if you are the developer of that REST API that is being called and you need to check that the caller is authenticated? Well, if you develop on DotNet, Microsoft have provided documentation for how to do it, but if you are a Java developer you’re not that lucky. Microsoft do not provide that level of documentation for Java and the Java community is not very clear on this topic either. So if you are a Java Spring Boot developer writing a REST API that requires an Azure AD access token, this article is for you.

Source: https://docs.microsoft.com/en-us/azure/active-directory/develop/v1-protocols-oauth-code

Resource Server with Spring Boot

Developing a REST API Resource Server with Spring Boot is documented in multiple places, and this is one of the links https://spring.io/guides/tutorials/spring-boot-oauth2/ . It is basically taking advantage of the @RestController annotation to do url routing to the proper java method. Writing a REST API whose APIs are public is very easy, even if you are not a skilled Java developer.

@RestController
public class ItemController {

    @GetMapping("/echo")
    public String echoTestRaw() {
        return "You reached the echo API";
    }
}

But when you search for how to add authentication to your API things are not that easy anymore. The reason for that is that there are plenty of blogs out there but very few of them have the angle of a REST API and OAuth authentication with Azure AD. In fact, most of them are not for Azure AD or a Resource Server and you’ll waste a substantial amount of time on reading them and trying to follow samples – only to discover that you’re on the wrong path.

Authentication of a Spring Boot REST API with Azure AD

So, if you are trying to protect your Java Spring Boot REST API with Azure AD and require that the caller invokes it with a valid “Authentication: Bearer <access_token>” that Azure AD issued for the client, then continue reading.

The sample you should read can be found here https://github.com/Microsoft/azure-spring-boot/tree/master/azure-spring-boot-samples/azure-active-directory-spring-boot-sample . Even though you’ll find that this sample has an Angular.js tiny webapp in the source code, it is how you implement authentication for a REST API. If you copy that sample and remove the Angular.js stuff, you have what you need.

The code that hooks up Azure AD authentication is in security/WebSecurityConfig.java class. The code looks like the below, but I’ve commented out lines you don’t need since the sample is the backend plus  an Angular.js webapp which supports interactive login. A Resource Server do not support logon and only supports validating a token for authentication. THe below example would protect everything under /api url path but keep root url for public access.

@EnableGlobalMethodSecurity(securedEnabled = true, prePostEnabled = true)
public class WebSecurityConfig extends WebSecurityConfigurerAdapter {

    @Autowired
    private AADAuthenticationFilter aadAuthFilter;

    @Override
    protected void configure(HttpSecurity http) throws Exception {

        http.authorizeRequests().antMatchers("/").permitAll();
        http.authorizeRequests().antMatchers("/api/**").authenticated();

        //http.logout().logoutRequestMatcher(new AntPathRequestMatcher("/logout"))
        //    .logoutSuccessUrl("/").deleteCookies("JSESSIONID").invalidateHttpSession(true);
        //http.authorizeRequests().anyRequest().permitAll();
        //http.csrf().csrfTokenRepository(CookieCsrfTokenRepository.withHttpOnlyFalse());

        http.addFilterBefore(aadAuthFilter, UsernamePasswordAuthenticationFilter.class);
    }
}

To complete the hook up for authentication with your Azure AD tenant, you also need to update the application.properties file under src/main/resources so that your API knows what Azure AD application we are using.

# By default, azure.activedirectory.environment property has value `global`,
# supported value is global, cn. Please refer to the README for details.
# azure.activedirectory.environment=global
azure.activedirectory.client-id=03da...c65cb
azure.activedirectory.client-secret=PKWp...sMac=
azure.activedirectory.ActiveDirectoryGroups=basilgroup,sybilgroup

The client-id and client-secret values comes from the App Registration entry you need to do in portal.azure.com (under Azure Active Directory). The client-id is the ApplicationID and the client-secret comes from the Key section where you create a key. The last line in the config file is if you have role based access based on groups.

Seeing is believing

I’ve created a REST API using what I’ve written above and deployed it to Azure. To invoke the API I use Postman and in the first API invokation I use an access token that is old and should be refreshed.

As you can see the reply is an http error saying that the JWT token has expired.

If I generate a new access token and use that one instead, I can successfully call the REST API. On the backend you can see the trace logging the call as authenticated and in Postman you see the json response of the API call.

Finally, since we only protect url paths starting with /api, we should be able to call the /echo method (not the /api/echo which is protected). And sure enough, I can

The Magic Sauce

At this point you must be saying to yourself – this is to easy, where is the magic happening that actually validates the access token? The code performing the magic resides in the azure-spring-boot and you need to add the below dependency in the pom.xml file (assuming Maven)

<dependency>
   <groupId>com.microsoft.azure</groupId>
   <artifactId>azure-active-directory-spring-boot-starter</artifactId>
   <version>2.0.7</version>
</dependency>

The code behind this magic can be found here in the github repo https://github.com/Microsoft/azure-spring-boot/tree/master/azure-spring-boot/src/main/java/com/microsoft/azure/spring/autoconfigure/aad

References

github source code for my REST API
https://github.com/cljung/java-rest-api