fix: handle HTTPS redirects in InsecureTlsHelper for OIDC discovery
All checks were successful
CI / cleanup-branch (push) Has been skipped
CI / build (push) Successful in 1m13s
CI / docker (push) Successful in 42s
CI / deploy-feature (push) Has been skipped
CI / deploy (push) Successful in 38s

Java's automatic redirect following creates new connections that do NOT
inherit custom SSLSocketFactory/HostnameVerifier. This caused the OIDC
discovery fetch to fail on redirect even with TLS_SKIP_VERIFY=true.
Now disables auto-redirect and follows manually with SSL on each hop.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
This commit is contained in:
hsiegeln
2026-04-06 00:51:49 +02:00
parent 083cb8b9ec
commit 99e2a8354f
2 changed files with 30 additions and 5 deletions

View File

@@ -10,6 +10,8 @@ import javax.net.ssl.SSLSocketFactory;
import javax.net.ssl.TrustManager; import javax.net.ssl.TrustManager;
import javax.net.ssl.X509TrustManager; import javax.net.ssl.X509TrustManager;
import java.io.InputStream; import java.io.InputStream;
import java.net.HttpURLConnection;
import java.net.URI;
import java.net.URL; import java.net.URL;
import java.net.URLConnection; import java.net.URLConnection;
import java.security.SecureRandom; import java.security.SecureRandom;
@@ -59,13 +61,33 @@ final class InsecureTlsHelper {
/** /**
* Opens an {@link InputStream} from the given URL, applying trust-all TLS * Opens an {@link InputStream} from the given URL, applying trust-all TLS
* if the connection is HTTPS and {@code skipVerify} is true. * if the connection is HTTPS and {@code skipVerify} is true.
* <p>
* Handles redirects manually because Java's automatic redirect following
* creates new connections that do NOT inherit custom SSLSocketFactory /
* HostnameVerifier settings.
*/ */
static InputStream openStream(URL url, boolean skipVerify) throws Exception { static InputStream openStream(URL url, boolean skipVerify) throws Exception {
URLConnection conn = url.openConnection(); URL current = url;
for (int i = 0; i < 5; i++) {
HttpURLConnection conn = (HttpURLConnection) current.openConnection();
if (skipVerify && conn instanceof HttpsURLConnection https) { if (skipVerify && conn instanceof HttpsURLConnection https) {
https.setSSLSocketFactory(socketFactory()); https.setSSLSocketFactory(socketFactory());
https.setHostnameVerifier(hostnameVerifier()); https.setHostnameVerifier(hostnameVerifier());
} }
conn.setInstanceFollowRedirects(false);
int code = conn.getResponseCode();
if (code >= 300 && code < 400) {
String location = conn.getHeaderField("Location");
conn.disconnect();
if (location == null) {
throw new java.io.IOException("Redirect with no Location header from " + current);
}
current = new URI(location).toURL();
log.debug("Following OIDC redirect -> {}", current);
continue;
}
return conn.getInputStream(); return conn.getInputStream();
} }
throw new java.io.IOException("Too many redirects from " + url);
}
} }

View File

@@ -61,6 +61,9 @@ public class OidcTokenExchanger {
SecurityProperties securityProperties) { SecurityProperties securityProperties) {
this.configRepository = configRepository; this.configRepository = configRepository;
this.tlsSkipVerify = securityProperties.isOidcTlsSkipVerify(); this.tlsSkipVerify = securityProperties.isOidcTlsSkipVerify();
if (tlsSkipVerify) {
log.warn("OIDC TLS skip-verify enabled for token exchanger");
}
} }
public record OidcUserInfo(String subject, String email, String name, List<String> roles, String idToken) {} public record OidcUserInfo(String subject, String email, String name, List<String> roles, String idToken) {}