-
Notifications
You must be signed in to change notification settings - Fork 5.9k
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
OAuth2 client: default redirection to login page is done on wrong socket when SSL is enabled (authorization-server instead of client) #12307
Comments
@ch4mpy sorry for the delay on this. Does this issue only occur when both client/server run on localhost? Does the behavior change if you use 127.0.0.1 as the host for the client and configure the redirect uri accordingly in Keycloak? Have you tried configuring If that doesn't yield a result, is there an easier way to set up a reproducing test than manually configuring SSL for both client/server? Can you provide a minimal sample with a test SSL setup and a mock oauth2 server? I think demonstrating the first incorrect redirect would be enough to get started triaging the issue. |
I have no easy access to a hosted server to make this test.
Not on my point of interest: I only get very inconvenient warnings when browsing over https by IP rather than hostname listed in the self-signed certificate altnames (certificate is added to my OS trusted root certificates)
No. but as ports 8080 and 8443 are already used by respectively resource-server and authorization-server, I don't really understand the point :/
The code source included in the description is a complete reproducer (but it is based on a real authorization-server)
Not that I know of. But I think it is acceptable as, with the right tools, it can take less than 5 minutes to generate a new certificate and start Keycloak and Spring client with SSL... Generating a self-signed certificate and adding it to JDK(s)
|
@sjohnr I updated my previous comment to better answer your questions |
@ch4mpy, I've looked into this and set up an OAuth2 client configured for SSL and an authorization server also configured for SSL. When configuring the client to run on port 8080, I do see the behavior you've outlined. However, this behavior would exist in any application with Spring Security. Consider the following security config: @Configuration
@EnableWebSecurity
public class SecurityConfig {
@Bean
public SecurityFilterChain securityFilterChain(HttpSecurity http) throws Exception {
http
.authorizeHttpRequests((authorize) -> authorize
.anyRequest().authenticated()
)
.formLogin(Customizer.withDefaults());
return http.build();
}
} When the application is configured to run on port 8080 with SSL enabled, visiting https://localhost:8080 I am redirected to https://localhost:8443/login. The reason for this is the default I don't seem to find much information on this topic in the reference, so this is a topic that could use some better documentation. If we don't wish to accept the defaults, we do have to configure Spring Security to use port 8080. You would do so by defining that you use port 8080 as the SSL port, like so: @Bean
public SecurityFilterChain securityFilterChain(HttpSecurity http) throws Exception {
http
// ...
.portMapper((ports) -> ports
.http(8080).mapsTo(8080)
);
return http.build();
} I would expect this to be enough to configure redirects to work properly. However, it appears this I think the solution would be to allow the above configuration to configure 8080 as the SSL port, if you really want to do so. It should be used by both the However, 8443 is the default for SSL and nothing would tell Spring Security you wish to use 8080 unless you configure it. I believe this makes sense since an application could be serving traffic on both 8080 and 8443 simultaneously and the defaults make sense in this (very common) case. In the meantime, you can work around this with the following configuration: @Configuration
@EnableWebSecurity
public class SecurityConfig {
@Bean
public SecurityFilterChain securityFilterChain(HttpSecurity http) throws Exception {
http
// ...
.oauth2Login(Customizer.withDefaults())
.exceptionHandling((exceptions) -> exceptions
.authenticationEntryPoint(authenticationEntryPoint())
);
return http.build();
}
private AuthenticationEntryPoint authenticationEntryPoint() {
PortMapperImpl portMapper = new PortMapperImpl();
portMapper.setPortMappings(Map.of("8080", "8080"));
PortResolverImpl portResolver = new PortResolverImpl();
portResolver.setPortMapper(portMapper);
LoginUrlAuthenticationEntryPoint authenticationEntryPoint =
new LoginUrlAuthenticationEntryPoint(
"/oauth2/authorization/spring-addons-public");
authenticationEntryPoint.setPortMapper(portMapper);
authenticationEntryPoint.setPortResolver(portResolver);
return authenticationEntryPoint;
}
} |
When I configure At minimum when using Spring-boot, couldn't auto-configuration pick server host and port and set spring-security defaults accordingly? |
I think that would be a spring boot question though given everything else we’re discussing I wouldn’t expect that to be a feature request that would be considered.
Keep in mind that just specifying port 8080 is not enough in an SSL scenario because you also need to redirect from an http port to an https port for browser based apps (which an OAuth client is). Your configuration does not account for this, which makes it a non-standard setup. |
Unless I want spring apps to be served with SSL only (end-to-end encryption), case where I am happy with boot default binding to a single port, even with https enabled. I let the Middleware handle redirections.
When starting a spring-boot app with SSL enabled and no port specified, the app is bound to 8080 as usual.
Wouldn't |
@sjohnr appart from the issue related to clients served with server:
port: 8080
ssl:
enabled: false
spring:
security:
oauth2:
client:
registration:
spring-addons-public:
client-id: "spring-addons-public"
client-secret: ""
client-name: "spring-addons-public"
provider: "keycloak"
scope:
- "openid"
- "profile"
client-authentication-method: "none"
authorization-grant-type: "authorization_code"
provider:
keycloak:
issuer-uri: "http://localhost:8442/realms/master"
authorization-uri: "http://localhost:8442/realms/master/protocol/openid-connect/auth"
token-uri: "http://localhost:8442/realms/master/protocol/openid-connect/token" Things work as expected Now, if I switch provider URIs to https://localhost:8443, things go wrong after user is redirected back to client with authorization-code:
I'd expect step 4. to be a redirect to http://localhost:8080/?continue (with tokens fetched by the client and attached to the user session between 3. and 4.) |
Spring security does not know about Spring Boot configuration as it can be used with and without Spring Boot. Security only knows that 8080 is used for SSL based on configuration as discussed above.
ServerProperties is a Spring Boot class which isn’t part of spring-security. It sounds like you are asking for features like the one discussed above that would be part of Boot auto configuration. If so, please open an issue on the boot issue tracker. If you believe you’ve found another bug/issue, please open a separate issue. |
It is interesting to note that this "feature" of rewriting the port of client URIs involved in login process from And honestly, I don't understand why the port should be modified when building redirection URIs: if the current request is being processed on the client with an
I don't agree with that (no more than 8080 is default for http). To me, defaults are 443 for https and 80 for http. 8443 is only a frequent usage for web apps started as user process and served with https (specially those historically based on Tomcat), and this does not apply to Spring Boot apps which are bound to 8080 by default, no matter if I'm not sure spring-security should make any assumption on the ports my Spring applications bind to (it being Boot or not), and It feels unnatural to me that spring-security is not configured by Boot with the server port (again, I understood this is not the place to complain about the second point).
What would be the point, considering that you (I mean spring-security team) decide what is done regarding security configuration in boot, and:
Let's save Spring Boot team some time and post here 2 workarounds for spring-security assumptions on the ports used by servlets. |
Hello @ch4mpy! I'd like to start by apologizing for the difficulties you are having and thanking you for your thorough feedback & posting your workarounds. The behavior you are seeing is indeed confusing and is an unfortunate result of a long history around redirects and ports. Given how long this logic has been in place, @sjohnr reached out to me to help since I have been working on Spring Security longer. Absolute Redirects vs Relative Redirects A large part of the problem is that Spring Security was written to conform with RFC2616 which requires that the location header be an absolute URI. In order to create an absolute URI, Spring Security must lookup the port to redirect to. At the time the APIs were created (and for quite some time afterwards), IE had a bug which caused To work around the IE bug, the When Absolute URIs Are Required There are places where absolute URIs are still necessary. This is typically an absolute URL needed for an exchange between an Identity Provider (IdP) so that after the user is authenticated with the IdP, the IdP is able to redirect back to the originating application. For consistency within the Servlet APIs, the WebFlux You won’t find the same problems in WebFlux because, whenever possible, it uses relative redirects which do not require a port. When an absolute URI is required, the actual port can be used because the IE bug has been fixed, IE is not nearly as popular as it once was, and it is consistent with the rest of WebFlux. It's also important to point out that in What to do? All that said, the issue you describe is still a problem. I acknowledge your comment that However, I’d prefer to spend the time migrating to an approach that does not require I hope this answers your questions around what is happening and why, and more importantly how we can ensure others do not have the same problems you are having. If you are agreeable to it, I'd like to close this issue out in favor of gh-12971 because it should fully address the issue throughout the portfolio. |
I wanted to apologize to @sjohnr too, it always takes me ages and dozens of edits to clarify my point. Sorry about that. |
Thanks @ch4mpy! No problem. |
I think the issue is https://github.com/spring-projects/spring-security/blame/22000b42e9e84d2a8b5479b076dbdf1998a91e17/config/src/main/java/org/springframework/security/config/annotation/web/configurers/oauth2/client/OAuth2LoginConfigurer.java#L565 which simply creates a |
Describe the bug
SSL is enabled by default for my spring-boot apps (I have set
SERVER_SSL_KEY_PASSWORD
,SERVER_SSL_KEY_STORE
andSERVER_SSL_KEY_STORE_PASSWORD
environement variables).I configured a very simple OAuth2 client with
oauth2Login
. Spring client application runs on port 8080 and authorization-server (Keycloak) on port 8443. When trying to first access a secured page, I'm redirected to spring-boot generated login page on wrong socket: authorization-server socket instead of client socket (https://localhost:8443/oauth2/authorization/spring-addons-public when I'd expect https://localhost:8080/oauth2/authorization/spring-addons-public).If I manually visit https://localhost:8080/oauth2/authorization/spring-addons-public, authorization-code flow is successful but I'm last redirected to the wrong socket again (sent to authorization-server index instead of client's one).
Interestingly enough, redirections are done on the right port if I explicitly disable SSL (
server.ssl.enabled=false
).To Reproduce
spring-addons-public
client (with authorization-code flow enabled and no client secret)oauth2Login
(detailed configuration below)Expected behavior
First redirection should be to https://localhost:8080/oauth2/authorization/spring-addons-public to initiate authorization-code flow from generated login page (instantly followed by another redirection to https://localhost:8443/realms/master/protocol/openid-connect/auth as there is only one provider in my conf)
Workarounds
When using a port that is not one of the two registered in
PortMapperImpl
default constructor, there is no alteration. So, the easiest solution, by far, is to use any port but 80 or 8080 when SSL is enabled.PortMapperImpl
is initialised with 80 -> 443 and 8080 -> 8443 and components likeLoginUrlAuthenticationEntryPoint
alter the request port using this mapper, and even if you define your own port mapper in the application context, it is not picked => you'll have to configure it explicitly as done below by @sjohnr (but 1. it gets funny when you have several client registrations, and 2. reconfiguring just the authentication entry-point is not enough as there are other places where the port is altered byPortMapperImpl
).Port mapper being ignored with absolute URIs, the second option is to use absolute URIs for login page, post login redirection URIs and inside authorization request resolver.
Warning: explicitly configuring the
loginPage
also requires to implement a controller to handle it.Then in the OAuth2 client configuration, I can use this ${base-uri} like that:
This is lot of configuration code for a Boot application, but I isolated it in alternate starters wrapping
spring-boot-starter-oauth2-client
which makes my OAuth2 clients "bootiful" enough (configurable from properties with little to no Java conf). For the curious, the starters are here for servlets and there for reactive apps. Both come with additional "worthless" auto-configuration controlled with properties (0 Java conf):GrantedAuthoritiesMapper
in each append_session_endpoint
in OpenID configuration). Auth0 and Amazon Cognito are samples of such OPspermitAll
for a list of path matchers andauthenticated
as default (to be fine tuned with method security or a configuration post-processor bean)To Reproduce (the issue, not the workarounds)
src/main/resources/static/index.html
:The text was updated successfully, but these errors were encountered: