Spring Boot Starter for Microsoft Entra developer's guide

This article applies to: ✅ Version 4.19.0 ✅ Version 5.19.0

This article describes the features and core scenarios of the Spring Boot Starter for Microsoft Entra ID. The article also includes guidance on common issues, workarounds, and diagnostic steps.

When you're building a web application, identity and access management are foundational pieces. Azure offers a cloud-based identity service that has deep integration with the rest of the Azure ecosystem.

Although Spring Security makes it easy to secure your Spring-based applications, it isn't tailored to a specific identity provider. The Spring Boot Starter for Microsoft Entra ID enables you to connect your web application to a Microsoft Entra tenant and protect your resource server with Microsoft Entra ID. It uses the Oauth 2.0 protocol to protect web applications and resource servers.

The following links provide access to the starter package, documentation, and samples:

Prerequisites

To follow the instructions in this guide, you must have the following prerequisites:

Important

Spring Boot version 2.5 or higher is required to complete the steps in this article.

Core scenarios

This guide describes how to use the Microsoft Entra starter in the following scenarios:

A web application is any web-based application that enables a user to sign in. A resource server will either accept or deny access after validating an access token.

Access a web application

This scenario uses the The OAuth 2.0 authorization code grant flow to enable a user to sign in with a Microsoft account.

To use the Microsoft Entra starter in this scenario, use the following steps:

Set the redirect URI to <application-base-uri>/login/oauth2/code/. For example: http://localhost:8080/login/oauth2/code/. Be sure to include the trailing /. For more information about the redirect URI, see Add a redirect URI in Quickstart: Register an application with the Microsoft identity platform.

Screenshot of Azure portal showing web app authentication page with redirect URI highlighted.

Add the following dependencies to your pom.xml file.

<dependency>
   <groupId>com.azure.spring</groupId>
   <artifactId>spring-cloud-azure-starter-active-directory</artifactId>
</dependency>
<dependency>
   <groupId>org.springframework.boot</groupId>
   <artifactId>spring-boot-starter-oauth2-client</artifactId>
</dependency>

Note

For more information about how to manage Spring Cloud Azure library versions by using a bill of materials (BOM), see the Getting started section of the Spring Cloud Azure developer guide.

Add the following properties to your application.yml file. You can get the values for these properties from the app registration you created in the Azure portal, as described in the prerequisites.

spring:
 cloud:
   azure:
     active-directory:
       enabled: true
       profile:
         tenant-id: <tenant>
       credential:
         client-id: <your-client-ID>
         client-secret: <your-client-secret>

Note

The values allowed for tenant-id are: common, organizations, consumers, or the tenant ID. For more information about these values, see the Used the wrong endpoint (personal and organization accounts) section of Error AADSTS50020 - User account from identity provider does not exist in tenant. For information on converting your single-tenant app, see Convert single-tenant app to multitenant on Microsoft Entra ID.

Use the default security configuration or provide your own configuration.

Option 1: Use the default configuration.

With this option, you don't need to do anything. The DefaultAadWebSecurityConfiguration class is configured automatically.

Option 2: Provide a self-defined configuration.

To provide a configuration, apply the AadWebApplicationHttpSecurityConfigurer#aadWebApplication method for the HttpSecurity, as shown in the following example:

@Configuration(proxyBeanMethods = false)
@EnableWebSecurity
@EnableMethodSecurity
public class AadOAuth2LoginSecurityConfig {

   /**
    * Add configuration logic as needed.
    */
   @Bean
   SecurityFilterChain filterChain(HttpSecurity http) throws Exception {
       http.apply(AadWebApplicationHttpSecurityConfigurer.aadWebApplication())
               .and()
           .authorizeHttpRequests()
               .anyRequest().authenticated();
           // Do some custom configuration.
       return http.build();
   }
}

Access resource servers from a web application

To use the Microsoft Entra starter in this scenario, use the following steps:

Set the redirect URI as described previously.

Add the following dependencies to your pom.xml file.

<dependency>
   <groupId>com.azure.spring</groupId>
   <artifactId>spring-cloud-azure-starter-active-directory</artifactId>
</dependency>
<dependency>
   <groupId>org.springframework.boot</groupId>
   <artifactId>spring-boot-starter-oauth2-client</artifactId>
</dependency>

Note

For more information about how to manage Spring Cloud Azure library versions by using a bill of materials (BOM), see the Getting started section of the Spring Cloud Azure developer guide.

Add the following properties to your application.yml file, as described previously:

spring:
 cloud:
   azure:
     active-directory:
       enabled: true
       profile:
         tenant-id: <tenant>
       credential:
         client-id: <your-client-ID>
         client-secret: <your-client-secret>
       authorization-clients:
         graph:
           scopes: https://graph.microsoft.com/Analytics.Read, email

Note

The values allowed for tenant-id are: common, organizations, consumers, or the tenant ID. For more information about these values, see the Used the wrong endpoint (personal and organization accounts) section of Error AADSTS50020 - User account from identity provider does not exist in tenant. For information on converting your single-tenant app, see Convert single-tenant app to multitenant on Microsoft Entra ID.

Here, graph is the name of your OAuth2AuthorizedClient, and scopes are the scopes needed for consent when logging in.

Add code to your application similar to the following example:

@GetMapping("/graph")
@ResponseBody
public String graph(
   @RegisteredOAuth2AuthorizedClient("graph") OAuth2AuthorizedClient graphClient
) {
   // toJsonString() is just a demo.
   // oAuth2AuthorizedClient contains access_token. We can use this access_token to access the resource server.
   return toJsonString(graphClient);
}

Here, graph is the client ID configured in the previous step. OAuth2AuthorizedClient contains the access token, which is used to access the resource server.

For a complete sample demonstrating this scenario, see spring-cloud-azure-starter-active-directory sample: aad-web-application.

Protect a resource server/API

This scenario doesn't support sign in, but protects the server by validating the access token. If the access token is valid, the server serves the request.

To use the Microsoft Entra starter in this scenario, use the following steps:

Add the following dependencies to your pom.xml file.

<dependency>
   <groupId>com.azure.spring</groupId>
   <artifactId>spring-cloud-azure-starter-active-directory</artifactId>
</dependency>
<dependency>
   <groupId>org.springframework.boot</groupId>
   <artifactId>spring-boot-starter-oauth2-resource-server</artifactId>
</dependency>

Note

For more information about how to manage Spring Cloud Azure library versions by using a bill of materials (BOM), see the Getting started section of the Spring Cloud Azure developer guide.

Add the following properties to your application.yml file, as described previously:

spring:
 cloud:
   azure:
     active-directory:
       enabled: true
       credential:
         client-id: <your-client-ID>
       app-id-uri: <your-app-ID-URI>

You can use both the <your-client-ID> and <your-app-ID-URI> values to verify the access token. You can get the <your-app-ID-URI> value from the Azure portal, as shown in the following images:

Screenshot of Azure portal showing web app Expose an API page with Application ID URI highlighted.

Use the default security configuration or provide your own configuration.

Option 1: Use the default configuration.

With this option, you don't need to do anything. The DefaultAadResourceServerConfiguration class is configured automatically.

Option 2: Provide a self-defined configuration.

To provide a configuration, apply the AadResourceServerHttpSecurityConfigurer#aadResourceServer method for the HttpSecurity, as shown in the following example:

@Configuration(proxyBeanMethods = false)
@EnableWebSecurity
@EnableMethodSecurity
public class AadOAuth2ResourceServerSecurityConfig {

   /**
    * Add configuration logic as needed.
    */
   @Bean
   public SecurityFilterChain apiFilterChain(HttpSecurity http) throws Exception {
       http.apply(AadResourceServerHttpSecurityConfigurer.aadResourceServer())
               .and()
           .authorizeHttpRequests()
               .anyRequest().authenticated();
       return http.build();
   }
}

For a complete sample demonstrating this scenario, see spring-cloud-azure-starter-active-directory sample: aad-resource-server.

Access other resource servers from a resource server

This scenario supports a resource server visiting other resource servers.

To use the Microsoft Entra starter in this scenario, use the following steps:

Add the following dependencies to your pom.xml file.

<dependency>
   <groupId>com.azure.spring</groupId>
   <artifactId>spring-cloud-azure-starter-active-directory</artifactId>
</dependency>
<dependency>
   <groupId>org.springframework.boot</groupId>
   <artifactId>spring-boot-starter-oauth2-resource-server</artifactId>
</dependency>
<dependency>
   <groupId>org.springframework.boot</groupId>
   <artifactId>spring-boot-starter-oauth2-client</artifactId>
</dependency>

Note

For more information about how to manage Spring Cloud Azure library versions by using a bill of materials (BOM), see the Getting started section of the Spring Cloud Azure developer guide.

Add the following properties to your application.yml file:

spring:
 cloud:
   azure:
     active-directory:
       enabled: true
       profile:
         tenant-id: <tenant>
       credential:
         client-id: <web-API-A-client-ID>
         client-secret: <web-API-A-client-secret>
       app-id-uri: <web-API-A-app-ID-URI>
       authorization-clients:
         graph:
           scopes:
              - https://graph.microsoft.com/User.Read

Note

The values allowed for tenant-id are: common, organizations, consumers, or the tenant ID. For more information about these values, see the Used the wrong endpoint (personal and organization accounts) section of Error AADSTS50020 - User account from identity provider does not exist in tenant. For information on converting your single-tenant app, see Convert single-tenant app to multitenant on Microsoft Entra ID.

Use the @RegisteredOAuth2AuthorizedClient attribute in your code to access the related resource server, as shown in the following example:

@PreAuthorize("hasAuthority('SCOPE_Obo.Graph.Read')")
@GetMapping("call-graph")
public String callGraph(@RegisteredOAuth2AuthorizedClient("graph") OAuth2AuthorizedClient graph) {
   return callMicrosoftGraphMeEndpoint(graph);
}

For a complete sample demonstrating this scenario, see spring-cloud-azure-starter-active-directory sample: aad-resource-server-obo.

Web application and resource server in one application

This scenario supports Access a web application and Protect a resource server/API in one application.

To use aad-starter in this scenario, follow these steps:

Add the following dependencies to your pom.xml file.

<dependency>
   <groupId>com.azure.spring</groupId>
   <artifactId>spring-cloud-azure-starter-active-directory</artifactId>
</dependency>
<dependency>
   <groupId>org.springframework.boot</groupId>
   <artifactId>spring-boot-starter-oauth2-resource-server</artifactId>
</dependency>
<dependency>
   <groupId>org.springframework.boot</groupId>
   <artifactId>spring-boot-starter-oauth2-client</artifactId>
</dependency>

Note

For more information about how to manage Spring Cloud Azure library versions by using a bill of materials (BOM), see the Getting started section of the Spring Cloud Azure developer guide.

Update your application.yml file. Set property spring.cloud.azure.active-directory.application-type to web_application_and_resource_server, and specify the authorization type for each authorization client, as shown in the following example.

spring:
 cloud:
   azure:
     active-directory:
       enabled: true
       profile:
         tenant-id: <tenant>
       credential:
         client-id: <Web-API-C-client-id>
         client-secret: <Web-API-C-client-secret>
       app-id-uri: <Web-API-C-app-id-url>
       application-type: web_application_and_resource_server  # This is required.
       authorization-clients:
         graph:
           authorizationGrantType: authorization_code  # This is required.
           scopes:
             - https://graph.microsoft.com/User.Read
             - https://graph.microsoft.com/Directory.Read.All

Note

The values allowed for tenant-id are: common, organizations, consumers, or the tenant ID. For more information about these values, see the Used the wrong endpoint (personal and organization accounts) section of Error AADSTS50020 - User account from identity provider does not exist in tenant. For information on converting your single-tenant app, see Convert single-tenant app to multitenant on Microsoft Entra ID.

Write Java code to configure multiple HttpSecurity instances.

In the following example code, AadWebApplicationAndResourceServerConfig contains two security filter chain beans, one for a resource server, and one for a web application. The apiFilterChain bean has a high priority to configure the resource server security builder. The htmlFilterChain bean has a low priority to configure the web application security builder.

@Configuration(proxyBeanMethods = false)
@EnableWebSecurity
@EnableMethodSecurity
public class AadWebApplicationAndResourceServerConfig {

    @Bean
    @Order(1)
    public SecurityFilterChain apiFilterChain(HttpSecurity http) throws Exception {
        http.apply(AadResourceServerHttpSecurityConfigurer.aadResourceServer())
                .and()
            // All the paths that match `/api/**`(configurable) work as the resource server. Other paths work as the web application.
            .securityMatcher("/api/**")
            .authorizeHttpRequests()
                .anyRequest().authenticated();
        return http.build();
    }

    @Bean
    public SecurityFilterChain htmlFilterChain(HttpSecurity http) throws Exception {
        // @formatter:off
        http.apply(AadWebApplicationHttpSecurityConfigurer.aadWebApplication())
                .and()
            .authorizeHttpRequests()
                .requestMatchers("/login").permitAll()
                .anyRequest().authenticated();
        // @formatter:on
        return http.build();
    }
}

Application type

The spring.cloud.azure.active-directory.application-type property is optional because its value can be inferred by dependencies. You must manually set the property only when you use the web_application_and_resource_server value.

Has dependency: spring-security-oauth2-client Has dependency: spring-security-oauth2-resource-server Valid values of application type Default value
Yes No web_application web_application
No Yes resource_server resource_server
Yes Yes web_application,resource_server,
resource_server_with_obo, web_application_and_resource_server
resource_server_with_obo

Configurable properties

The Spring Boot Starter for Microsoft Entra ID provides the following properties:

Properties Description
spring.cloud.azure.active-directory.app-id-uri Used by the resource server to validate the audience in the access token. The access token is valid only when the audience is equal to the <your-client-ID> or <your-app-ID-URI> values described previously.
spring.cloud.azure.active-directory.authorization-clients A map that configures the resource APIs the application is going to visit. Each item corresponds to one resource API the application is going to visit. In your Spring code, each item corresponds to one OAuth2AuthorizedClient object.
spring.cloud.azure.active-directory.authorization-clients.<your-client-name>.scopes The API permissions of a resource server that the application is going to acquire.
spring.cloud.azure.active-directory.authorization-clients.<your-client-name>.authorization-grant-type The type of authorization client. Supported types are authorization_code (default type for webapp), on_behalf_of (default type for resource-server), client_credentials.
spring.cloud.azure.active-directory.application-type Refer to Application type.
spring.cloud.azure.active-directory.profile.environment.active-directory-endpoint The base URI for the authorization server. The default value is https://login.microsoftonline.com/.
spring.cloud.azure.active-directory.credential.client-id The registered application ID in Microsoft Entra ID.
spring.cloud.azure.active-directory.credential.client-secret The client secret of the registered application.
spring.cloud.azure.active-directory.user-group.use-transitive-members Use v1.0/me/transitiveMemberOf to get groups if set to true. Otherwise, use /v1.0/me/memberOf.
spring.cloud.azure.active-directory.post-logout-redirect-uri The redirect URI for posting the sign-out.
spring.cloud.azure.active-directory.profile.tenant-id The Azure tenant ID. The values allowed for tenant-id are: common, organizations, consumers, or the tenant ID.
spring.cloud.azure.active-directory.user-group.allowed-group-names The expected user groups that an authority will be granted to if found in the response from the MemberOf Graph API call.
spring.cloud.azure.active-directory.user-name-attribute Indicates which claim will be the principal's name.

The following examples show you how to use these properties:

Property example 1: To use Azure China 21Vianet instead of Azure Global, use the following step.

  • Add the following properties to your application.yml file:

    spring:
       cloud:
         azure:
           active-directory:
             enabled: true
             profile:
               environment:
                 active-directory-endpoint: https://login.partner.microsoftonline.cn
    

With this method, you can use an Azure sovereign or national cloud instead of the Azure public cloud.

Property example 2: To use a group name to protect some method in a web application, use the following steps:

Add the following property to your application.yml file:

spring:
 cloud:
   azure:
     active-directory:
       enabled: true
       user-group:
         allowed-groups: group1, group2

Use the default security configuration or provide your own configuration.

Option 1: Use the default configuration. With this option, you don't need to do anything. The DefaultAadWebSecurityConfiguration class is configured automatically.

Option 2: Provide a self-defined configuration. To provide a configuration, apply the AadWebApplicationHttpSecurityConfigurer#aadWebApplication method for the HttpSecurity, as shown in the following example:

@Configuration(proxyBeanMethods = false)
@EnableWebSecurity
@EnableMethodSecurity
public class AadOAuth2LoginSecurityConfig {

   /**
    * Add configuration logic as needed.
    */
   @Bean
   public SecurityFilterChain htmlFilterChain(HttpSecurity http) throws Exception {
       // @formatter:off
       http.apply(AadWebApplicationHttpSecurityConfigurer.aadWebApplication())
               .and()
           .authorizeHttpRequests()
               .anyRequest().authenticated();
       // @formatter:on
       // Do some custom configuration.
       return http.build();
   }
}

Use the @PreAuthorize annotation to protect the method, as shown in the following example:

@Controller
public class RoleController {
   @GetMapping("group1")
   @ResponseBody
   @PreAuthorize("hasRole('ROLE_group1')")
   public String group1() {
       return "group1 message";
   }

   @GetMapping("group2")
   @ResponseBody
   @PreAuthorize("hasRole('ROLE_group2')")
   public String group2() {
       return "group2 message";
   }

   @GetMapping("group1Id")
   @ResponseBody
   @PreAuthorize("hasRole('ROLE_<group1-id>')")
   public String group1Id() {
       return "group1Id message";
   }

   @GetMapping("group2Id")
   @ResponseBody
   @PreAuthorize("hasRole('ROLE_<group2-id>')")
   public String group2Id() {
       return "group2Id message";
   }
}

Property example 3: To enable client credential flow in a resource server visiting resource servers, use the following steps:

Add the following property to your application.yml file:

spring:
 cloud:
   azure:
     active-directory:
       enabled: true
       authorization-clients:
         webapiC:   # When authorization-grant-type is null, on behalf of flow is used by default
           authorization-grant-type: client_credentials
           scopes:
             - <Web-API-C-app-id-url>/.default

Add code to your application similar to the following example:

@PreAuthorize("hasAuthority('SCOPE_Obo.WebApiA.ExampleScope')")
@GetMapping("webapiA/webapiC")
public String callClientCredential() {
   String body = webClient
       .get()
       .uri(CUSTOM_LOCAL_READ_ENDPOINT)
       .attributes(clientRegistrationId("webapiC"))
       .retrieve()
       .bodyToMono(String.class)
       .block();
   LOGGER.info("Response from Client Credential: {}", body);
   return "client Credential response " + (null != body ? "success." : "failed.");
}

Advanced features

Support access control by ID token in a web application

The starter supports creating GrantedAuthority from an ID token's roles claim to allow using the ID token for authorization in a web application. You can use the appRoles feature of Microsoft Entra ID to create a roles claim and implement access control.

Note

The roles claim generated from appRoles is decorated with prefix APPROLE_.

When using appRoles as a roles claim, avoid configuring a group attribute as roles at the same time. Otherwise, the group attribute will override the claim to contain group information instead of appRoles. You should avoid the following configuration in your manifest:

"optionalClaims": {
    "idtoken": [{
        "name": "groups",
        "additionalProperties": ["emit_as_roles"]
    }]
}

To support access control by ID token in a web application, use the following steps:

Add app roles in your application and assign them to users or groups. For more information, see How to: Add app roles to your application and receive them in the token.

Add the following appRoles configuration to your application's manifest:

 "appRoles": [
   {
     "allowedMemberTypes": [
       "User"
     ],
     "displayName": "Admin",
     "id": "2fa848d0-8054-4e11-8c73-7af5f1171001",
     "isEnabled": true,
     "description": "Full admin access",
     "value": "Admin"
    }
 ]

Add code to your application similar to the following example:

@GetMapping("Admin")
@ResponseBody
@PreAuthorize("hasAuthority('APPROLE_Admin')")
public String Admin() {
   return "Admin message";
}

Troubleshooting

Enable client logging

The Azure SDKs for Java offer a consistent logging story to help troubleshoot and resolve application errors. The logs produced will capture the flow of an application before reaching the terminal, helping to locate the root issue. View the logging wiki for guidance on enabling logging.

Enable Spring logging

Spring enables all the supported logging systems to set logger levels in the Spring environment (for example, in application.properties) by using logging.level.<logger-name>=<level> where level is one of TRACE, DEBUG, INFO, WARN, ERROR, FATAL, or OFF. You can configure the root logger by using logging.level.root.

The following example shows potential logging settings in the application.properties file:

logging.level.root=WARN
logging.level.org.springframework.web=DEBUG
logging.level.org.hibernate=ERROR

For more information about logging configuration in Spring, see Logging in the Spring documentation.

Next steps

To learn more about Spring and Azure, continue to the Spring on Azure documentation center.