3.2.7. OIDC redirect filters
You can register one or more io.quarkus.oidc.OidcRedirectFilter implementations to filter OIDC redirects to OIDC authorization and logout endpoints but also local redirects to custom error and session expired pages. Custom OidcRedirectFilter can add additional query parameters, response headers and set new cookies.
For example, the following simple custom OidcRedirectFilter adds an additional query parameter and a custom response header for all redirect requests that can be done by Quarkus OIDC:
package io.quarkus.it.keycloak;
import jakarta.enterprise.context.ApplicationScoped;
import io.quarkus.arc.Unremovable;
import io.quarkus.oidc.OidcRedirectFilter;
@ApplicationScoped
@Unremovable
public class GlobalOidcRedirectFilter implements OidcRedirectFilter {
@Override
public void filter(OidcRedirectContext context) {
if (context.redirectUri().contains("/session-expired-page")) {
context.additionalQueryParams().add("redirect-filtered", "true,");
context.routingContext().response().putHeader("Redirect-Filtered", "true");
}
}
}
See also the Customizing authentication requests section how to configure additional query parameters for OIDC authorization point.
Custom OidcRedirectFilter for local error and session expired pages can also create secure cookies to help with generating such pages.
For example, let’s assume you need to redirect the current user whose session has expired to a custom session expired page available at http://localhost:8080/session-expired-page. The following custom OidcRedirectFilter encrypts the user name in a custom session_expired cookie using an OIDC tenant client secret:
package io.quarkus.it.keycloak;
import jakarta.enterprise.context.ApplicationScoped;
import org.eclipse.microprofile.jwt.Claims;
import io.quarkus.arc.Unremovable;
import io.quarkus.oidc.AuthorizationCodeTokens;
import io.quarkus.oidc.OidcRedirectFilter;
import io.quarkus.oidc.Redirect;
import io.quarkus.oidc.Redirect.Location;
import io.quarkus.oidc.TenantFeature;
import io.quarkus.oidc.runtime.OidcUtils;
import io.smallrye.jwt.build.Jwt;
@ApplicationScoped
@Unremovable
@TenantFeature("tenant-refresh")
@Redirect(Location.SESSION_EXPIRED_PAGE)
public class SessionExpiredOidcRedirectFilter implements OidcRedirectFilter {
@Override
public void filter(OidcRedirectContext context) {
if (context.redirectUri().contains("/session-expired-page")) {
AuthorizationCodeTokens tokens = context.routingContext().get(AuthorizationCodeTokens.class.getName());
String userName = OidcUtils.decodeJwtContent(tokens.getIdToken()).getString(Claims.preferred_username.name());
String jwe = Jwt.preferredUserName(userName).jwe()
.encryptWithSecret(context.oidcTenantConfig().credentials.secret.get());
OidcUtils.createCookie(context.routingContext(), context.oidcTenantConfig(), "session_expired",
jwe + "|" + context.oidcTenantConfig().tenantId.get(), 10);
}
}
}
- 1
- Make sure this redirect filter is only called during a redirect to the session expired page.
- 2
- Access
AuthorizationCodeTokenstokens associated with the now expired session as aRoutingContextattribute. - 3
- Decode ID token claims and get a user name.
- 4
- Save the user name in a JWT token encrypted with the current OIDC tenant’s client secret.
- 5
- Create a custom
session_expiredcookie valid for 5 seconds which joins the encrypted token and a tenant id using a "|" separator. Recording a tenant id in a custom cookie can help to generate correct session expired pages in a multi-tenant OIDC setup.
Next, a public JAX-RS resource which generates session expired pages can use this cookie to create a page tailored for this user and the corresponding OIDC tenant, for example:
package io.quarkus.it.keycloak;
import jakarta.inject.Inject;
import jakarta.ws.rs.CookieParam;
import jakarta.ws.rs.GET;
import jakarta.ws.rs.Path;
import org.eclipse.microprofile.jwt.Claims;
import org.eclipse.microprofile.jwt.JsonWebToken;
import io.quarkus.oidc.OidcTenantConfig;
import io.quarkus.oidc.runtime.OidcUtils;
import io.quarkus.oidc.runtime.TenantConfigBean;
import io.smallrye.jwt.auth.principal.DefaultJWTParser;
import io.vertx.ext.web.RoutingContext;
@Path("/session-expired-page")
public class SessionExpiredResource {
@Inject
RoutingContext context;
@Inject
TenantConfigBean tenantConfig;
@GET
public String sessionExpired(@CookieParam("session_expired") String sessionExpired) throws Exception {
// Cookie format: jwt|<tenant id>
String[] pair = sessionExpired.split("\\|");
OidcTenantConfig oidcConfig = tenantConfig.getStaticTenantsConfig().get(pair[1]).getOidcTenantConfig();
JsonWebToken jwt = new DefaultJWTParser().decrypt(pair[0], oidcConfig.credentials.secret.get());
OidcUtils.removeCookie(context, oidcConfig, "session_expired");
return jwt.getClaim(Claims.preferred_username) + ", your session has expired. "
+ "Please login again at http://localhost:8081/" + oidcConfig.tenantId.get();
}
}
- 1
- Inject
TenantConfigBeanwhich can be used to access all the current OIDC tenant configurations. - 2
- Split the custom cookie value into 2 parts, first part is the encrypted token, last part is the tenant id.
- 3
- Get the OIDC tenant configuration.
- 4
- Decrypt the cookie value using the OIDC tenant’s client secret.
- 5
- Remove the custom cookie.
- 6
- Use the username in the decrypted token and the tenant id to generate the service expired page response.