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

Update OIDC bearer doc with a section about response filters #45143

Merged
merged 1 commit into from
Dec 16, 2024
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 @@ -1346,6 +1346,49 @@ Authentication that requires a dynamic tenant will fail.
You can filter OIDC requests made by Quarkus to the OIDC provider by registering one or more `OidcRequestFilter` implementations, which can update or add new request headers, and log requests.
For more information, see xref:security-oidc-code-flow-authentication#code-flow-oidc-request-filters[OIDC request filters].

[[bearer-token-oidc-response-filters]]
=== OIDC response filters

You can filter responses from the OIDC providers by registering one or more `OidcResponseFilter` implementations, which can check the response status, headers and body in order to log them or perform other actions.

You can have a single filter intercepting all the OIDC responses, or use an `@OidcEndpoint` annotation to apply this filter to the specific endpoint responses only. For example:

[source,java]
----
package io.quarkus.it.keycloak;

import jakarta.enterprise.context.ApplicationScoped;

import io.quarkus.arc.Unremovable;
import io.quarkus.logging.Log;
import io.quarkus.oidc.common.OidcEndpoint;
import io.quarkus.oidc.common.OidcEndpoint.Type;
import io.quarkus.oidc.common.OidcResponseFilter;
import io.quarkus.oidc.common.runtime.OidcConstants;
import io.quarkus.oidc.runtime.OidcUtils;

@ApplicationScoped
@Unremovable
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Is @Unremovable always required? Wonder if beans with @OidcEndpoint can be considered @Unremovable by default

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@gastaldi Thanks, interesting idea, we can probably make an enhancement request to introspect it at build time. Though @OidcEndpont is optional, without it a filter applies to all endpoint responses (in case of response filters)

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Right, or maybe consider OidcResponseFilter implementations unremovable?

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yeah, for all 3 types of filters (request, response, redirect)

@OidcEndpoint(value = Type.DISCOVERY) <1>
public class DiscoveryEndpointResponseFilter implements OidcResponseFilter {

@Override
public void filter(OidcResponseContext rc) {
String contentType = rc.responseHeaders().get("Content-Type"); <2>
if (contentType.equals("application/json") {
String tenantId = rc.requestProperties().get(OidcUtils.TENANT_ID_ATTRIBUTE); <3>
String metadata = rc.responseBody().toString(); <4>
Log.debugf("Tenant %s OIDC metadata: %s", tenantId, metadata);
}
}
}

----
<1> Restrict this filter to requests targeting the OIDC discovery endpoint only.
<2> Check the response `Content-Type` header.
<3> Use `OidcRequestContextProperties` request properties to get the tenant id.
<4> Get the response data as String.

== References

* xref:security-oidc-configuration-properties-reference.adoc[OIDC configuration properties]
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -392,9 +392,8 @@ package io.quarkus.it.keycloak;

import jakarta.enterprise.context.ApplicationScoped;

import org.jboss.logging.Logger;

import io.quarkus.arc.Unremovable;
import io.quarkus.logging.Log;
import io.quarkus.oidc.common.OidcEndpoint;
import io.quarkus.oidc.common.OidcEndpoint.Type;
import io.quarkus.oidc.common.OidcResponseFilter;
Expand All @@ -405,16 +404,15 @@ import io.quarkus.oidc.runtime.OidcUtils;
@Unremovable
@OidcEndpoint(value = Type.TOKEN) <1>
public class TokenEndpointResponseFilter implements OidcResponseFilter {
private static final Logger LOG = Logger.getLogger(TokenResponseFilter.class);


@Override
public void filter(OidcResponseContext rc) {
String contentType = rc.responseHeaders().get("Content-Type"); <2>
if (contentType.equals("application/json")
&& OidcConstants.AUTHORIZATION_CODE.equals(rc.requestProperties().get(OidcConstants.GRANT_TYPE)) <3>
&& "code-flow-user-info-cached-in-idtoken".equals(rc.requestProperties().get(OidcUtils.TENANT_ID_ATTRIBUTE)) <3>
&& rc.responseBody().toJsonObject().containsKey("id_token")) { <4>
LOG.debug("Authorization code completed for tenant 'code-flow-user-info-cached-in-idtoken'");
Log.debug("Authorization code completed for tenant 'code-flow-user-info-cached-in-idtoken'");
}
}
}
Expand Down
Loading