Skip to content

Commit

Permalink
User Oauth2 WebClient instead of msal to get groups from graph. (Azur…
Browse files Browse the repository at this point in the history
…e#17529)

* User Oauth2 WebClient instead of msal to get groups from graph.
  • Loading branch information
Rujun Chen authored Nov 17, 2020
1 parent db7a82d commit 7eb0427
Show file tree
Hide file tree
Showing 46 changed files with 906 additions and 558 deletions.
1 change: 1 addition & 0 deletions eng/versioning/external_dependencies.txt
Original file line number Diff line number Diff line change
Expand Up @@ -99,6 +99,7 @@ org.springframework.boot:spring-boot-starter-logging;2.3.5.RELEASE
org.springframework.boot:spring-boot-starter-test;2.3.5.RELEASE
org.springframework.boot:spring-boot-starter-validation;2.3.5.RELEASE
org.springframework.boot:spring-boot-starter-web;2.3.5.RELEASE
org.springframework.boot:spring-boot-starter-webflux;2.3.5.RELEASE
org.springframework.boot:spring-boot-starter;2.3.5.RELEASE
org.springframework.boot:spring-boot;2.3.5.RELEASE
org.springframework.data:spring-data-commons;2.3.5.RELEASE
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -3,9 +3,7 @@

package com.azure.spring.sample.aad.security;

import com.azure.spring.autoconfigure.aad.AADAuthenticationFailureHandler;
import com.azure.spring.autoconfigure.aad.AADOAuth2AuthorizationRequestResolver;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.ApplicationContext;
import org.springframework.security.config.annotation.method.configuration.EnableGlobalMethodSecurity;
Expand All @@ -31,16 +29,15 @@ protected void configure(HttpSecurity http) throws Exception {
final ClientRegistrationRepository clientRegistrationRepository =
applicationContext.getBean(ClientRegistrationRepository.class);
http.authorizeRequests()
.anyRequest().authenticated()
.and()
.oauth2Login()
.userInfoEndpoint()
.oidcUserService(oidcUserService)
.and()
.authorizationEndpoint()
.authorizationRequestResolver(
new AADOAuth2AuthorizationRequestResolver(clientRegistrationRepository))
.and()
.failureHandler(new AADAuthenticationFailureHandler());
.anyRequest().authenticated()
.and()
.oauth2Login()
.userInfoEndpoint()
.oidcUserService(oidcUserService)
.and()
.authorizationEndpoint()
.authorizationRequestResolver(
new AADOAuth2AuthorizationRequestResolver(clientRegistrationRepository)
);
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@

## 3.0.0-beta.1 (Unreleased)
### Breaking Changes
- Conditional access policy is not supported temporary, we may recover it in the future.
- Configuration items like `spring.security.oauth2.client.xxx` is not supported anymore. Please use the following configuration items instead:
```
azure.activedirectory.tenant-id=xxxxxx-your-tenant-id-xxxxxx
Expand Down
33 changes: 0 additions & 33 deletions sdk/spring/azure-spring-boot-starter-active-directory/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -204,39 +204,6 @@ azure.activedirectory.environment=cn-v2-graph
Please refer to [azure-spring-boot-sample-active-directory-backend-v2](https://github.com/Azure/azure-sdk-for-java/blob/master/sdk/spring/azure-spring-boot-samples/azure-spring-boot-sample-active-directory-backend-v2/README.md) to see a sample configured to use the Microsoft Graph API.


### AAD Conditional Access Policy
Now azure-active-directory-spring-boot-starter has supported AAD conditional access policy, if you are using this policy, you need add **AADOAuth2AuthorizationRequestResolver** and **AADAuthenticationFailureHandler** to your WebSecurityConfigurerAdapter.
<!-- embedme ../azure-spring-boot/src/samples/java/com/azure/spring/aad/AADOAuth2LoginConditionalPolicyConfigSample.java#L26-L53 -->
```java
@EnableWebSecurity
@EnableGlobalMethodSecurity(prePostEnabled = true)
public class AADOAuth2LoginConditionalPolicyConfigSample extends WebSecurityConfigurerAdapter {

@Autowired
private OAuth2UserService<OidcUserRequest, OidcUser> oidcUserService;

@Autowired
ApplicationContext applicationContext;

@Override
protected void configure(HttpSecurity http) throws Exception {
final ClientRegistrationRepository clientRegistrationRepository =
applicationContext.getBean(ClientRegistrationRepository.class);

http.authorizeRequests()
.anyRequest().authenticated()
.and()
.oauth2Login()
.userInfoEndpoint()
.oidcUserService(oidcUserService)
.and()
.authorizationEndpoint()
.authorizationRequestResolver(new AADOAuth2AuthorizationRequestResolver(clientRegistrationRepository))
.and()
.failureHandler(new AADAuthenticationFailureHandler());
}
}
```
### Customize scopes in authorize requests

By default, `azure-spring-boot-starter-active-directory` configures scopes of `openid`, `profile` and `https://graph.microsoft.com/user.read` to implement OpenID Connect protocol and access of Microsoft Graph API. For customization of scope, developers need to configure in the `application.properties`:
Expand Down
16 changes: 14 additions & 2 deletions sdk/spring/azure-spring-boot-starter-active-directory/pom.xml
Original file line number Diff line number Diff line change
Expand Up @@ -30,6 +30,11 @@
<artifactId>spring-boot-starter-validation</artifactId>
<version>2.3.5.RELEASE</version> <!-- {x-version-update;org.springframework.boot:spring-boot-starter-validation;external_dependency} -->
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-webflux</artifactId>
<version>2.3.5.RELEASE</version> <!-- {x-version-update;org.springframework.boot:spring-boot-starter-webflux;external_dependency} -->
</dependency>
<dependency>
<groupId>com.azure.spring</groupId>
<artifactId>azure-spring-boot</artifactId>
Expand Down Expand Up @@ -70,6 +75,11 @@
<artifactId>jackson-databind</artifactId>
<version>2.11.3</version> <!-- {x-version-update;com.fasterxml.jackson.core:jackson-databind;external_dependency} -->
</dependency>
<dependency>
<groupId>io.projectreactor.netty</groupId>
<artifactId>reactor-netty</artifactId>
<version>0.9.13.RELEASE</version> <!-- {x-version-update;io.projectreactor.netty:reactor-netty;external_dependency} -->
</dependency>
</dependencies>

<build>
Expand All @@ -85,12 +95,14 @@
<include>com.fasterxml.jackson.core:jackson-databind:[2.11.3]</include> <!-- {x-include-update;com.fasterxml.jackson.core:jackson-databind;external_dependency} -->
<include>com.microsoft.azure:msal4j:[1.8.0]</include> <!-- {x-include-update;com.microsoft.azure:msal4j;external_dependency} -->
<include>com.nimbusds:nimbus-jose-jwt:[8.19]</include> <!-- {x-include-update;com.nimbusds:nimbus-jose-jwt;external_dependency} -->
<include>org.springframework:spring-web:[5.2.10.RELEASE]</include> <!-- {x-include-update;org.springframework:spring-web;external_dependency} -->
<include>org.springframework.boot:spring-boot-starter:[2.3.5.RELEASE]</include> <!-- {x-include-update;org.springframework.boot:spring-boot-starter;external_dependency} -->
<include>io.projectreactor.netty:reactor-netty:[0.9.13.RELEASE]</include> <!-- {x-include-update;io.projectreactor.netty:reactor-netty;external_dependency} -->
<include>org.springframework.boot:spring-boot-starter-validation:[2.3.5.RELEASE]</include> <!-- {x-include-update;org.springframework.boot:spring-boot-starter-validation;external_dependency} -->
<include>org.springframework.boot:spring-boot-starter-webflux:[2.3.5.RELEASE]</include> <!-- {x-include-update;org.springframework.boot:spring-boot-starter-webflux;external_dependency} -->
<include>org.springframework.boot:spring-boot-starter:[2.3.5.RELEASE]</include> <!-- {x-include-update;org.springframework.boot:spring-boot-starter;external_dependency} -->
<include>org.springframework.security:spring-security-config:[5.3.5.RELEASE]</include> <!-- {x-include-update;org.springframework.security:spring-security-config;external_dependency} -->
<include>org.springframework.security:spring-security-core:[5.3.5.RELEASE]</include> <!-- {x-include-update;org.springframework.security:spring-security-core;external_dependency} -->
<include>org.springframework.security:spring-security-web:[5.3.5.RELEASE]</include> <!-- {x-include-update;org.springframework.security:spring-security-web;external_dependency} -->
<include>org.springframework:spring-web:[5.2.10.RELEASE]</include> <!-- {x-include-update;org.springframework:spring-web;external_dependency} -->
</includes>
</bannedDependencies>
</rules>
Expand Down
Original file line number Diff line number Diff line change
@@ -1,8 +1,8 @@
package com.azure.test.aad.auth;

import com.azure.spring.aad.implementation.AzureClientRegistrationRepository;
import com.azure.spring.aad.implementation.DefaultClient;
import com.azure.spring.aad.implementation.IdentityEndpoints;
import com.azure.spring.autoconfigure.aad.AuthorizationServerEndpoints;
import com.azure.spring.autoconfigure.aad.AzureClientRegistrationRepository;
import com.azure.spring.autoconfigure.aad.DefaultClient;
import com.azure.test.utils.AppRunner;
import org.junit.jupiter.api.Test;
import org.springframework.boot.autoconfigure.EnableAutoConfiguration;
Expand Down Expand Up @@ -34,17 +34,17 @@ public void clientRegistered() {
assertEquals("fake-client-id", azureClientRegistration.getClientId());
assertEquals("fake-client-secret", azureClientRegistration.getClientSecret());

IdentityEndpoints identityEndpoints = new IdentityEndpoints();
AuthorizationServerEndpoints authorizationServerEndpoints = new AuthorizationServerEndpoints();
assertEquals(
identityEndpoints.authorizationEndpoint("fake-tenant-id"),
authorizationServerEndpoints.authorizationEndpoint("fake-tenant-id"),
azureClientRegistration.getProviderDetails().getAuthorizationUri()
);
assertEquals(
identityEndpoints.tokenEndpoint("fake-tenant-id"),
authorizationServerEndpoints.tokenEndpoint("fake-tenant-id"),
azureClientRegistration.getProviderDetails().getTokenUri()
);
assertEquals(
identityEndpoints.jwkSetEndpoint("fake-tenant-id"),
authorizationServerEndpoints.jwkSetEndpoint("fake-tenant-id"),
azureClientRegistration.getProviderDetails().getJwkSetUri()
);
assertEquals(
Expand All @@ -58,7 +58,10 @@ public void clientRegistered() {
@Test
public void clientRequiresPermissionRegistered() {
try (AppRunner appRunner = createApp()) {
appRunner.property("azure.activedirectory.authorization.graph.scope", "Calendars.Read");
appRunner.property(
"azure.activedirectory.authorization.graph.scope",
"https://graph.microsoft.com/Calendars.Read"
);
appRunner.start();

ClientRegistrationRepository clientRegistrationRepository =
Expand All @@ -67,17 +70,23 @@ public void clientRequiresPermissionRegistered() {
ClientRegistration graphClientRegistration = clientRegistrationRepository.findByRegistrationId("graph");

assertNotNull(azureClientRegistration);
assertDefaultScopes(azureClientRegistration, "openid", "profile", "offline_access", "Calendars.Read");
assertDefaultScopes(
azureClientRegistration,
"openid", "profile", "offline_access", "https://graph.microsoft.com/Calendars.Read"
);

assertNotNull(graphClientRegistration);
assertDefaultScopes(graphClientRegistration, "Calendars.Read");
assertDefaultScopes(graphClientRegistration, "https://graph.microsoft.com/Calendars.Read");
}
}

@Test
public void clientRequiresMultiPermissions() {
try (AppRunner appRunner = createApp()) {
appRunner.property("azure.activedirectory.authorization.graph.scope", "Calendars.Read");
appRunner.property(
"azure.activedirectory.authorization.graph.scope",
"https://graph.microsoft.com/Calendars.Read"
);
appRunner.property(
"azure.activedirectory.authorization.arm.scope",
"https://management.core.windows.net/user_impersonation"
Expand All @@ -95,19 +104,22 @@ public void clientRequiresMultiPermissions() {
"openid",
"profile",
"offline_access",
"Calendars.Read",
"https://graph.microsoft.com/Calendars.Read",
"https://management.core.windows.net/user_impersonation"
);

assertNotNull(graphClientRegistration);
assertDefaultScopes(graphClientRegistration, "Calendars.Read");
assertDefaultScopes(graphClientRegistration, "https://graph.microsoft.com/Calendars.Read");
}
}

@Test
public void clientRequiresPermissionInDefaultClient() {
try (AppRunner appRunner = createApp()) {
appRunner.property("azure.activedirectory.authorization.azure.scope", "Calendars.Read");
appRunner.property(
"azure.activedirectory.authorization.azure.scope",
"https://graph.microsoft.com/Calendars.Read"
);
appRunner.start();

ClientRegistrationRepository clientRegistrationRepository =
Expand All @@ -117,15 +129,18 @@ public void clientRequiresPermissionInDefaultClient() {
assertNotNull(azureClientRegistration);
assertDefaultScopes(
azureClientRegistration,
"openid", "profile", "offline_access", "Calendars.Read"
"openid", "profile", "offline_access", "https://graph.microsoft.com/Calendars.Read"
);
}
}

@Test
public void aadAwareClientRepository() {
try (AppRunner appRunner = createApp()) {
appRunner.property("azure.activedirectory.authorization.graph.scope", "Calendars.Read");
appRunner.property(
"azure.activedirectory.authorization.graph.scope",
"https://graph.microsoft.com/Calendars.Read")
;
appRunner.start();

AzureClientRegistrationRepository azureClientRegistrationRepository =
Expand Down Expand Up @@ -155,40 +170,44 @@ public void aadAwareClientRepository() {
@Test
public void defaultClientWithAuthzScope() {
try (AppRunner appRunner = createApp()) {
appRunner.property("azure.activedirectory.authorization.azure.scope", "Calendars.Read");
appRunner.property(
"azure.activedirectory.authorization.azure.scope",
"https://graph.microsoft.com/Calendars.Read"
);
appRunner.start();

AzureClientRegistrationRepository azureClientRegistrationRepository =
appRunner.getBean(AzureClientRegistrationRepository.class);
assertDefaultScopes(
azureClientRegistrationRepository.defaultClient(),
"openid", "profile", "offline_access", "Calendars.Read"
"openid", "profile", "offline_access", "https://graph.microsoft.com/Calendars.Read"
);
}
}

@Test
public void customizeUri() {
public void customizeEnvironment() {
try (AppRunner appRunner = createApp()) {
appRunner.property("azure.activedirectory.uri", "http://localhost/");
appRunner.property("azure.activedirectory.environment", "cn-v2-graph");
appRunner.start();

AzureClientRegistrationRepository azureClientRegistrationRepository =
appRunner.getBean(AzureClientRegistrationRepository.class);
ClientRegistration azureClientRegistration =
azureClientRegistrationRepository.findByRegistrationId("azure");

IdentityEndpoints endpoints = new IdentityEndpoints("http://localhost/");
AuthorizationServerEndpoints authorizationServerEndpoints =
new AuthorizationServerEndpoints("https://login.partner.microsoftonline.cn");
assertEquals(
endpoints.authorizationEndpoint("fake-tenant-id"),
authorizationServerEndpoints.authorizationEndpoint("fake-tenant-id"),
azureClientRegistration.getProviderDetails().getAuthorizationUri()
);
assertEquals(
endpoints.tokenEndpoint("fake-tenant-id"),
authorizationServerEndpoints.tokenEndpoint("fake-tenant-id"),
azureClientRegistration.getProviderDetails().getTokenUri()
);
assertEquals(
endpoints.jwkSetEndpoint("fake-tenant-id"),
authorizationServerEndpoints.jwkSetEndpoint("fake-tenant-id"),
azureClientRegistration.getProviderDetails().getJwkSetUri()
);
}
Expand Down
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
package com.azure.test.aad.auth;

import com.azure.spring.aad.implementation.AzureClientRegistrationRepository;
import com.azure.spring.aad.implementation.AzureOAuth2AuthorizedClientRepository;
import com.azure.spring.autoconfigure.aad.AzureClientRegistrationRepository;
import com.azure.spring.autoconfigure.aad.AzureOAuth2AuthorizedClientRepository;
import com.azure.test.utils.AppRunner;
import org.junit.jupiter.api.AfterEach;
import org.junit.jupiter.api.BeforeEach;
Expand Down
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
package com.azure.test.aad.auth;

import com.azure.spring.aad.implementation.AzureClientRegistrationRepository;
import com.azure.spring.aad.implementation.AzureOAuth2AuthorizationCodeGrantRequestEntityConverter;
import com.azure.spring.autoconfigure.aad.AzureClientRegistrationRepository;
import com.azure.spring.autoconfigure.aad.AzureOAuth2AuthorizationCodeGrantRequestEntityConverter;
import com.azure.test.utils.AppRunner;
import org.junit.jupiter.api.AfterEach;
import org.junit.jupiter.api.BeforeEach;
Expand Down
1 change: 1 addition & 0 deletions sdk/spring/azure-spring-boot/CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@

## 3.0.0-beta.1 (Unreleased)
### Breaking Changes
- Conditional access policy is not supported temporary, we may recover it in the future.
- Configuration items like `spring.security.oauth2.client.xxx` is not supported anymore. Please use the following configuration items instead:
```
azure.activedirectory.tenant-id=xxxxxx-your-tenant-id-xxxxxx
Expand Down
Loading

0 comments on commit 7eb0427

Please sign in to comment.