Secure Java Spring Boot apps using roles and role claims

This article demonstrates a Java Spring Boot web app that uses the Microsoft Entra ID Spring Boot Starter client library for Java for authentication, authorization, and token acquisition. The app uses the OpenID Connect protocol to sign in users, and restricts access to some routes using Microsoft Entra ID Application Roles (app roles) for authorization.

App roles, along with security groups, are popular means to implement authorization. Using role-based access control (RBAC) with application roles and role claims, you can securely enforce authorization policies with minimal effort. Another approach is to use Microsoft Entra ID groups and group claims. Microsoft Entra ID groups and application roles aren't mutually exclusive. You can use them both to provide fine-grained access control.

For a video that covers a similar scenario, see Implement authorization in your applications using app roles, security groups, scopes, and directory roles.

For more information about how the protocols work in this scenario and other scenarios, see Authentication vs. authorization.

The following diagram shows the topology of the app:

Diagram that shows the topology of the app.

The app uses the Microsoft Entra ID Spring Boot Starter client library for Java to sign in a user and obtain an ID token from Microsoft Entra ID. The ID token contains the roles claim. The application inspects the value of this claim to determine which pages the user is authorized to access.

This kind of authorization is implemented using RBAC. With RBAC, an administrator grants permissions to roles, not to individual users or groups. The administrator can then assign roles to different users and groups to control who has access to certain content and functionality.

This sample application defines the following two Application Roles:

  • PrivilegedAdmin: Authorized to access the Admins Only and the Regular Users pages.
  • RegularUser: Authorized to access the Regular Users page.

These application roles are defined in the Azure portal in the application's registration manifest. When a user signs into the application, Microsoft Entra ID emits a roles claim for each role granted individually to the user in the form of role membership.

You can assign users and groups to roles through the Azure portal or programmatically using Microsoft Graph and Microsoft Azure AD PowerShell. This article describes both techniques.

Note

Role claims aren't present for guest users in a tenant if the https://login.microsoftonline.com/common/ endpoint is used as the authority to sign in users. You need to sign-in a user to a tenanted endpoint like https://login.microsoftonline.com/tenantid.

Prerequisites

Recommendations

  • Some familiarity with the Spring Framework
  • Some familiarity with Linux/OSX terminal or Windows PowerShell
  • jwt.ms for inspecting your tokens.
  • Fiddler for monitoring your network activity and troubleshooting.
  • Follow the Microsoft Entra ID Blog to stay up-to-date with the latest developments.

Set up the sample

The following sections show you how to set up the sample application.

Clone or download the sample repository

To clone the sample, open a Bash window and use the following command:

git clone https://github.com/Azure-Samples/ms-identity-java-spring-tutorial.git
cd ms-identity-java-spring-tutorial
cd 3-Authorization-II/roles

Alternatively, navigate to the ms-identity-java-spring-tutorial repository, then download it as a .zip file and extract it to your hard drive.

Important

To avoid file path length limitations on Windows, clone or extract the repository into a directory near the root of your hard drive.

Register the sample application with your Azure Active Directory tenant

There's one project in this sample. To register the app on the Azure portal, you can either follow manual configuration steps or use a PowerShell script. The script does the following tasks:

  • Creates the Microsoft Entra ID applications and related objects, such as passwords, permissions, and dependencies.
  • Modifies the project configuration files.

Use the following steps to run the PowerShell script:

  1. On Windows, run PowerShell as administrator and navigate to the root of the cloned directory.

  2. If you're new to Azure AD PowerShell, see App Creation Scripts in the source repository to ensure that your environment is prepared correctly.

  3. Use the following command to set the execution policy for PowerShell:

    Set-ExecutionPolicy -ExecutionPolicy RemoteSigned -Scope Process -Force
    
  4. Use the following commands to run the configuration script:

    cd .\AppCreationScripts\
    .\Configure.ps1
    

    Note

    Other ways of running the scripts are described in App Creation Scripts. The scripts also provide a guide to automated application registration, configuration, and removal, which can help in your CI/CD scenarios.

Configure the app (java-spring-webapp-roles) to use your app registration

Use the following steps to configure the app:

Note

In the following steps, ClientID is the same as Application ID or AppId.

  1. Open the project in your IDE.

  2. Open the src\main\resources\application.yml file.

  3. Find the placeholder Enter_Your_Tenant_ID_Here and replace the existing value with your Microsoft Entra tenant ID.

  4. Find the placeholder Enter_Your_Client_ID_Here and replace the existing value with the application ID or clientId of the java-spring-webapp-roles app copied from the Azure portal.

  5. Find the placeholder Enter_Your_Client_Secret_Here and replace the existing value with the value you saved during the creation of java-spring-webapp-roles copied from the Azure portal.

  6. Open the src/main/java/com/microsoft/azuresamples/msal4j/msidentityspringbootapplication/Sample.Controller.java file.

  7. Find the references to the PrivilegedAdmin and RegularUser app roles in this file. If necessary, change them to reflect the app role names you chose in the previous steps.

Run the sample

The following sections show you how to deploy the sample to Azure Spring Apps.

Prerequisites

Prepare the Spring project

Use the following steps to prepare the project:

  1. Use the following Maven command to build the project:

    mvn clean package
    
  2. Run the sample project locally by using the following command:

    mvn spring-boot:run
    

Configure the Maven plugin

Run the following command in the root of the project to configure the app using the Maven plugin for Azure Spring Apps:

mvn com.microsoft.azure:azure-spring-apps-maven-plugin:1.19.0:config

The following list describes the command interactions:

  • OAuth2 login: You need to authorize the sign-in to Azure based on the OAuth2 protocol.
  • Select subscription: Select the subscription list number where you want to create your Azure Spring Apps instance, which defaults to the first subscription in the list. If you want to use the default number, press Enter.
  • Input the Azure Spring Apps name: Enter the name for the spring apps instance you want to create. If you want to use the default name, press Enter.
  • Input the resource group name: Enter the name for the resource group you want to create your spring apps instance in. If you want to use the default name, press Enter.
  • Skus: Select the SKU you want to use for your spring apps instance. If you want to use the default number, press Enter.
  • Input the app name (demo): Provide an app name. If you want to use the default project artifact ID, press Enter.
  • Runtimes: Select the runtime you want to use for your spring apps instance. In this case, you should use the default number, so press Enter.
  • Expose public access for this app (boot-for-azure): Press y.
  • Confirm to save all the above configurations: Press y. If you press n, the configuration isn't saved in the .pom file.

The following example shows the output of the deployment process:

Summary of properties:
Subscription id   : 12345678-1234-1234-1234-123456789101
Resource group name : rg-ms-identity-spring-boot-webapp
Azure Spring Apps name : cluster-ms-identity-spring-boot-webapp
Runtime Java version : Java 11
Region            : eastus
Sku               : Standard
App name          : ms-identity-spring-boot-webapp
Public access     : true
Instance count/max replicas : 1
CPU count         : 1
Memory size(GB)   : 2
Confirm to save all the above configurations (Y/n):
[INFO] Configurations are saved to: /home/user/ms-identity-java-spring-tutorial/1-Authentication/sign-in/pom.    xml
[INFO] ------------------------------------------------------------------------
[INFO] BUILD SUCCESS
[INFO] ------------------------------------------------------------------------
[INFO] Total time:  01:57 min
[INFO] Finished at: 2024-02-14T13:50:44Z
[INFO] ------------------------------------------------------------------------

After you've confirmed your choices, the plugin adds the required plugin element and settings to your project's pom.xml file to configure your app to run in Azure Spring Apps.

The relevant portion of the pom.xml file should look similar to the following example:

<plugin>
    <groupId>com.microsoft.azure</groupId>
    <artifactId>azure-spring-apps-maven-plugin</artifactId>
    <version>1.19.0</version>
    <configuration>
        <subscriptionId>12345678-1234-1234-1234-123456789101</subscriptionId>
        <resourceGroup>rg-ms-identity-spring-boot-webapp</resourceGroup>
        <clusterName>cluster-ms-identity-spring-boot-webapp</clusterName>
        <region>eastus</region>
        <sku>Standard</sku>
        <appName>ms-identity-spring-boot-webapp</appName>
        <isPublic>true</isPublic>
        <deployment>
            <cpu>1</cpu>
            <memoryInGB>2</memoryInGB>
            <instanceCount>1</instanceCount>
            <runtimeVersion>Java 11</runtimeVersion>
            <resources>
                <resource>
                    <directory>${project.basedir}/target</directory>
                    <includes>
                        <include>*.jar</include>
                    </includes>
                </resource>
            </resources>
        </deployment>
    </configuration>
</plugin>

You can modify the configurations for Azure Spring Apps directly in your pom.xml file. Some common configurations are listed in the following table:

Property Required Description
subscriptionId false The subscription ID.
resourceGroup true The Azure resource group for your Azure Spring Apps instance.
clusterName true The Azure Spring Apps cluster name. In case you're using a subscription and resource group that already have an Azure Spring Apps instance deployed, you can also use this existing cluster to deploy to.
appName true The name of your app in Azure Spring Apps.
region false The region in which to host your Azure Spring Apps instance. The default value is eastus. For valid regions, see Supported Regions.
sku false The pricing tier for your Azure Spring Apps instance. The default value is Basic, which is suited only for development and test environments.
runtime false The runtime environment configuration. For more information, see Configuration Details.
deployment false The deployment configuration. For more information, see Configuration Details.

For the complete list of configurations, see the plugin reference documentation. All the Azure Maven plugins share a common set of configurations. For these configurations, see Common Configurations. For configurations specific to Azure Spring Apps, see Azure Spring Apps: Configuration Details.

Be sure to save aside the clusterName and appName values for later use.

Prepare the app for deployment

When you deploy your application to Azure Spring Apps, your redirect URL changes to the redirect URL of your deployed app instance in Azure Spring Apps. Use the following steps to change these settings in your application.yml file:

  1. Navigate to your app's src\main\resources\application.yml file and change the value of post-logout-redirect-uri to your deployed app's domain name, as shown in the following example. For example, if you chose cluster-ms-identity-spring-boot-webapp for your Azure Spring Apps instance in the previous step and ms-identity-spring-boot-webapp for your app name, you must now use https://cluster-ms-identity-spring-boot-webapp-ms-identity-spring-boot-webapp.azuremicroservices.io for the post-logout-redirect-uri value.

    post-logout-redirect-uri: https://<cluster-name>-<app-name>.azuremicroservices.io
    
  2. After saving this file, use the following command to rebuild your app:

    mvn clean package
    

Important

The application.yml file of the application currently holds the value of your client secret in the client-secret parameter. It isn't good practice to keep this value in this file. You might also be taking a risk if you commit it to a Git repository.

As an extra security step, you can store this value in Azure Key Vault and load the secret from Key Vault to make it available in your application.

Update your Microsoft Entra ID app registration

Because the redirect URI changes to your deployed app on Azure Spring Apps, you also need to change the redirect URI in your Microsoft Entra ID app registration. Use the following steps to make this change:

  1. Navigate to the Microsoft identity platform for developers App registrations page.

  2. Use the search box to search for your app registration - for example, java-servlet-webapp-authentication.

  3. Open your app registration by selecting its name.

  4. Select Authentication from the menu.

  5. In the Web - Redirect URIs section, select Add URI.

  6. Fill out the URI of your app, appending /login/oauth2/code/ - for example, https://<cluster-name>-<app-name>.azuremicroservices.io/login/oauth2/code/.

  7. Select Save.

Deploy the app

Use the following command to deploy the app:

mvn azure-spring-apps:deploy

The following list describes the command interaction:

  • OAuth2 login: You need to authorize the sign-in to Azure based on the OAuth2 protocol.

After the command is executed, you can see from the following log messages that the deployment was successful:

[INFO] Deployment(default) is successfully created
[INFO] Starting Spring App after deploying artifacts...
[INFO] Deployment Status: Running
[INFO]   InstanceName:demo-default-x-xxxxxxxxxx-xxxxx  Status:Running Reason:null       DiscoverStatus:UNREGISTERED
[INFO]   InstanceName:demo-default-x-xxxxxxxxx-xxxxx  Status:Terminating Reason:null       DiscoverStatus:UNREGISTERED
[INFO] Getting public url of app(demo)...
[INFO] Application url: https://<your-Azure-Spring-Apps-instance-name>-demo.azuremicroservices.io

Validate the app

After the deployment finishes, access the application with the output application URL. Use the following steps to check the app's logs to investigate any deployment issue:

  1. Access the output application URL from the Outputs page of the Deployment section.

  2. From the navigation pane of the Azure Spring Apps instance Overview page, select Logs to check the app's logs.

Explore the sample

Use the following steps to explore the sample:

  1. Notice the signed-in or signed-out status displayed at the center of the screen.
  2. Select the context-sensitive button in the corner. This button reads Sign In when you first run the app. Alternatively, select token details, admins only, or regular users. Because these pages are protected and require authentication, you're automatically redirected to the sign-in page.
  3. On the next page, follow the instructions and sign in with an account in the Microsoft Entra ID tenant.
  4. On the consent screen, notice the scopes that are being requested.
  5. Upon successful completion of the sign-in flow, you should be redirected to the home page - which shows the sign in status - or one of the other pages, depending on which button triggered your sign-in flow.
  6. Notice that the context-sensitive button now says Sign out and displays your username.
  7. If you're on the home page, select ID Token Details to see some of the ID token's decoded claims, including roles.
  8. Select Admins Only to view the /admin_only. Only users with app role PrivilegedAdmin can view this page. Otherwise, an authorization failure message is displayed.
  9. Select Regular Users to view the /regular_user page. Only users with app role RegularUser or PrivilegedAdmin can view this page. Otherwise, an authorization failure message is displayed.
  10. Use the button in the corner to sign out. The status page reflects the new state.

About the code

This sample demonstrates how to use Microsoft Entra ID Spring Boot Starter client library for Java to sign in users into your Microsoft Entra ID tenant. The sample also makes use of the Spring Oauth2 Client and Spring Web boot starters. The sample uses claims from the ID token obtained from Microsoft Entra ID to display the details of the signed-in user, and to restrict access to some pages by using the roles claim for authorization.

Contents

The following table shows the contents of the sample project folder:

File/folder Description
AppCreationScripts/ Scripts to automatically configure Microsoft Entra ID app registrations.
pom.xml Application dependencies.
src/main/resources/templates/ Thymeleaf Templates for UI.
src/main/resources/application.yml Application and Microsoft Entra ID Boot Starter Library Configuration.
src/main/java/com/microsoft/azuresamples/msal4j/msidentityspringbootwebapp/ This directory contains the main application entry point, controller, and config classes.
.../MsIdentitySpringBootWebappApplication.java Main class.
.../SampleController.java Controller with endpoint mappings.
.../SecurityConfig.java Security configuration - for example, which routes require authentication.
.../Utilities.java Utility class - for example, filter ID token claims.
CHANGELOG.md List of changes to the sample.
CONTRIBUTING.md Guidelines for contributing to the sample.
LICENSE` The license for the sample.

ID token claims

To extract token details, the app makes use of Spring Security's AuthenticationPrincipal and OidcUser object in a request mapping, as shown in the following example. See the Sample Controller for the full details of how this app makes use of ID token claims.

import org.springframework.security.oauth2.core.oidc.user.OidcUser;
import org.springframework.security.core.annotation.AuthenticationPrincipal;
//...
@GetMapping(path = "/some_path")
public String tokenDetails(@AuthenticationPrincipal OidcUser principal) {
    Map<String, Object> claims = principal.getIdToken().getClaims();
}

Process a roles claim in the ID token

The roles claim of the token includes the names of the roles that the signed-in user is assigned to, as shown in the following example:

{
  ...
  "roles": [
    "PrivilegedAdmin",
    "RegularUser",]
  ...
}

A common way to access the role names is documented in the ID token claims section.

Microsoft Entra ID Boot Starter v3.3 and higher also parses the roles claim automatically and adds each role to the signed-in user's Authorities, prefixing each with the string APPROLE_. This configuration enables developers to make use of app roles with Spring PrePost condition annotations using the hasAuthority method. For example, you can find the following @PreAuthorize conditions demonstrated in SampleController.java:

@GetMapping(path = "/admin_only")
@PreAuthorize("hasAuthority('APPROLE_PrivilegedAdmin')")
public String adminOnly(Model model) {
    // restrict to users who have PrivilegedAdmin app role only
}
@GetMapping(path = "/regular_user")
@PreAuthorize("hasAnyAuthority('APPROLE_PrivilegedAdmin','APPROLE_RegularUser')")
public String regularUser(Model model) {
    // restrict to users who have any of RegularUser or PrivilegedAdmin app roles
}

The following code gets a full list of authorities for a given user:

@GetMapping(path = "/some_path")
public String tokenDetails(@AuthenticationPrincipal OidcUser principal) {
   Collection<? extends GrantedAuthority> authorities = principal.getAuthorities();
}

For sign-in, the app makes a request to the Azure Active Directory sign-in endpoint automatically configured by Microsoft Entra ID Spring Boot Starter client library for Java, as shown in the following example:

<a class="btn btn-success" href="/oauth2/authorization/azure">Sign In</a>

For sign-out, the app makes a POST request to the logout endpoint, as shown in the following example:

<form action="#" th:action="@{/logout}" method="post">
  <input class="btn btn-warning" type="submit" value="Sign Out" />
</form>

Authentication-dependent UI elements

The app has some simple logic in the UI template pages for determining content to display based on whether the user is authenticated, as shown in the following example using Spring Security Thymeleaf tags:

<div sec:authorize="isAuthenticated()">
  this content only shows to authenticated users
</div>
<div sec:authorize="isAnonymous()">
  this content only shows to not-authenticated users
</div>

Protect routes with AADWebSecurityConfigurerAdapter

By default, the app protects the ID Token Details, Admins Only, and Regular Users pages so that only signed-in users can access them. The app configures these routes from the app.protect.authenticated property from the application.yml file. To configure your app's specific requirements, you can extend AADWebSecurityConfigurationAdapter in one of your classes. For an example, see this app's SecurityConfig class, shown in the following code:

@EnableWebSecurity
@EnableGlobalMethodSecurity(prePostEnabled = true)
public class SecurityConfig extends AADWebSecurityConfigurerAdapter{
  @Value( "${app.protect.authenticated}" )
  private String[] protectedRoutes;

    @Override
    public void configure(HttpSecurity http) throws Exception {
    // use required configuration form AADWebSecurityAdapter.configure:
    super.configure(http);
    // add custom configuration:
    http.authorizeRequests()
      .antMatchers(protectedRoutes).authenticated()     // limit these pages to authenticated users (default: /token_details, /admin_only, /regular_user)
      .antMatchers("/**").permitAll();                  // allow all other routes.
    }
}

More information

For more information about how OAuth 2.0 protocols work in this scenario and other scenarios, see Authentication Scenarios for Microsoft Entra ID.