In this blog post I explain the setup of a application running on Wildfly 29 using the OIDC authentication mechanism. It took me a long time to figure out the correct and necessary configuration steps. My requirement was not only to authenticate a user with Keycloak via OpenID Connect (OIDC), but also enable my backend services to authenticate programmatically to access the Rest API.

So we have two requirements: User login via Keycloak/OIDC and a programmatically login for backend service. The later is called Bearer Authentication mechanism.
Bearer Token Authentication
The Bearer Token Authorization is the process of authorizing HTTP requests through a valid Bearer Token. Such a token can be obtained from a Identity Authority like Keycloak using a simple curl command. For example to get a valid token from a Keycloak server you can run:
curl -X POST \
-d "grant_type=password" \
-d "client_id=imixs" \
-d "client_secret=xxxxxxxxxxxxxxxxxxx" \
-d "username=anna" \
-d "password=123" \
"https://my-keycloak.server/realms/my-keycloak-realm/protocol/openid-connect/token"
This will result in a JSON Web Token (JWT) containing differnet sections.
{"access_token":"eyxxxxx.eyxxxxxxxxx",
"expires_in":300,
"refresh_expires_in":1800,
"refresh_token":"eyyyyyyyyyy.eyyyyyyy",
"token_type":"Bearer",
"not-before-policy":0,
"session_state":"fc2f7e36-e4ba-145a-b493-efb287ec0c7a",
"scope":"profile email"
}
The interesting one is the ‘access_token’. You can copy this part and now you can request a secured resource from your applications Rest API:
curl -X GET \
-H "Authorization: Bearer eyyyyyyyyyyyyyyyyyy" \
"https://my-app/api/documents/ABC"
OK, this all sounds very easy and straight forward. But due to the fact that this security mechanisms evolving fast also in wildfly there were differnet concepts used in the past. So the following will work for Wildfly 29 (and hopefully later) version.
The Wildfly Descriptor ‘oidc.json’
An easy and very fast setup is to use the Wildfly specific deployment descriptor file ‘oidc.json
‘. This file is placed in /WEB-INF/
directory:
{
"client-id" : "my-client-id",
"provider-url" : "https://my-keycloak.server/realms/my-keycloak-realm",
"principal-attribute" : "preferred_username",
"credentials" : {
"secret" : "xxxxxxxxxxxxxxx"
}
}
In addition change the login-config in your web.xml
file to ‘OIDC’
...
<login-config>
<auth-method>OIDC</auth-method>
</login-config>
...
No further configuration is needed. No realms need to be configured at all in the standalone.xml
or in your application.
The Jakarta OpenIdAuthenticationMechanismDefinition
Jakarta EE 10 includes a new authentication mechanism: OpenID Connect! This can be added to a Jakarta EE servlet using the new @OpenIdAuthenticationMechanismDefinition
annotation.
This annotation is the standarized way to use OIDC authentication mechanism. You need to implement a CDI security bean in your application like shown in the following example:
import jakarta.enterprise.context.RequestScoped;
import jakarta.enterprise.event.Observes;
import jakarta.inject.Inject;
import jakarta.security.enterprise.authentication.mechanism.http.OpenIdAuthenticationMechanismDefinition;
import jakarta.security.enterprise.identitystore.openid.AccessToken;
import jakarta.security.enterprise.identitystore.openid.OpenIdContext;
import jakarta.ws.rs.GET;
import jakarta.ws.rs.Path;
import jakarta.ws.rs.Produces;
import jakarta.ws.rs.core.MediaType;
@RequestScoped
@Path("/oidc")
@Produces({ MediaType.TEXT_PLAIN })
@OpenIdAuthenticationMechanismDefinition( //
clientId = "${oidcConfig.clientId}", //
clientSecret = "${oidcConfig.clientSecret}", //
redirectURI = "${baseURL}/callback", //
providerURI = "${oidcConfig.issuerUri}" //
)
public class Securitybean implements Serializable {
private static final long serialVersionUID = 1L;
@Inject
Principal principal;
@Inject
private OpenIdContext context;
@GET
@Produces("text/plain")
public String sessionInfoAuth() {
String message = "";
try {
System.out.println("=========================================");
if (principal != null) {
System.out.println(" Principal name: " + principal.getName());
} else {
System.out.println(" Principal resolved to null!");
}
// Here's the unique subject identifier within the issuer
if (context == null) {
message = "Failed to resolve OpenIdContext!";
} else {
System.out.println(" Subject = " + context.getSubject());
System.out.println(" Access token = " + context.getAccessToken());
System.out.println(" ID token = " + context.getIdentityToken());
System.out.println(" Claims json = " + context.getClaimsJson());
System.out.println("=========================================");
message = "Imixs-Security-OIDC ==> OK \n" + //
"User Principal ==> " + principal.getName()
+ "\n\nSession details are available on server log";
}
} catch (Exception e) {
message = "Failed to resolve OpenIdContext!";
}
return message;
}
}
The important part is only the annotation. I added the method sessionInfoAuth only for convenience to provide a rest API to check the auth information.
Using this mechanism
it is important to disable the integrated-jaspi module in your standalone.xml
file:
...
<subsystem xmlns="urn:jboss:domain:undertow:14.0" default-virtual-host="default-host"
default-servlet-container="default" default-server="default-server"
statistics-enabled="${wildfly.undertow.statistics-enabled:${wildfly.statistics-enabled:false}}"
default-security-domain="other">
......
<application-security-domains>
<application-security-domain name="other" security-domain="ApplicationDomain"
integrated-jaspi="false" />
</application-security-domains>
.......
</subsystem>
.....
The problem is, that with this setup you can login as a user like before with the oidc.yaml file, but a programmatic login with the access token is no longer possible.
If you find an solution for this problem, please let me know đ