-
Notifications
You must be signed in to change notification settings - Fork 141
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Add support for audience parameter #600
base: send-audience
Are you sure you want to change the base?
Changes from all commits
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -15,7 +15,7 @@ | |
*/ | ||
package com.okta.spring.boot.oauth; | ||
|
||
import org.springframework.beans.factory.annotation.Value; | ||
import com.okta.spring.boot.oauth.config.OktaOAuth2Properties; | ||
import org.springframework.http.HttpStatus; | ||
import org.springframework.http.MediaType; | ||
import org.springframework.security.config.annotation.web.builders.HttpSecurity; | ||
|
@@ -24,6 +24,7 @@ | |
import org.springframework.security.oauth2.client.registration.ClientRegistrationRepository; | ||
import org.springframework.security.oauth2.client.registration.ReactiveClientRegistrationRepository; | ||
import org.springframework.security.oauth2.client.web.DefaultOAuth2AuthorizationRequestResolver; | ||
import org.springframework.security.oauth2.client.web.OAuth2AuthorizationRequestResolver; | ||
import org.springframework.security.oauth2.client.web.server.DefaultServerOAuth2AuthorizationRequestResolver; | ||
import org.springframework.security.oauth2.client.web.server.ServerOAuth2AuthorizationRequestResolver; | ||
import org.springframework.security.oauth2.core.OAuth2AuthenticationException; | ||
|
@@ -102,12 +103,6 @@ public static ServerHttpSecurity configureOAuth2WithPkce(ServerHttpSecurity http | |
authorizationRequestResolver.setAuthorizationRequestCustomizer(withPkce()); | ||
// enable oauth2 login that uses PKCE | ||
http.oauth2Login().authorizationRequestResolver(authorizationRequestResolver); | ||
// enable passing the audience parameter | ||
http.oauth2Login(oauth2 -> oauth2 | ||
.authorizationRequestResolver( | ||
authorizeRequestResolver(clientRegistrationRepository) | ||
) | ||
); | ||
|
||
return http; | ||
} | ||
|
@@ -135,6 +130,61 @@ public static HttpSecurity configureOAuth2WithPkce(HttpSecurity http, ClientRegi | |
return http; | ||
} | ||
|
||
/** | ||
* Configures the {@code http} with an OAuth2 Login, that sends an audience parameter with the authorize request. | ||
* | ||
* @param http the HttpSecurity to configure | ||
* @param clientRegistrationRepository the repository bean, this should be injected into the calling method. | ||
* @param properties the OktaOAuth2Properties bean, this should be injected into the calling method. | ||
* @return the {@code http} to allow method chaining | ||
*/ | ||
public static ServerHttpSecurity configureOAuth2WithAudience(ServerHttpSecurity http, | ||
ReactiveClientRegistrationRepository clientRegistrationRepository, | ||
OktaOAuth2Properties properties) throws Exception { | ||
// Don't add audience parameter if empty or default | ||
if (isDefaultAudience(properties)) { | ||
return http; | ||
} | ||
http.oauth2Login(oauth2 -> oauth2 | ||
.authorizationRequestResolver( | ||
reactiveAuthzRequestResolver(clientRegistrationRepository, properties.getAudience()) | ||
) | ||
); | ||
|
||
return http; | ||
} | ||
|
||
private static boolean isDefaultAudience(OktaOAuth2Properties properties) { | ||
String audience = properties.getAudience(); | ||
return audience == null || audience.isEmpty() || audience.equals("api://default"); | ||
} | ||
|
||
/** | ||
* Configures the {@code http} with an OAuth2 Login, that sends an audience parameter with the authorize request. | ||
* | ||
* @param http the HttpSecurity to configure | ||
* @param clientRegistrationRepository the repository bean, this should be injected into the calling method. | ||
* @param properties the OktaOAuth2Properties bean, this should be injected into the calling method. | ||
* @return the {@code http} to allow method chaining | ||
*/ | ||
public static HttpSecurity configureOAuth2WithAudience(HttpSecurity http, | ||
ClientRegistrationRepository clientRegistrationRepository, | ||
OktaOAuth2Properties properties) throws Exception { | ||
// Don't add audience parameter if empty or default | ||
if (isDefaultAudience(properties)) { | ||
return http; | ||
} | ||
http.oauth2Login(oauth2 -> oauth2 | ||
.authorizationEndpoint(authorization -> authorization | ||
.authorizationRequestResolver( | ||
mvcAuthzRequestResolver(clientRegistrationRepository, properties.getAudience()) | ||
) | ||
) | ||
); | ||
|
||
return http; | ||
} | ||
|
||
private static RequestMatcher textRequestMatcher() { | ||
return new MediaTypeRequestMatcher(new HeaderContentNegotiationStrategy(), MediaType.TEXT_PLAIN); | ||
} | ||
|
@@ -162,22 +212,31 @@ static String statusAsString(HttpStatus status) { | |
return status.value() + " " + status.getReasonPhrase(); | ||
} | ||
|
||
@Value("${okta.oauth2.audience:}") | ||
private static String audience; | ||
|
||
private static ServerOAuth2AuthorizationRequestResolver authorizeRequestResolver( | ||
ReactiveClientRegistrationRepository clientRegistrationRepository) { | ||
private static ServerOAuth2AuthorizationRequestResolver reactiveAuthzRequestResolver( | ||
ReactiveClientRegistrationRepository clientRegistrationRepository, String audience) { | ||
|
||
DefaultServerOAuth2AuthorizationRequestResolver authorizationRequestResolver = | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. How do you get the |
||
new DefaultServerOAuth2AuthorizationRequestResolver( | ||
clientRegistrationRepository); | ||
authorizationRequestResolver.setAuthorizationRequestCustomizer( | ||
authorizeRequestCustomizer()); | ||
authorizationRequestCustomizer(audience)); | ||
|
||
return authorizationRequestResolver; | ||
} | ||
|
||
private static OAuth2AuthorizationRequestResolver mvcAuthzRequestResolver( | ||
ClientRegistrationRepository clientRegistrationRepository, String audience) { | ||
|
||
DefaultOAuth2AuthorizationRequestResolver authorizationRequestResolver = | ||
new DefaultOAuth2AuthorizationRequestResolver( | ||
clientRegistrationRepository, "/oauth2/authorization"); | ||
authorizationRequestResolver.setAuthorizationRequestCustomizer( | ||
authorizationRequestCustomizer(audience)); | ||
|
||
return authorizationRequestResolver; | ||
} | ||
|
||
private static Consumer<OAuth2AuthorizationRequest.Builder> authorizeRequestCustomizer() { | ||
private static Consumer<OAuth2AuthorizationRequest.Builder> authorizationRequestCustomizer(String audience) { | ||
return customizer -> customizer | ||
.additionalParameters(params -> params.put("audience", audience)); | ||
} | ||
|
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -79,11 +79,12 @@ OAuth2UserService<OidcUserRequest, OidcUser> oidcUserService( | |
static class OAuth2SecurityFilterChainConfiguration { | ||
|
||
@Bean | ||
SecurityFilterChain oauth2SecurityFilterChain(HttpSecurity http, ClientRegistrationRepository clientRegistrationRepository) throws Exception { | ||
SecurityFilterChain oauth2SecurityFilterChain(HttpSecurity http, ClientRegistrationRepository clientRegistrationRepository, OktaOAuth2Properties oktaOAuth2Properties) throws Exception { | ||
// as of Spring Security 5.4 the default chain uses oauth2Login OR a JWT resource server (NOT both) | ||
// this does the same as both defaults merged together (and provides the previous behavior) | ||
http.authorizeRequests((requests) -> requests.anyRequest().authenticated()); | ||
Okta.configureOAuth2WithPkce(http, clientRegistrationRepository); | ||
Okta.configureOAuth2WithAudience(http, clientRegistrationRepository, oktaOAuth2Properties); | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. The audience overrides the PKCE parameters added on the previous line. Is there a way to chain these together so both parameters are added? |
||
http.oauth2Client(); | ||
http.oauth2ResourceServer(OAuth2ResourceServerConfigurer::jwt); | ||
return http.build(); | ||
|
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -17,10 +17,8 @@ | |
|
||
import com.okta.spring.boot.oauth.config.OktaOAuth2Properties; | ||
import org.springframework.beans.factory.annotation.Qualifier; | ||
import org.springframework.beans.factory.annotation.Value; | ||
import org.springframework.boot.autoconfigure.AutoConfiguration; | ||
import org.springframework.boot.autoconfigure.AutoConfigureBefore; | ||
import org.springframework.boot.autoconfigure.condition.ConditionalOnBean; | ||
import org.springframework.boot.autoconfigure.condition.ConditionalOnClass; | ||
import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean; | ||
import org.springframework.boot.autoconfigure.condition.ConditionalOnWebApplication; | ||
|
@@ -52,9 +50,6 @@ | |
@Import(AuthorityProvidersConfig.class) | ||
class ReactiveOktaOAuth2AutoConfig { | ||
|
||
@Value("${okta.oauth2.audience:}") | ||
private String audience; | ||
|
||
@Bean | ||
@ConditionalOnMissingBean | ||
ReactiveOAuth2UserService<OAuth2UserRequest, OAuth2User> oauth2UserService(Collection<AuthoritiesProvider> authoritiesProviders) { | ||
|
@@ -69,13 +64,13 @@ OidcReactiveOAuth2UserService oidcUserService(Collection<AuthoritiesProvider> au | |
} | ||
|
||
@Bean | ||
@ConditionalOnBean(ReactiveJwtDecoder.class) | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Removing this was necessary to invoke this configuration when no |
||
@ConditionalOnMissingBean(SecurityWebFilterChain.class) | ||
SecurityWebFilterChain springSecurityFilterChain(ServerHttpSecurity http, ReactiveJwtDecoder jwtDecoder, ReactiveClientRegistrationRepository clientRegistrationRepository) { | ||
SecurityWebFilterChain springSecurityFilterChain(ServerHttpSecurity http, ReactiveJwtDecoder jwtDecoder, ReactiveClientRegistrationRepository clientRegistrationRepository, OktaOAuth2Properties properties) throws Exception { | ||
// as of Spring Security 5.4 the default chain uses oauth2Login OR a JWT resource server (NOT both) | ||
// this does the same as both defaults merged together (and provides the previous behavior) | ||
http.authorizeExchange().anyExchange().authenticated(); | ||
Okta.configureOAuth2WithPkce(http, clientRegistrationRepository); | ||
Okta.configureOAuth2WithAudience(http, clientRegistrationRepository, properties); | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. This line clobbers the previous lines added parameters. We need to figure out a way to chain them together. There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. @arvindkrishnakumar-okta It seems like we could create the There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. @mraible I think that is the only way out here to resolve the clobbering. There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Would it be possible to overload the method with new signature and deprecate the current one? |
||
http.oauth2Client(); | ||
http.oauth2ResourceServer((server) -> customDecoder(server, jwtDecoder)); | ||
return http.build(); | ||
|
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This means that
api://default
will never be sent if it's the audience. I tried to make it so it only happens when it's Auth0, but was unable to figure out how to do it.There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I guess it should not be a problem sending it for Okta too.