Keycloak OAUTH2 example with a REST Application

This article will guide you through understanding OAuth2 and OpenID usage with Keycloak using a JAX-RS filter named ContainerRequestFilter which is available in JAX-RS servers such as WildFly.

OpenID is a process which deals with authentication (i.e. proving who you are). On the other habd, OAuth is about authorisation (i.e. to grant access to resources without having to deal with the original authentication).

OAuth is typically used in external partner sites to allow access to protected data without them having to re-authenticate a user.

In order to run this example, you will need a Keycloak server up and running. To learn more about it, check this tutorial: Introduction to Keycloak

Using Keycloak Admin CLI to create a Realm

Our example requires a Keycloak Realm to be set up and a Client definition which is allows up to be authorized using a Token issued by Keycloak. You can create the Realm and Client by executing the following CLI, which uses Keycloak Admin CLI (located in the bin folder):

#Authenticate with the Admin Server    
./kcadm.sh config credentials --server http://localhost:8180/auth --realm master --user admin --password admin
#Create Realm Demo-Realm
./kcadm.sh create realms -s realm=demo-realm -s enabled=true -o
#Create User customer-admin
./kcadm.sh create users -r demo-realm -s username=customer-admin -s enabled=true -s email="joe@gmail.com"
#Set customer-admin password
./kcadm.sh set-password -r demo-realm --username customer-admin --new-password admin
#Create Client
./kcadm.sh create clients -r demo-realm -s clientId=customer-manager-client -s bearerOnly="false"   -s "redirectUris=[\"http://localhost:8080/*\"]" -s enabled=true -s directAccessGrantsEnabled=true -s clientAuthenticatorType=client-secret -s secret=mysecret
#Create Role customer-manager
./kcadm.sh create roles -r demo-realm -s name=customer-manager
#Assign Role to customer-admin 
./kcadm.sh add-roles --uusername customer-admin --rolename customer-manager -r demo-realm

You can check from Keycloak’s Admin Console that the Realm and Client have been correctly defined:

As you can see, we have defined as Redirect URI a generic localhost:8080 which will return, after checking the token, to the root page of a Web Server. In real world examples, you will want to redirect to a precise Web context.

Building the OAUTH Example

This project will run as WildFly Bootable Jar. To learn more about WildFly bootable Jar we recommend checking this tutorial: Turn your WildFly applications in bootable JARs
In terms of application set-up, the application will be a JAX-RS endpoint with a Secured endpoint.
Here is our JAX-RS Secured Endpoint:

The “hello” GET Endpoint returns a JSON which contains a set of information collected from the KeycloakPrincipal. It is going to include the Principal name (the User name that logged in) and the User’s email.

@Path("/")
public class MyService {
    @Context
    SecurityContext context;

    @GET
    @Path("hello")
    @RolesAllowed({"customer-manager"})
    @Produces(MediaType.APPLICATION_JSON)
    public HashMap hello() {
        KeycloakPrincipal principal = (KeycloakPrincipal) context.getUserPrincipal();
        
        HashMap map = new HashMap();
        map.put("principal",principal.getName());
        map.put("email", principal.getKeycloakSecurityContext().getToken().getEmail());
        
        Map<String, Object> customClaims = (principal.getKeycloakSecurityContext().getToken().getOtherClaims());
        map.putAll(customClaims);
        return map;				
     
    }

}

The Endpoint also collects the list of all available Claims, such as Custom Attributes, from the KeycloakSecurityContext. In the section “Adding Custom Attributes to our Realm” we will see how to add a Custom Attribute to our User.

The JAX-RS Endpoint requires an Activator to be available:

@ApplicationPath("/rest")
public class JaxRsActivator extends Application {
   /* class body intentionally left blank */
}

In order to use Keycloak Authentication, we have mainly three options. Check the Installation Tab of your Keycloak Client and select the Keycloak OIDS JBoss Subsystem CLI:

/subsystem=keycloak/secure-deployment="simple-webapp.war"/:add( \
    realm="demo-realm", \
    resource="customer-manager-client", \
    auth-server-url=http://localhost:8180/auth/, \
    ssl-required=EXTERNAL)

/subsystem=keycloak/secure-deployment="simple-webapp.war"/credential=secret:add(value=mysecret)

Include this CLI script in the root directory of your project.

Without our pom.xml, we will configure the project dependencies and, in the Bootable JAR plugin, the WildFly layers to be included in our Runtime and the Path to the CLI Script to be executed:

<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/maven-v4_0_0.xsd">
   <modelVersion>4.0.0</modelVersion>
   <groupId>com.mastertheboss.jaxrs</groupId>
   <artifactId>keycloak-demo</artifactId>
   <packaging>war</packaging>
   <version>1.0.0</version>
   <name>WildFly Bootable JAR - Keycloak</name>
   <description>An application secured with Keycloak example</description>
   <properties>
      <version.keycloak>13.0.0</version.keycloak>
      <version.wildfly>24.0.0.Final</version.wildfly>
      <surefire.version>3.0.0-M5</surefire.version>
      <maven.compiler.source>11</maven.compiler.source>
      <maven.compiler.target>11</maven.compiler.target>
      <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
   </properties>
   <dependencyManagement>
      <dependencies>
         <dependency>
            <groupId>jakarta.platform</groupId>
            <artifactId>jakarta.jakartaee-api</artifactId>
            <version>8.0.0</version>
            <scope>provided</scope>
         </dependency>
      </dependencies>
   </dependencyManagement>
   <dependencies>
      <dependency>
         <groupId>jakarta.platform</groupId>
         <artifactId>jakarta.jakartaee-api</artifactId>
         <scope>provided</scope>
      </dependency>
      <dependency>
         <groupId>org.keycloak</groupId>
         <artifactId>keycloak-core</artifactId>
         <version>15.0.1</version>
      </dependency>
   </dependencies>
   <build>
      <finalName>simple-webapp</finalName>
      <plugins>
         <plugin>
            <groupId>org.wildfly.plugins</groupId>
            <artifactId>wildfly-jar-maven-plugin</artifactId>
            <version>5.0.2.Final</version>
            <configuration>
               <feature-packs>
                  <feature-pack>
                     <location>wildfly@maven(org.jboss.universe:community-universe)#${version.wildfly}</location>
                  </feature-pack>
                  <feature-pack>
                     <groupId>org.keycloak</groupId>
                     <artifactId>keycloak-adapter-galleon-pack</artifactId>
                     <version>${version.keycloak}</version>
                  </feature-pack>
               </feature-packs>
               <layers>
                  <layer>jaxrs</layer>
                  <layer>management</layer>
                  <layer>keycloak-client-oidc</layer>
               </layers>
               <context-root>false</context-root>
               <cli-sessions>
                  <cli-session>
                     <script-files>
                        <script>keycloak.cli</script>
                     </script-files>
                  </cli-session>
               </cli-sessions>
            </configuration>
            <executions>
               <execution>
                  <goals>
                     <goal>package</goal>
                  </goals>
               </execution>
            </executions>
         </plugin>
      </plugins>
   </build>
</project>

You can start the application as follows:

mvn clean install wildfly-jar:run

Testing from the Browser

If you are testing the Endpoint from the browser, just head to:

http://localhost:8080/simple-webapp/rest/hello
You will be redirected to Keycloak Login Page:

Enter “customer-admin” / “admin” as User and Password. You will get as response the JSON Payload:
{
  "principal": "65007a42-be20-4172-a25a-a1f3f3f19e85",
  "email": "joe@gmail.com",

}

Testing from the Command Line

On the other hand,you can access the Endpoint from the Command Line as well. For that, we will at first collect the Keycloak Token sending the Username credentials and the Secret:

export access_token=$(\
    curl -X POST http://localhost:8180/auth/realms/demo-realm/protocol/openid-connect/token \
    --user customer-manager-client:mysecret \
    -H 'content-type: application/x-www-form-urlencoded' \
    -d 'username=customer-admin&password=admin&grant_type=password' | jq --raw-output '.access_token' \
 )

Then, you can access the Endpoint with cURL as follows:

curl -H "Authorization: Bearer $access_token" http://localhost:8080/simple-webapp/rest/hello

Adding Custom attributes to our Realm

Let’s add a Custom Attribute to our user. Custom Attributes are used to extend the basic Metadata information available to Users.

We will add a Custom Attribute from Keycloak Admin application. Select the Users Panel and check the Attributes Tab. There Click to Add an Attribute and Save it.

We have now added the customer.id Custom Attribute. To make this attribute available to our Client application, we need to create a Protocol Mapper. From your Clients Panel, select the customer-manager-client and the Mappers Tab and click on Create:

Above you can see the Mapper that has been added for the User attribute “customer.id“. Save the Mapper.

Accessing User Custom Attributes

Now that you have added a Custom Attribute to your Keycloak user, let’s try again to access the endpoint.

curl -s -H "Authorization: Bearer $access_token" http://localhost:8080/simple-webapp/rest/hello | jq
{
  "principal": "65007a42-be20-4172-a25a-a1f3f3f19e85",
  "email": "joe@gmail.com",
  "sid": "45c81ff7-3064-41d0-b376-a281492dc5a6",
  "customer": {
    "id": "1234"
  }
}

As you can see, the JSON Payoad now includes the customer.id attribute.

Conclusion

In this turorial we have covered how to create a JAX-RS Application protected with OAUTH2 and WildFly. If you want to see an equivalent REST Application running with Spring Boot and secured with Keycloak check this tutorial: Managing Keycloak user metadata and custom attributes

Source code for this tutorial: https://github.com/fmarchioni/mastertheboss/tree/master/keycloak/oauth2