Group OIDC settings under cameleer.server.security.oidc.*
Some checks failed
CI / cleanup-branch (push) Has been skipped
CI / build (push) Successful in 1m59s
CI / deploy (push) Has been cancelled
CI / deploy-feature (push) Has been cancelled
CI / docker (push) Has been cancelled

Move OIDC properties into a nested Oidc class within SecurityProperties
for clearer grouping. Env vars gain an extra separator:

  cameleer.server.security.oidc.issueruri     → CAMELEER_SERVER_SECURITY_OIDC_ISSUERURI
  cameleer.server.security.oidc.jwkseturi     → CAMELEER_SERVER_SECURITY_OIDC_JWKSETURI
  cameleer.server.security.oidc.audience      → CAMELEER_SERVER_SECURITY_OIDC_AUDIENCE
  cameleer.server.security.oidc.tlsskipverify → CAMELEER_SERVER_SECURITY_OIDC_TLSSKIPVERIFY

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
This commit is contained in:
hsiegeln
2026-04-11 21:30:33 +02:00
parent 60fb5fe21a
commit 534e936cd4
5 changed files with 35 additions and 27 deletions

View File

@@ -59,7 +59,7 @@ public class UserAdminController {
this.rbacService = rbacService;
this.userRepository = userRepository;
this.auditService = auditService;
String issuer = securityProperties.getOidcIssuerUri();
String issuer = securityProperties.getOidc().getIssuerUri();
this.oidcEnabled = issuer != null && !issuer.isBlank();
}

View File

@@ -88,7 +88,7 @@ public class OidcTokenExchanger {
body += "&resource=" + java.net.URLEncoder.encode(configAudience, java.nio.charset.StandardCharsets.UTF_8);
httpRequest.setBody(body);
}
if (securityProperties.isOidcTlsSkipVerify()) {
if (securityProperties.getOidc().isTlsSkipVerify()) {
httpRequest.setSSLSocketFactory(InsecureTlsHelper.socketFactory());
httpRequest.setHostnameVerifier(InsecureTlsHelper.hostnameVerifier());
}
@@ -267,7 +267,7 @@ public class OidcTokenExchanger {
OIDCProviderMetadata metadata = getProviderMetadata(issuerUri);
URL jwksUrl = metadata.getJWKSetURI().toURL();
JWKSource<SecurityContext> jwkSource = OidcProviderHelper.buildJwkSource(
jwksUrl, securityProperties.isOidcTlsSkipVerify());
jwksUrl, securityProperties.getOidc().isTlsSkipVerify());
var keySelector = new JWSVerificationKeySelector<SecurityContext>(
OidcProviderHelper.SUPPORTED_ALGORITHMS, jwkSource);
ConfigurableJWTProcessor<SecurityContext> processor = new DefaultJWTProcessor<>();
@@ -286,7 +286,7 @@ public class OidcTokenExchanger {
synchronized (this) {
if (providerMetadata == null || !issuerUri.equals(cachedIssuerUri)) {
providerMetadata = OidcProviderHelper.fetchMetadata(
issuerUri, securityProperties.isOidcTlsSkipVerify());
issuerUri, securityProperties.getOidc().isTlsSkipVerify());
cachedIssuerUri = issuerUri;
jwtProcessor = null; // Reset processor when issuer changes
log.info("OIDC provider metadata loaded from {}", issuerUri);
@@ -303,7 +303,7 @@ public class OidcTokenExchanger {
OIDCProviderMetadata metadata = getProviderMetadata(issuerUri);
URL jwksUrl = metadata.getJWKSetURI().toURL();
JWKSource<SecurityContext> jwkSource = OidcProviderHelper.buildJwkSource(
jwksUrl, securityProperties.isOidcTlsSkipVerify());
jwksUrl, securityProperties.getOidc().isTlsSkipVerify());
var keySelector = new JWSVerificationKeySelector<SecurityContext>(
OidcProviderHelper.SUPPORTED_ALGORITHMS, jwkSource);

View File

@@ -63,7 +63,7 @@ public class SecurityConfig {
CorsConfigurationSource corsConfigurationSource,
UserRepository userRepository) throws Exception {
JwtDecoder oidcDecoder = null;
String issuer = securityProperties.getOidcIssuerUri();
String issuer = securityProperties.getOidc().getIssuerUri();
if (issuer != null && !issuer.isBlank()) {
try {
oidcDecoder = buildOidcDecoder(securityProperties);
@@ -155,22 +155,22 @@ public class SecurityConfig {
* {@code at+jwt} token type (RFC 9068) by accepting any JWT type.
*/
private JwtDecoder buildOidcDecoder(SecurityProperties properties) throws Exception {
String issuerUri = properties.getOidcIssuerUri();
String issuerUri = properties.getOidc().getIssuerUri();
// Resolve JWKS URI: use explicit config if set, otherwise discover from OIDC metadata.
URL jwksUri;
String jwkSetUri = properties.getOidcJwkSetUri();
String jwkSetUri = properties.getOidc().getJwkSetUri();
if (jwkSetUri != null && !jwkSetUri.isBlank()) {
jwksUri = new URI(jwkSetUri).toURL();
log.info("Using explicit JWKS URI: {}", jwksUri);
} else {
OIDCProviderMetadata metadata = OidcProviderHelper.fetchMetadata(
issuerUri, properties.isOidcTlsSkipVerify());
issuerUri, properties.getOidc().isTlsSkipVerify());
jwksUri = metadata.getJWKSetURI().toURL();
}
JWKSource<SecurityContext> jwkSource = OidcProviderHelper.buildJwkSource(
jwksUri, properties.isOidcTlsSkipVerify());
jwksUri, properties.getOidc().isTlsSkipVerify());
var keySelector = new JWSVerificationKeySelector<SecurityContext>(
OidcProviderHelper.SUPPORTED_ALGORITHMS, jwkSource);
var processor = new DefaultJWTProcessor<SecurityContext>();
@@ -181,7 +181,7 @@ public class SecurityConfig {
// Validate issuer + optionally audience
OAuth2TokenValidator<Jwt> validators;
String audience = properties.getOidcAudience();
String audience = properties.getOidc().getAudience();
if (audience != null && !audience.isBlank()) {
validators = new DelegatingOAuth2TokenValidator<>(
JwtValidators.createDefaultWithIssuer(issuerUri),

View File

@@ -17,11 +17,24 @@ public class SecurityProperties {
private String uiPassword;
private String uiOrigin;
private String jwtSecret;
private String oidcIssuerUri;
private String oidcJwkSetUri;
private String oidcAudience;
private boolean oidcTlsSkipVerify;
private String corsAllowedOrigins;
private Oidc oidc = new Oidc();
public static class Oidc {
private String issuerUri;
private String jwkSetUri;
private String audience;
private boolean tlsSkipVerify;
public String getIssuerUri() { return issuerUri; }
public void setIssuerUri(String issuerUri) { this.issuerUri = issuerUri; }
public String getJwkSetUri() { return jwkSetUri; }
public void setJwkSetUri(String jwkSetUri) { this.jwkSetUri = jwkSetUri; }
public String getAudience() { return audience; }
public void setAudience(String audience) { this.audience = audience; }
public boolean isTlsSkipVerify() { return tlsSkipVerify; }
public void setTlsSkipVerify(boolean tlsSkipVerify) { this.tlsSkipVerify = tlsSkipVerify; }
}
public long getAccessTokenExpiryMs() { return accessTokenExpiryMs; }
public void setAccessTokenExpiryMs(long accessTokenExpiryMs) { this.accessTokenExpiryMs = accessTokenExpiryMs; }
@@ -39,14 +52,8 @@ public class SecurityProperties {
public void setUiOrigin(String uiOrigin) { this.uiOrigin = uiOrigin; }
public String getJwtSecret() { return jwtSecret; }
public void setJwtSecret(String jwtSecret) { this.jwtSecret = jwtSecret; }
public String getOidcIssuerUri() { return oidcIssuerUri; }
public void setOidcIssuerUri(String oidcIssuerUri) { this.oidcIssuerUri = oidcIssuerUri; }
public String getOidcJwkSetUri() { return oidcJwkSetUri; }
public void setOidcJwkSetUri(String oidcJwkSetUri) { this.oidcJwkSetUri = oidcJwkSetUri; }
public String getOidcAudience() { return oidcAudience; }
public void setOidcAudience(String oidcAudience) { this.oidcAudience = oidcAudience; }
public boolean isOidcTlsSkipVerify() { return oidcTlsSkipVerify; }
public void setOidcTlsSkipVerify(boolean oidcTlsSkipVerify) { this.oidcTlsSkipVerify = oidcTlsSkipVerify; }
public String getCorsAllowedOrigins() { return corsAllowedOrigins; }
public void setCorsAllowedOrigins(String corsAllowedOrigins) { this.corsAllowedOrigins = corsAllowedOrigins; }
public Oidc getOidc() { return oidc; }
public void setOidc(Oidc oidc) { this.oidc = oidc; }
}

View File

@@ -69,11 +69,12 @@ cameleer:
uipassword: ${CAMELEER_SERVER_SECURITY_UIPASSWORD:admin}
uiorigin: ${CAMELEER_SERVER_SECURITY_UIORIGIN:http://localhost:5173}
jwtsecret: ${CAMELEER_SERVER_SECURITY_JWTSECRET:}
oidcissueruri: ${CAMELEER_SERVER_SECURITY_OIDCISSUERURI:}
oidcjwkseturi: ${CAMELEER_SERVER_SECURITY_OIDCJWKSETURI:}
oidcaudience: ${CAMELEER_SERVER_SECURITY_OIDCAUDIENCE:}
oidctlsskipverify: ${CAMELEER_SERVER_SECURITY_OIDCTLSSKIPVERIFY:false}
corsallowedorigins: ${CAMELEER_SERVER_SECURITY_CORSALLOWEDORIGINS:}
oidc:
issueruri: ${CAMELEER_SERVER_SECURITY_OIDC_ISSUERURI:}
jwkseturi: ${CAMELEER_SERVER_SECURITY_OIDC_JWKSETURI:}
audience: ${CAMELEER_SERVER_SECURITY_OIDC_AUDIENCE:}
tlsskipverify: ${CAMELEER_SERVER_SECURITY_OIDC_TLSSKIPVERIFY:false}
clickhouse:
url: ${CAMELEER_SERVER_CLICKHOUSE_URL:jdbc:clickhouse://localhost:8123/cameleer}
username: ${CAMELEER_SERVER_CLICKHOUSE_USERNAME:default}