Skip to content
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

Do not require UserInfo when its injection point is detected for OIDC tenants without the UserInfo endpoint #40456

Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -218,14 +218,16 @@ private static boolean isTenantIdentityProviderType(InjectionPointInfo ip) {
@Record(ExecutionTime.RUNTIME_INIT)
@BuildStep
public SyntheticBeanBuildItem setup(
BeanRegistrationPhaseBuildItem beanRegistration,
OidcConfig config,
OidcRecorder recorder,
CoreVertxBuildItem vertxBuildItem,
TlsConfig tlsConfig,
// this is required for setup ordering: we need CP set up
ContextPropagationInitializedBuildItem cpInitializedBuildItem) {
return SyntheticBeanBuildItem.configure(TenantConfigBean.class).unremovable().types(TenantConfigBean.class)
.supplier(recorder.setup(config, vertxBuildItem.getVertx(), tlsConfig))
.supplier(
recorder.setup(config, vertxBuildItem.getVertx(), tlsConfig, detectUserInfoRequired(beanRegistration)))
.destroyer(TenantConfigBean.Destroyer.class)
.scope(Singleton.class) // this should have been @ApplicationScoped but fails for some reason
.setRuntimeInit()
Expand All @@ -252,15 +254,8 @@ public void registerTenantResolverInterceptor(Capabilities capabilities, OidcRec
}
}

@BuildStep
void detectUserInfoRequired(BeanRegistrationPhaseBuildItem beanRegistrationPhaseBuildItem,
BuildProducer<RunTimeConfigurationDefaultBuildItem> runtimeConfigDefaultProducer) {
if (isInjected(beanRegistrationPhaseBuildItem, USER_INFO_NAME, null)) {
runtimeConfigDefaultProducer.produce(
new RunTimeConfigurationDefaultBuildItem("quarkus.oidc.authentication.user-info-required", "true"));
runtimeConfigDefaultProducer.produce(
new RunTimeConfigurationDefaultBuildItem("quarkus.oidc.*.authentication.user-info-required", "true"));
}
private static boolean detectUserInfoRequired(BeanRegistrationPhaseBuildItem beanRegistrationPhaseBuildItem) {
return isInjected(beanRegistrationPhaseBuildItem, USER_INFO_NAME, null);
}

@BuildStep
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -37,6 +37,10 @@ public class UserInfoRequiredDetectionTest {
quarkus.oidc.named.auth-server-url=${quarkus.oidc.auth-server-url}
quarkus.oidc.named.tenant-paths=/user-info/named-tenant
quarkus.oidc.named.user-info-path=http://${quarkus.http.host}:${quarkus.http.port}/user-info-endpoint
quarkus.oidc.named-2.auth-server-url=${quarkus.oidc.auth-server-url}
quarkus.oidc.named-2.tenant-paths=/user-info/named-tenant-2
quarkus.oidc.named-2.discovery-enabled=false
quarkus.oidc.named-2.jwks-path=protocol/openid-connect/certs
quarkus.http.auth.proactive=false
"""),
"application.properties"));
Expand All @@ -53,6 +57,12 @@ public void testNamedTenant() {
.body(Matchers.is("alice"));
}

@Test
public void testUserInfoNotRequiredWhenMissingUserInfoEndpoint() {
RestAssured.given().auth().oauth2(getAccessToken()).get("/user-info/named-tenant-2").then().statusCode(200)
.body(Matchers.is("false"));
}

private static String getAccessToken() {
return new KeycloakTestClient().getAccessToken("alice", "alice", "quarkus-service-app", "secret", List.of("openid"));
}
Expand Down Expand Up @@ -94,6 +104,13 @@ public String getNamedTenantName() {
}
return userInfo.getPreferredUserName();
}

@PermissionsAllowed("openid")
@Path("named-tenant-2")
@GET
public boolean getNamed2TenantUserInfoRequired() {
return config.namedTenants.get("named-2").authentication.userInfoRequired.orElse(false);
}
}

}
Original file line number Diff line number Diff line change
Expand Up @@ -68,6 +68,7 @@ public class OidcRecorder {

private static final Map<String, TenantConfigContext> dynamicTenantsConfig = new ConcurrentHashMap<>();
private static final Set<String> tenantsExpectingServerAvailableEvents = ConcurrentHashMap.newKeySet();
private static volatile boolean userInfoInjectionPointDetected = false;

public Supplier<DefaultTokenIntrospectionUserInfoCache> setupTokenCache(OidcConfig config, Supplier<Vertx> vertx) {
return new Supplier<DefaultTokenIntrospectionUserInfoCache>() {
Expand All @@ -78,7 +79,9 @@ public DefaultTokenIntrospectionUserInfoCache get() {
};
}

public Supplier<TenantConfigBean> setup(OidcConfig config, Supplier<Vertx> vertx, TlsConfig tlsConfig) {
public Supplier<TenantConfigBean> setup(OidcConfig config, Supplier<Vertx> vertx, TlsConfig tlsConfig,
boolean userInfoInjectionPointDetected) {
OidcRecorder.userInfoInjectionPointDetected = userInfoInjectionPointDetected;
final Vertx vertxValue = vertx.get();

String defaultTenantId = config.defaultTenant.getTenantId().orElse(DEFAULT_TENANT_ID);
Expand Down Expand Up @@ -541,6 +544,9 @@ public Uni<OidcProviderClient> apply(OidcConfigurationMetadata metadata, Throwab
"The application supports RP-Initiated Logout but the OpenID Provider does not advertise the end_session_endpoint"));
}
}
if (userInfoInjectionPointDetected && metadata.getUserInfoUri() != null) {
enableUserInfo(oidcConfig);
}
if (oidcConfig.authentication.userInfoRequired.orElse(false) && metadata.getUserInfoUri() == null) {
client.close();
return Uni.createFrom().failure(new ConfigurationException(
Expand Down