How to enable MSAL for Java (MSAL4J) logging in a Spring Boot application

In this blog, I’ll show how to enable MSAL4J logging using the logback framework in a spring boot web application. I’ll use our Azure AD B2C web sample here. The complete code for this blog is on github. Refer to the MSAL for Java logging documentation for more info. There are 3 main things you need to do for logging to work

1) Include the logback package in the pom.xml file

		<dependency>
			<groupId>ch.qos.logback</groupId>
			<artifactId>logback-classic</artifactId>
			<version>1.2.3</version>
		</dependency>

2) Add a file called ‘logback.xml’ with the following content to the resources folder.

This configuration (aka appender) logs to the console. There are other example appenders here you can use as well. You can change the logging level to a different level (error, warning, info, verbose) in your application since debug level is quite chatty.

<?xml version = "1.0" encoding = "UTF-8"?>
<configuration>
    <appender name="STDOUT" class="ch.qos.logback.core.ConsoleAppender">
        <encoder>
            <pattern>%d{HH:mm:ss.SSS} [%thread] %-5level %logger{36} - %msg%n</pattern>
        </encoder>
    </appender>
    <root level="debug">
        <appender-ref ref="STDOUT" />
    </root>
</configuration>

3) Set the logging.config property to the location of the logback.xml file before the main method:

@SpringBootApplication
public class MsalB2CWebSampleApplication {

	static { System.setProperty("logging.config", "C:\\Users\\<your path>\\src\\main\\resources\\logback.xml");}
	public static void main(String[] args) {
		// Console.log("main");
		// System.console().printf("hello");
		// System.out.printf("Hello %s!%n", "World");
		System.out.printf("%s%n", "Hello World");
		SpringApplication.run(MsalB2CWebSampleApplication.class, args);
	}
}

HTTPS support

The sample uses https. I follow step 2 from ‘Configure the Web App‘ section to generate a self-signed certificate and place the keystore.p12 file in the resources folder.

App Registration

Make sure you have 2 different app registrations in your Azure AD B2C tenant. One for the web app and one for the web API. Expose the scope in the web API (refer to this documentation if you are not familiar with how to expose web API scope) and configure the web API scope in the ‘API Permission’ blade for the web app. You should also grant admin consent to all the configured permission in the web app. You can also follow this tutorial for app registrations covered in this blog. Below is my set up:

Below is how logging should look like when done correctly:

Azure AD-protected Web API using Spring Boot Starter for Azure Active Directory

In this blog post, I will demonstrate a simple Azure AD-protected Web API sample using Spring Boot Starter for Azure Active Directory. If you are not familiar with Spring Boot Starter for Azure Active Directory, please take a look at azure-sdk-for-java/sdk/spring/azure-spring-boot-starter-active-directory at main · Azure/azure-sdk-for-java (github.com) and the Azure AD Spring Developer’s Guide.

Requirement: You must have a Web API Application registered in Azure Active Directory and expose its permission scope (Delegated and/or Application). To understand the difference between Delegated and Application permission refer to difference between application permission and delegated permission | Azure Active Directory Developer Support Team (aaddevsup.xyz).

Below is my sample Web API app registration. In the overview blade, take note of both the Application (client) ID and the Directory (tenant) ID fields as you will need these to configure in the application.properties file.

In this sample Web API, I’ll expose both types of permission. For delegated permission, use the ‘Expose an API’ blade. Set the App ID URI field if the value has not been set already. For my web API, I create a custom scope named ‘hello’. The fully qualified scope name is in this format: {App ID URI}/{scope name}. Take note of both the App ID URI and the scope name as we will need these for the project.

For application permission, I use the ‘App roles’ blade to create a permission scope named ‘app_hello’ with the allowed member types to be Applications.

For testing purposes, I have another client app registered and configure the ‘API permissions’ blade with this Web API’s delegated and application permissions. Note: you need to grant admin consent to these permissions before requesting an access token.

Configuring the Spring Boot Web API project

The complete source code for this project is located here. The sample uses maven and Java version 11 as indicated in the pom.xml file. For more info on how this sample code was constructed refer to our Azure AD Spring Boot Web API sample at azure-sdk-for-java/sdk/spring/azure-spring-boot-samples/azure-spring-boot-sample-active-directory-resource-server at main · Azure/azure-sdk-for-java (github.com). Below are the main things you need to configure for the web API project

application.properties file

Put your Application Registration info (Application ID, Tenant ID, and App ID URI) in this file as below:

azure.activedirectory.client-id=<Web API Application ID>
azure.activedirectory.app-id-uri=<Web API App ID URI>
azure.activedirectory.tenant-id=<tenant ID>

The Controller (Restful) Method(s)

For delegated permission, you need to use ‘SCOPE_<Delegated Permisssion Scope Name>’ in the PreAuthorize’s hasAuthority Annotation, while for application permission, use ‘APPROLE_<Application Permission Scope Name>’ in the PreAuthrorize annotation instead of. I have a few different routes defined in this sample: webAPIA route will only accept Delegated Permission token, webAPIB route will only accept Application Permission token, and webAPIC route will accept either type.

@RestController
public class HomeController {

    @GetMapping("/webapiA")
    @ResponseBody
    @PreAuthorize("hasAuthority('SCOPE_hello')")
    public String fileA() {
        return "Response from WebApiA.";
    }

    @GetMapping("/webapiB")
    @ResponseBody
    @PreAuthorize("hasAuthority('APPROLE_app_hello')")
    public String fileB() {
        return "Response from WebApiB.";
    }
    
    @GetMapping("/webapiC")
    @ResponseBody
    @PreAuthorize("hasAnyAuthority('APPROLE_app_hello','SCOPE_hello')")
    public String fileC() {
        return "Response from WebApiC.";
    }
}

Build and Run the project

I use the following commands to build and run this Spring project, respectively

mvn clean package -DskipTests
mvn spring-boot:run

Testing the web API

To test the web API, I use PostMan’s built-in feature to get a Delegated permission token using Authorization Code Grant flow and then use the token to call the Web API.

Note: For delegated permission token, the permission shows up in the scp claim (decoded using https://jwt.ms)

Sending the request to webAPIA Route:

For Application permission testing, I use PostMan’s built-in feature to get an Application permission token using Client Credentials Grant flow and then use the token to call the Web API.

For Application permission token the permission is in the roles claim

Sending the request to webAPIB Route: