Skip to content
Merged
Show file tree
Hide file tree
Changes from 4 commits
Commits
Show all changes
24 commits
Select commit Hold shift + click to select a range
29b5cd0
* Make changes according to checkstyle
ZhuXiaoBing-cn Nov 17, 2020
081cb82
* Make changes according to checkstyle
ZhuXiaoBing-cn Nov 17, 2020
c8a3508
* Make changes according to checkstyle
ZhuXiaoBing-cn Nov 17, 2020
b9aa067
* add JavaDoc
ZhuXiaoBing-cn Nov 17, 2020
fdfb007
* remove tenant validator
ZhuXiaoBing-cn Nov 18, 2020
bddc2e4
* add Converter,it's effect will be to store the UserPrincipal to Aut…
ZhuXiaoBing-cn Nov 19, 2020
cb82a9b
Merge branch 'master' into access_token_validate
ZhuXiaoBing-cn Nov 19, 2020
0732746
Merge branch 'master' into access_token_validate
ZhuXiaoBing-cn Nov 19, 2020
3f8dac0
* update file name
ZhuXiaoBing-cn Nov 20, 2020
4b6e974
* Make changes according to checkstyle
ZhuXiaoBing-cn Nov 23, 2020
ce65efe
Merge branch 'master' into access_token_validate
ZhuXiaoBing-cn Nov 23, 2020
05d929d
* Make changes according to checkstyle
ZhuXiaoBing-cn Nov 23, 2020
4e4d053
* Make changes according to checkstyle
ZhuXiaoBing-cn Nov 23, 2020
b362dfb
* Make changes according to checkstyle
ZhuXiaoBing-cn Nov 23, 2020
ae39fe7
* Make changes according to checkstyle
ZhuXiaoBing-cn Nov 23, 2020
acaeefa
* update AzureJwtBearerTokenAuthenticationConverter
ZhuXiaoBing-cn Nov 23, 2020
d25e9df
Merge branch 'master' into access_token_validate
ZhuXiaoBing-cn Nov 23, 2020
a31f1a6
* add AzureOAuth2AuthenticatedPrincipal
ZhuXiaoBing-cn Nov 25, 2020
f7aa628
Merge branch 'master' into access_token_validate
ZhuXiaoBing-cn Nov 25, 2020
0851fc6
* update AzureActiveDirectoryResourceServerConfiguration
ZhuXiaoBing-cn Nov 25, 2020
0ac20f9
* Make changes according to checkstyle
ZhuXiaoBing-cn Nov 25, 2020
9f80db0
* Make changes according to checkstyle.
ZhuXiaoBing-cn Nov 25, 2020
fd2155b
* update unit test
ZhuXiaoBing-cn Nov 26, 2020
7403602
* Modify invalid import
ZhuXiaoBing-cn Nov 26, 2020
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions eng/versioning/external_dependencies.txt
Original file line number Diff line number Diff line change
Expand Up @@ -110,6 +110,7 @@ org.springframework.kafka:spring-kafka;2.5.7.RELEASE
org.springframework.security:spring-security-config;5.3.5.RELEASE
org.springframework.security:spring-security-core;5.3.5.RELEASE
org.springframework.security:spring-security-oauth2-client;5.3.5.RELEASE
org.springframework.security:spring-security-oauth2-resource-server;5.3.5.RELEASE
org.springframework.security:spring-security-oauth2-core;5.3.5.RELEASE
org.springframework.security:spring-security-oauth2-jose;5.3.5.RELEASE
org.springframework.security:spring-security-web;5.3.5.RELEASE
Expand Down
7 changes: 7 additions & 0 deletions sdk/spring/azure-spring-boot/pom.xml
Original file line number Diff line number Diff line change
Expand Up @@ -81,6 +81,12 @@
<version>5.3.5.RELEASE</version> <!-- {x-version-update;org.springframework.security:spring-security-oauth2-client;external_dependency} -->
<optional>true</optional>
</dependency>
<dependency>
<groupId>org.springframework.security</groupId>
<artifactId>spring-security-oauth2-resource-server</artifactId>
<version>5.3.5.RELEASE</version> <!-- {x-version-update;org.springframework.security:spring-security-oauth2-resource-server;external_dependency} -->
<optional>true</optional>
</dependency>
<dependency>
<groupId>org.springframework.security</groupId>
<artifactId>spring-security-oauth2-jose</artifactId>
Expand Down Expand Up @@ -336,6 +342,7 @@
<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-oauth2-client:[5.3.5.RELEASE]</include> <!-- {x-include-update;org.springframework.security:spring-security-oauth2-client;external_dependency} -->
<include>org.springframework.security:spring-security-oauth2-resource-server:[5.3.5.RELEASE]</include> <!-- {x-include-update;org.springframework.security:spring-security-oauth2-resource-server;external_dependency} -->
<include>org.springframework.security:spring-security-oauth2-jose:[5.3.5.RELEASE]</include> <!-- {x-include-update;org.springframework.security:spring-security-oauth2-jose;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.apache.qpid:qpid-jms-client:[0.53.0]</include> <!-- {x-include-update;org.apache.qpid:qpid-jms-client;external_dependency} -->
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -4,23 +4,24 @@
package com.azure.spring.autoconfigure.aad;

import com.nimbusds.jose.jwk.source.RemoteJWKSet;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.boot.context.properties.ConfigurationProperties;
import org.springframework.boot.context.properties.DeprecatedConfigurationProperty;
import org.springframework.validation.annotation.Validated;

import javax.annotation.PostConstruct;
import javax.validation.constraints.NotEmpty;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collections;
import java.util.HashMap;
import java.util.HashSet;
import java.util.List;
import java.util.Map;
import java.util.Objects;
import java.util.Optional;
import java.util.Set;
import java.util.concurrent.TimeUnit;
import javax.annotation.PostConstruct;
import javax.validation.constraints.NotEmpty;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.boot.context.properties.ConfigurationProperties;
import org.springframework.boot.context.properties.DeprecatedConfigurationProperty;
import org.springframework.validation.annotation.Validated;

/**
* Configuration properties for Azure Active Directory Authentication.
Expand Down Expand Up @@ -54,6 +55,11 @@ public class AADAuthenticationProperties {
*/
private String clientId;

/**
* Tenant ID that is allowed under multi-tenant
*/
private Set<String> allowedTenantIds = new HashSet<>();

/**
* API Access Key of the registered application.
* Must be configured when OAuth2 authentication is done in front end
Expand Down Expand Up @@ -423,6 +429,14 @@ public static String getTransitiveGroupRelationship() {
return GROUP_RELATIONSHIP_TRANSITIVE;
}

public Set<String> getAllowedTenantIds() {
return allowedTenantIds;
}

public void setAllowedTenantIds(Set<String> allowedTenantIds) {
this.allowedTenantIds = allowedTenantIds;
}

public boolean isAllowedGroup(String group) {
return Optional.ofNullable(getUserGroup())
.map(UserGroupProperties::getAllowedGroups)
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,40 @@
// Copyright (c) Microsoft Corporation. All rights reserved.
// Licensed under the MIT License.
package com.azure.spring.autoconfigure.aad;

import java.util.List;
import org.springframework.security.oauth2.core.OAuth2TokenValidator;
import org.springframework.security.oauth2.core.OAuth2TokenValidatorResult;
import org.springframework.security.oauth2.jwt.Jwt;
import org.springframework.security.oauth2.jwt.JwtClaimValidator;
import org.springframework.util.Assert;

/**
* Validates the "aud" claim in a {@link Jwt}, that is matches a configured value
*/
public class AADJwtAudienceValidator implements OAuth2TokenValidator<Jwt> {

private final JwtClaimValidator<List<String>> validator;

/**
* Constructs a {@link AADJwtAudienceValidator} using the provided parameters
*
* @param audiences - The audience that each {@link Jwt} should have.
*/
@SuppressWarnings({"unchecked", "rawtypes"})
public AADJwtAudienceValidator(List<String> audiences) {
Assert.notNull(audiences, "audiences cannot be null");
this.validator = new JwtClaimValidator(AADTokenClaim.AUD, aud -> audiences.containsAll((List<String>) aud));
Comment thread
saragluna marked this conversation as resolved.
}

/**
* {@inheritDoc}
*/
@Override
public OAuth2TokenValidatorResult validate(Jwt token) {
Assert.notNull(token, "token cannot be null");
return this.validator.validate(token);
}


}
Original file line number Diff line number Diff line change
@@ -0,0 +1,80 @@
// Copyright (c) Microsoft Corporation. All rights reserved.
// Licensed under the MIT License.
package com.azure.spring.autoconfigure.aad;

import java.util.Set;
import java.util.function.Predicate;
import org.springframework.security.oauth2.core.OAuth2TokenValidator;
import org.springframework.security.oauth2.core.OAuth2TokenValidatorResult;
import org.springframework.security.oauth2.jwt.Jwt;
import org.springframework.security.oauth2.jwt.JwtClaimValidator;
import org.springframework.util.Assert;

/**
* Validates the "iss" claim in a {@link Jwt}, that is matches a configured value
*/
public class AADJwtIssuerValidator implements OAuth2TokenValidator<Jwt> {

private static final String LOGIN_MICROSOFT_ONLINE_ISSUER = "https://login.microsoftonline.com/";
private static final String STS_WINDOWS_ISSUER = "https://sts.windows.net/";
private static final String STS_CHINA_CLOUD_API_ISSUER = "https://sts.chinacloudapi.cn/";
private static final String COMMON = "common";
Comment thread
saragluna marked this conversation as resolved.
Outdated
private final JwtClaimValidator<String> validator;

/**
* Constructs a {@link AADJwtIssuerValidator} using the provided parameters
*
* @param tenantId - The tenant that each {@link Jwt} should have.
* @param allowedTenantIds - Multi-tenant is allowed tenantIds
*/
@SuppressWarnings({"unchecked", "rawtypes"})
public AADJwtIssuerValidator(String tenantId, Set<String> allowedTenantIds) {
Assert.notNull(tenantId, "tenantId cannot be null");
Assert.notNull(allowedTenantIds, "allowedTenantIds cannot be null");
if (tenantId.equals(COMMON)) {
if (allowedTenantIds.isEmpty()) {
this.validator = new JwtClaimValidator(AADTokenClaim.ISS, iss -> true);
} else {
this.validator = new JwtClaimValidator(AADTokenClaim.ISS, validIssuer(allowedTenantIds));
}
} else {
this.validator = new JwtClaimValidator(AADTokenClaim.ISS, validIssuer(tenantId));
}
}

private Predicate<String> validIssuer(String tenantId) {
return tid -> {
if (tid.startsWith(LOGIN_MICROSOFT_ONLINE_ISSUER) || tid.startsWith(STS_WINDOWS_ISSUER)
|| tid.startsWith(STS_CHINA_CLOUD_API_ISSUER)) {
if (tid.contains(tenantId)) {
return true;
}
}
return false;
};
}

private Predicate<String> validIssuer(Set<String> allowedTenantIds) {
return tid -> {
if (tid.startsWith(LOGIN_MICROSOFT_ONLINE_ISSUER) || tid.startsWith(STS_WINDOWS_ISSUER)
|| tid.startsWith(STS_CHINA_CLOUD_API_ISSUER)) {
for (String allowedTenantId : allowedTenantIds) {
if (tid.contains(allowedTenantId)) {
return true;
}
}
}
return false;
};
}

/**
* {@inheritDoc}
*/
@Override
public OAuth2TokenValidatorResult validate(Jwt token) {
Assert.notNull(token, "token cannot be null");
return this.validator.validate(token);
}

}
Original file line number Diff line number Diff line change
@@ -0,0 +1,50 @@
// Copyright (c) Microsoft Corporation. All rights reserved.
// Licensed under the MIT License.
package com.azure.spring.autoconfigure.aad;

import java.util.Set;
import org.springframework.security.oauth2.core.OAuth2TokenValidator;
import org.springframework.security.oauth2.core.OAuth2TokenValidatorResult;
import org.springframework.security.oauth2.jwt.Jwt;
import org.springframework.security.oauth2.jwt.JwtClaimValidator;
import org.springframework.util.Assert;

/**
* Validates the "tid" claim in a {@link Jwt}, that is matches a configured value
*/
public class AADJwtTenantValidator implements OAuth2TokenValidator<Jwt> {

private final JwtClaimValidator<String> validator;
private static final String COMMON = "common";

/**
* Constructs a {@link AADJwtIssuerValidator} using the provided parameters
*
* @param tenantId - The tenant that each {@link Jwt} should have.
* @param allowedTenantIds - Multi-tenant is allowed tenantIds
*/
@SuppressWarnings({"unchecked", "rawtypes"})
public AADJwtTenantValidator(String tenantId, Set<String> allowedTenantIds) {
Assert.notNull(tenantId, "tenantId cannot be null");
Assert.notNull(allowedTenantIds, "allowedTenantIds cannot be null");
if (tenantId.equals(COMMON)) {
if (allowedTenantIds.isEmpty()) {
this.validator = new JwtClaimValidator(AADTokenClaim.TID, tid -> true);
} else {
this.validator = new JwtClaimValidator(AADTokenClaim.TID, allowedTenantIds::contains);
}
} else {
this.validator = new JwtClaimValidator(AADTokenClaim.TID, tenantId::equals);
}
}

/**
* {@inheritDoc}
*/
@Override
public OAuth2TokenValidatorResult validate(Jwt token) {
Assert.notNull(token, "token cannot be null");
return this.validator.validate(token);
}

}
Original file line number Diff line number Diff line change
@@ -0,0 +1,94 @@
// Copyright (c) Microsoft Corporation. All rights reserved.
// Licensed under the MIT License.
package com.azure.spring.autoconfigure.aad;


import java.util.ArrayList;
import java.util.List;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.autoconfigure.condition.ConditionalOnClass;
import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean;
import org.springframework.boot.context.properties.EnableConfigurationProperties;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.security.config.annotation.web.builders.HttpSecurity;
import org.springframework.security.config.annotation.web.configuration.EnableWebSecurity;
import org.springframework.security.config.annotation.web.configuration.WebSecurityConfigurerAdapter;
import org.springframework.security.config.annotation.web.configurers.oauth2.server.resource.OAuth2ResourceServerConfigurer;
import org.springframework.security.oauth2.core.DelegatingOAuth2TokenValidator;
import org.springframework.security.oauth2.core.OAuth2TokenValidator;
import org.springframework.security.oauth2.jwt.Jwt;
import org.springframework.security.oauth2.jwt.JwtDecoder;
import org.springframework.security.oauth2.jwt.JwtTimestampValidator;
import org.springframework.security.oauth2.jwt.NimbusJwtDecoder;
import org.springframework.security.oauth2.server.resource.BearerTokenAuthenticationToken;
import org.springframework.util.StringUtils;

/**
* <p>
* The configuration will not be activated if no {@link BearerTokenAuthenticationToken} class provided.
* <p>
* By default, creating a JwtDecoder through JwkKeySetUri will be auto-configured.
*/
@Configuration(proxyBeanMethods = false)
@EnableConfigurationProperties(AADAuthenticationProperties.class)
@ConditionalOnClass(BearerTokenAuthenticationToken.class)
public class AADResourceServerAutoConfiguration {

@Autowired
private AADAuthenticationProperties aadAuthenticationProperties;

/**
* Use JwkKeySetUri to create JwtDecoder
*
* @return JwtDecoder bean
*/
@Bean
@ConditionalOnMissingBean(JwtDecoder.class)
public JwtDecoder jwtDecoderByJwkKeySetUri() {
if (StringUtils.isEmpty(aadAuthenticationProperties.getTenantId())) {
aadAuthenticationProperties.setTenantId("common");
}
AuthorizationServerEndpoints endpoints = new AuthorizationServerEndpoints();
NimbusJwtDecoder nimbusJwtDecoder = NimbusJwtDecoder
.withJwkSetUri(endpoints.jwkSetEndpoint(aadAuthenticationProperties.getTenantId())).build();
List<OAuth2TokenValidator<Jwt>> validators = createDefaultValidator();
nimbusJwtDecoder.setJwtValidator(new DelegatingOAuth2TokenValidator<>(validators));
return nimbusJwtDecoder;
}

public List<OAuth2TokenValidator<Jwt>> createDefaultValidator() {
List<OAuth2TokenValidator<Jwt>> validators = new ArrayList<>();
if (!StringUtils.isEmpty(aadAuthenticationProperties.getClientId()) || !StringUtils
.isEmpty(aadAuthenticationProperties.getAppIdUri())) {
List<String> validAudiences = new ArrayList<>();
validAudiences.add(aadAuthenticationProperties.getClientId());
validAudiences.add(aadAuthenticationProperties.getAppIdUri());
validators.add(new AADJwtAudienceValidator(validAudiences));
}
validators.add(new AADJwtTenantValidator(aadAuthenticationProperties.getTenantId(),
aadAuthenticationProperties.getAllowedTenantIds()));
validators.add(new AADJwtIssuerValidator(aadAuthenticationProperties.getTenantId(),
aadAuthenticationProperties.getAllowedTenantIds()));
validators.add(new JwtTimestampValidator());
return validators;
}

/**
* Default configuration class for using AAD authentication and authorization. User can write another configuration
* bean to override it.
*/
@Configuration
@ConditionalOnMissingBean(WebSecurityConfigurerAdapter.class)
@EnableWebSecurity
public static class DefaultAzureOAuth2ResourceServerWebSecurityConfigurerAdapter extends
WebSecurityConfigurerAdapter {

@Override
protected void configure(HttpSecurity http) throws Exception {
http.authorizeRequests((requests) -> requests.anyRequest().authenticated());
http.oauth2ResourceServer(OAuth2ResourceServerConfigurer::jwt);
}
}
}

Original file line number Diff line number Diff line change
Expand Up @@ -11,4 +11,6 @@ public class AADTokenClaim {
public static final String NAME = "name";
public static final String TID = "tid";
public static final String ROLES = "roles";
public static final String ISS = "iss";
public static final String AUD = "aud";
}
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@ org.springframework.boot.env.EnvironmentPostProcessor=com.azure.spring.cloudfoun
org.springframework.boot.autoconfigure.EnableAutoConfiguration=\
com.azure.spring.autoconfigure.aad.AADAuthenticationFilterAutoConfiguration, \
com.azure.spring.autoconfigure.aad.AzureActiveDirectoryAutoConfiguration, \
com.azure.spring.autoconfigure.aad.AADResourceServerAutoConfiguration, \
com.azure.spring.autoconfigure.b2c.AADB2CAutoConfiguration,\
com.azure.spring.autoconfigure.cosmos.CosmosAutoConfiguration,\
com.azure.spring.autoconfigure.cosmos.CosmosHealthConfiguration,\
Expand Down
Loading