2
votes

I'm going through a tutorial on Microsoft Docs on how to secure my Spring MVC application using Spring Boot Starter for Azure Active Directory. I'm trying to secure the admin area of my site. I have a @PreAuthorize annotation on a controller method that I only want users in the admin group to have access to. I am able to get a login prompt, but after succesfully logging in I get a 403 for that controller method. When I remove the @PreAuthorize annotation, I can log in and access the method just fine.

Here is the controller method:

@Controller
public class PostController {

    ...

    @PreAuthorize("hasRole('admin')")
    @RequestMapping(path = "/admin/post", method = RequestMethod.GET)
    public String getPost(@ModelAttribute("post") Post post, Model model) {
        List<Tag> tags = tagService.getAll();
        model.addAttribute("tags", tags);
        return "post";
    }

    ...

Here is the WebSecurityConfig.java:

@EnableWebSecurity
@EnableGlobalMethodSecurity(prePostEnabled = true)
public class WebSecurityConfig extends WebSecurityConfigurerAdapter {
    @Autowired
    private OAuth2UserService<OidcUserRequest, OidcUser> oidcUserService;

    @Override
    protected void configure(HttpSecurity http) throws Exception {
        http
            .authorizeRequests()
            .antMatchers("/admin/**").authenticated()
            .and()
            .oauth2Login()
            .userInfoEndpoint()
            .oidcUserService(oidcUserService);
    }
}

Here is the application.properties:

# Azure AD
# Specifies your Active Directory ID:
azure.activedirectory.tenant-id=<my-ad-tenant-id>

# Specifies your App Registration's Application ID:
spring.security.oauth2.client.registration.azure.client-id=<my-app-registration-app-id>

# Specifies your App Registration's secret key:
spring.security.oauth2.client.registration.azure.client-secret=<my-client-secret>

# Specifies the list of Active Directory groups to use for authorization:
azure.activedirectory.activeDirectoryGroups=admin

Here is the app registration manifest:

{
    "id": "<id>",
    "acceptMappedClaims": null,
    "accessTokenAcceptedVersion": null,
    "addIns": [],
    "allowPublicClient": null,
    "appId": "<app-id>",
    "appRoles": [],
    "oauth2AllowUrlPathMatching": false,
    "createdDateTime": "2020-04-10T23:37:26Z",
    "groupMembershipClaims": null,
    "identifierUris": [],
    "informationalUrls": {
        "termsOfService": null,
        "support": null,
        "privacy": null,
        "marketing": null
    },
    "keyCredentials": [],
    "knownClientApplications": [],
    "logoUrl": null,
    "logoutUrl": null,
    "name": "test",
    "oauth2AllowIdTokenImplicitFlow": true,
    "oauth2AllowImplicitFlow": true,
    "oauth2Permissions": [],
    "oauth2RequirePostResponse": false,
    "optionalClaims": null,
    "orgRestrictions": [],
    "parentalControlSettings": {
        "countriesBlockedForMinors": [],
        "legalAgeGroupRule": "Allow"
    },
    "passwordCredentials": [
        {
            "customKeyIdentifier": null,
            "endDate": "2299-12-31T05:00:00Z",
            "keyId": "<key-id>",
            "startDate": "2020-04-10T23:39:47.917Z",
            "value": null,
            "createdOn": "2020-04-10T23:39:48.4572747Z",
            "hint": "SVb",
            "displayName": "test key"
        }
    ],
    "preAuthorizedApplications": [],
    "publisherDomain": "test.onmicrosoft.com",
    "replyUrlsWithType": [
        {
            "url": "http://localhost:8080/login/oauth2/code/azure",
            "type": "Web"
        }
    ],
    "requiredResourceAccess": [
        {
            "resourceAppId": "<resource-app-id>",
            "resourceAccess": [
                {
                    "id": "<resource-access-id>",
                    "type": "Scope"
                }
            ]
        }
    ],
    "samlMetadataUrl": null,
    "signInUrl": null,
    "signInAudience": "AzureADMyOrg",
    "tags": [],
    "tokenEncryptionKeyId": null
}

Here's the group in Azure:

enter image description here

The user I'm signing in with is a member of this group. According to the documentation, only users that are not authorized should receive a 403. I've tried changing the hasRole() parameter to 'ROLE_admin' but that does not work. I've also tried to configure the hasRole("admin") in the WebSecurityConfig.java like this:

@Override
protected void configure(HttpSecurity http) throws Exception {
    http
        .authorizeRequests()
        .antMatchers("/admin/**").authenticated()
        .antMatchers("/admin/**").hasRole("admin")
        .and()
        .oauth2Login()
        .userInfoEndpoint()
        .oidcUserService(oidcUserService);
}

But that does not work either. I've been at this for a couple of days but I cannot figure out why I'm getting a 403 when the user I'm signing in with is in the admin group.

EDIT

pom.xml:

<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
    xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
    <modelVersion>4.0.0</modelVersion>
    <parent>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-parent</artifactId>
        <version>2.2.5.RELEASE</version>
    </parent>
    <groupId>me.test</groupId>
    <artifactId>test-me</artifactId>
    <version>1.0</version>
    <packaging>war</packaging>
    <name>test.me</name>

    <properties>
        <java.version>1.8</java.version>
        <azure.version>2.2.0</azure.version>
    </properties>

    <dependencies>
        <dependency>
            <groupId>com.microsoft.azure</groupId>
            <artifactId>azure-active-directory-spring-boot-starter</artifactId>
        </dependency>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-web</artifactId>
        </dependency>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-thymeleaf</artifactId>
        </dependency>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-security</artifactId>
        </dependency>
        <dependency>
            <groupId>org.springframework.security</groupId>
            <artifactId>spring-security-oauth2-client</artifactId>
        </dependency>
        <dependency>
            <groupId>org.springframework.security</groupId>
            <artifactId>spring-security-oauth2-jose</artifactId>
        </dependency>
        <dependency>
            <groupId>org.thymeleaf.extras</groupId>
            <artifactId>thymeleaf-extras-springsecurity5</artifactId>
        </dependency>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-data-jpa</artifactId>
        </dependency>
        <dependency>
            <groupId>com.microsoft.sqlserver</groupId>
            <artifactId>mssql-jdbc</artifactId>
        </dependency>
        <dependency>
            <groupId>org.hibernate</groupId>
            <artifactId>hibernate-validator</artifactId>
            <version>6.1.0.Final</version>
        </dependency>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-configuration-processor</artifactId>
            <optional>true</optional>
        </dependency>
        <dependency>
            <groupId>org.apache.commons</groupId>
            <artifactId>commons-lang3</artifactId>
            <version>3.9</version>
        </dependency>
        <dependency>
            <groupId>com.azure</groupId>
            <artifactId>azure-storage-blob</artifactId>
            <version>12.5.0</version>
        </dependency>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-test</artifactId>
            <scope>test</scope>
            <exclusions>
                <exclusion>
                    <groupId>org.junit.vintage</groupId>
                    <artifactId>junit-vintage-engine</artifactId>
                </exclusion>
            </exclusions>
        </dependency>
    </dependencies>

    <dependencyManagement>
        <dependencies>
            <dependency>
                <groupId>com.microsoft.azure</groupId>
                <artifactId>azure-spring-boot-bom</artifactId>
                <version>${azure.version}</version>
                <type>pom</type>
                <scope>import</scope>
            </dependency>
        </dependencies>
    </dependencyManagement>

    <build>
    <finalName>ROOT</finalName>
        <plugins>
            <plugin>
                <groupId>org.springframework.boot</groupId>
                <artifactId>spring-boot-maven-plugin</artifactId>
            </plugin>
            <plugin>
                <artifactId>maven-failsafe-plugin</artifactId>
                <executions>
                    <execution>
                        <goals>
                            <goal>integration-test</goal>
                            <goal>verify</goal>
                        </goals>
                    </execution>
                </executions>
            </plugin>
        </plugins>
    </build>

</project>

You can view the Microsoft Spring Boot / Azure AD sample in GitHub.

1
Have you tried to configure group claim(docs.microsoft.com/en-us/azure/active-directory/develop/…)? - Jim Xu

1 Answers

1
votes

According to my research, if we want to retrieves user's group membership using graph API which requires the registered app to have Direcory.AccessAsUser.All permissions. For more details, please refer to the document and the document.

For example

  1. Update permissions enter image description here

  2. application.properties

# Azure AD
# Specifies your Active Directory ID:
azure.activedirectory.tenant-id=<my-ad-tenant-id>

# Specifies your App Registration's Application ID:
spring.security.oauth2.client.registration.azure.client-id=<my-app-registration-app-id>

# Specifies your App Registration's secret key:
spring.security.oauth2.client.registration.azure.client-secret=<my-client-secret>

# Specifies the list of Active Directory groups to use for authorization:
azure.activedirectory.activeDirectoryGroups=SQLAdmin

# Configure log level
logging.level.root=Debug
  1. WebSecurityConfig.java
@EnableWebSecurity
@EnableGlobalMethodSecurity(prePostEnabled = true)
public class WebSecurityConfig extends WebSecurityConfigurerAdapter {
    @Autowired
    private OAuth2UserService<OidcUserRequest, OidcUser> oidcUserService;

    @Override
    protected void configure(HttpSecurity http) throws Exception {
        http
                .authorizeRequests()
                .antMatchers("/admin/**").authenticated()
                .and()
                .oauth2Login()
                .userInfoEndpoint()
                .oidcUserService(oidcUserService);
    }
}
  1. Controller
 @Autowired
    @PreAuthorize("hasRole('SQLAdmin')")
    @GetMapping("/admin")
    @ResponseBody
    public String helloWorld() {
        return "Hello World!";
    }

enter image description here enter image description here