After upgrade to spring boot 3.0.3, Swagger authorization stopped working on http://localhost:8080.

Spring Server request URL with spring-webflux 6.0.5 is in resolved IP6 format

The issue is caused by changed method:

private static URI resolveBaseUrl(HttpServerRequest request) throws URISyntaxException {
        String scheme = getScheme(request);

        InetSocketAddress hostAddress = request.hostAddress();
        ...
}

From:

private static URI resolveBaseUrl(HttpServerRequest request) throws URISyntaxException {
        String scheme = getScheme(request);
        String header = request.requestHeaders().get(HttpHeaderNames.HOST);
        ...
}

The root cause of issue is that instead of resolving host to http://localhost, it resolves http://[0:0:0:0:0:0:0:1].

The main functionality of swagger works with this IP address, but authorization fails because it redirects to:

Spring Server request URL with spring-webflux 6.0.5 is in resolved IP6 format

To fix that issue I can set server.address, but then it breaks accessing swagger by external IP.

I found the issue after checking swagger, but rest of application works fine. Should openapi be fixed or resolveBaseUrl method?

Even enforcing IP4 by -Djava.net.preferIPv4Stack=true does not solve issue.

Downgrading to spring-web 6.0.4 solves the issue. Also using IP instead of DSN does not generate such issues.

Comment From: bclozel

I think this is linked to spring-projects/spring-framework#28601.

Transferring this issue to Spring Framework.

Comment From: Tomasz-Marciniak

@bclozel - thanks for handling this.

Comment From: wilkinsona

We suspect that https://github.com/spring-projects/spring-boot/issues/34395 is another symptom of the same underlying problem.

Comment From: rstoyanchev

Indeed, both this issue and https://github.com/spring-projects/spring-boot/issues/34395 are linked to the change in #28601.

I tried the sample from https://github.com/spring-projects/spring-boot/issues/34395, and the case there is an "X-Forwarded-Host" that contains both host and port (e.g. "localhost:3000"). When we parse the "Host" header ourselves, we handle this case. However, when relying on Reactor's request.hostAddress(), the hostString in it has the port and so the java.net.URI constructor fails with such a host. @violetagg, is there another Reactor request method we should use that takes care of this, or should we always parse the hostString we get from requst.hostAddress() just like we parse the "Host" header?

The case reported here is a little different. It looks like request.hostAddress() has a hostString that differs from what's in the "Host" header. I'm not clear on what to do with this one. It seems like the "Host" header has better information in this case, but if we use it first, then we're back to the issue with #28601. @violetagg any insight from your side would be appreciated. Ideally we would use a Reactor API, if available to obtain this information.

Comment From: bclozel

See #30047 for a possible fix involving Netty's ˋNetUtil`.

Comment From: rstoyanchev

30047 looks more of an optimization that avoids an extra URI creation. I'm not sure it solves the issues here, but worth to consider at the same time.

Comment From: rstoyanchev

As expected, Netty's NetUtil doesn't do anything for a hostString that has a port, so #30047 doesn't help for https://github.com/spring-projects/spring-boot/issues/34395, and I suspect it doesn't help for this issue either, but I have no way to confirm that.

@intosoft if you can provide an isolated sample to debug or verify changes with, that would be very helpful. Probably no need to see the authorization failures, but just enough to get such a server URL that differs between 6.0.4 and 6.0.5.

Comment From: violetagg

"X-Forwarded-Host" that contains both host and port (e.g. "localhost:3000")

I think this is not a valid value

https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/X-Forwarded-Host

[Syntax]
X-Forwarded-Host: <host>

[Directives]
<host>
The domain name of the forwarded server.

[Examples]
X-Forwarded-Host: id42.example-cdn.com

Comment From: msosa

But <host> according to them may optionally include a port

https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/Host

The Host request header specifies the host and port number of the server to which the request is being sent.

I would think that X-Forwarded-Host would be closely related to just the Host header

Comment From: violetagg

https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/Host

@msosa

Here I think the <host> corresponds to those in X-Forwarded-Host. It does not contain port, which is a separate directive.

[Syntax]
Host: <host>:<port>

[Directives]
<host>
the domain name of the server (for virtual hosting).

<port> Optional
TCP port number on which the server is listening.

[Examples]
Host: developer.mozilla.org

Comment From: rstoyanchev

Also the port is present in both "X-Forwarded-Port: 3000" and "X-Forwarded-Host: localhost:3000", and it's not clear why does it even needs to be in "X-Forwarded-Host" in the first place, creating ambiguity. Yes, they are the same in this case, but generally when both are present one would have to be preferred over the other. This is why it makes sense that "X-Forwarded-Host" is for the host (only) and "X-Forwarded-Port" is for the port.

Comment From: msosa

I definitely agree that it is odd to have the port in the X-Forwarded-Host, not sure why my frontend proxy sends the header in that way.

I did think that Host could include port and X-Forwarded-Host is just a forwarded header of that. But if that's not the case I'll try and find another proxy that sends the header correctly, or just stick with framework strategy until it changes similarly

Comment From: abelsromero

We found a related issue when running spring-cloud-gateway with oauth spring-security integration. The redirect_uris sent to the auth server now contain the port (even when using 80) and when running with localhost they are sent as 127.0.0.1. That means the admin needs to change the auth server configuration. I narrowed it down to the actual commit related to https://github.com/spring-projects/spring-framework/issues/28601 issue. A custom build of 6.0.5 without that commit works fine.

Comment From: violetagg

We found a related issue when running spring-cloud-gateway with oauth spring-security integration. The redirect_uris sent to the auth server now contain the port (even when using 80) and when running with localhost they are sent as 127.0.0.1. That means the admin needs to change the auth server configuration. I narrowed it down to the actual commit related to #28601 issue. A custom build of 6.0.5 without that commit works fine.

This is related to the first issue isn't it? Not the one with X-Forwarded-For? Is it easy to reproduce it?

Comment From: msosa

Here is an example that reproduces the problem for the first issue(the non X-Forwarded-For issue). The Servers dropdown represents the url as

http://[0:0:0:0:0:0:0:1]:8080 - Generated server url

instead of

http://localhost:8080 - Generated server url

Comment From: abelsromero

This is related to the first issue isn't it? Not the one with X-Forwarded-For?

It seems to me the origin is the same. We see the same "localhost -> IP" change and as I said, I narrowed it down to this actual commit https://github.com/spring-projects/spring-framework/commit/a2b7a907ec8aa5f34a20f533a064067e56ef6967 and confirmed via debugging that we are entering into the new if condition.

Is it easy to reproduce it?

Sadly not, it's a complex setup, including an Okta service.

Comment From: rstoyanchev

Thanks for the sample @msosa. I can't load the index.html page, probably because I'm not running an API server, but the problem is easy to see anyway. The Reactor Netty request.hostAddress() resolves to an IPv6 address while the "Host" header, which we used before has a host name. That's one of the issues.

The other one is the "X-Forwarded-Host" with "host:port", and will be addressed in https://github.com/reactor/reactor-netty/issues/2711.

@abelsromero yes we know it's linked to that change, but it isn't as simple as reverting it, and we need to understand the scenarios in order to fix the issues for each.

Comment From: violetagg

For the issue with X-Forwarded-Host, are you able to test Reactor Netty 1.0.29-SNAPSHOT/1.1.4-SNAPSHOT (https://repo.spring.io/snapshot)

Comment From: rstoyanchev

I've scheduled this for 6.0.6, as we are expecting a change in Reactor Netty to provide a hostName and hostPort, and we are going to build on it.

The X-Forwarded-Host issue should already have a fix in the latest Reactor Netty 1.1.4 snapshot. @msosa, I've checked with your sample but if you can also confirm from your end, that would be great.

Comment From: msosa

Yes, the enhancement works on my project as well, thank you!

Something to note though, when I set my UI port to 80 and go to http://localhost the OAuth redirect URI is generated with http://localhost:80 instead of http://localhost. Not sure if this is something that needs to be looked at or not, but if you would like a repo for this I can post one.

Comment From: violetagg

Yes, the enhancement works on my project as well, thank you!

Something to note though, when I set my UI port to 80 and go to http://localhost the OAuth redirect URI is generated with http://localhost:80 instead of http://localhost. Not sure if this is something that needs to be looked at or not, but if you would like a repo for this I can post one.

This will be addressed with the changes that we are preparing for the other issue.

Comment From: rstoyanchev

We now have https://github.com/reactor/reactor-netty/issues/2711 and https://github.com/reactor/reactor-netty/pull/2714 in place, and also #30062 to take advantage of the new hostName and hostPort request properties. So in effect this issue is now superceded by #30062.

Comment From: rstoyanchev

There is now a 6.0.6 snapshot available with the changes. If you have the option to test, please give it a try together with Reactor-Bom 2022.0.4 snapshot.

I'll close this as superseded, but please feel free to add more comments.

Comment From: rstoyanchev

Thanks @msosa and @abelsromero for confirming the changes.