This is the second post in the Getting Started with Keycloak series. In this post we'll be securing a simple REST service with Keycloak. The example REST service is created using JAX-RS and deployed to WildFly.
Start a Keycloak Server
Follow the steps from the previous post in the series Installing the Keycloak Server as you will need to have a Keycloak server up and running. If you are planning to run the Keycloak server on the same machine make sure you start it on a different port:
Downloading the REST service example
You need to have Java 8 (or 7) and Maven 3 installed prior to building and deploying the REST service.
The first thing we need to do is to download the REST service. The service is hosted on GitHub so you should either fork and clone the project from GitHub or download an archive of the project.
To clone the project on GitHub simply run:
git clone https://github.com/stianst/keycloak-blog-gs.git
If you are not familiar with Git then simply download and extract the project from here.
A look at the Services
The services consists of only 3 Java classes inside
The classes and what they do are:
- Application - this is used to bootstrap the application. It contains an @ApplicationPath annotation which in this case instructs JAX-RS to deploy the services to '/' within the applications context-path
- Resource - this is the actual service endpoints we're creating. It consists of 3 endpoints, public, secured and admin. They are very simply and only support GET requests. Each endpoint will simply return a message with the name of the endpoint invoked
- Message - this represents the JSON structure returned by our endpoints. Again very simple and all the endpoints return are a message saying what endpoint was invoked
Deploying the services
To deploy the services first download and install WildFly 9.0.2.Final. You can download it from here. Once downloaded simply extract it to a directory and start it by running:
wildfly-9.0.2.Final/bin/standalone.shor if you are using Windows by running:
Once WildFly is up and running you can deploy the services by opening the directory you clone (or extracted) the project to. Then deploy them to WildFly by running:
cd services-jaxrs mvn clean install wildfly:deploy
Once the services has been deployed you can open the following endpoints in your browser:
Securing the services
Now let's secure the REST services. To do this open the
web.xml file within the service sources
services-jaxrs/src/main/webapp/WEB-INF/web.xml). Then add the following snippet:
<security-constraint> <web-resource-collection> <url-pattern>/secured</url-pattern> </web-resource-collection> <auth-constraint> <role-name>user</role-name> </auth-constraint> </security-constraint> <security-constraint> <web-resource-collection> <url-pattern>/admin</url-pattern> </web-resource-collection> <auth-constraint> <role-name>admin</role-name> </auth-constraint> </security-constraint> <login-config> <auth-method>BASIC</auth-method> </login-config> <security-role> <role-name>admin</role-name> <role-name>user</role-name> </security-role>This configures the container (in this case WildFly, well actually Undertow is the web server on WildFly) to require the users to have certain roles to be allowed to invoke the endpoints. In this case the endpoint
securedrequires the role user and the endpoint
adminrequires the role admin.
Now re-deploy the services by running:
cd services-jaxrs mvn install wildfly:deploy
Now you can again try to invoke the endpoints:
Creating a Client in Keycloak
Now we need to create a client for the services within Keycloak. To do this open Keycloak admin console in your browser. Sign in and click on Clients in the menu on the left hand side. Once that's open click on Create on top of the table. On the next screen fill in the following values:
- Client ID: service
- Access Type: bearer-only
Now click on the Installation tab on the top of the form. Under format option
select Keycloak JSON. Click on the Download tab. You should move the downloaded file (
services/src/main/webapp/WEB-INF. This provides the required configuration for the Keycloak
There's also an alternative method when deploying to WildFly (or JBoss EAP) which is to specify the configuration found in keycloak.json inside standalone.xml instead. We're not going to cover this approach in this post, but it can be more convinient to use this option if you don't want to open up your WAR (or change the source like we did now).
You should also open
services/src/main/webapps/WEB-INF/web.xml in your favorite editor and change:
Don't try to redeploy the WAR just yet as we have to first install the Keycloak client adapter into WildFly.
Create User and Roles
Now we need to create a user that has the correct roles to access the endpoints. Again open the Keycloak admin console
in your browser. First click on Roles there's already an admin role so we don't need to add that, but we
need to create the user role so click on Add Role. Use
user for the Role name and click
We've now have the two roles we need for our service, next step is to create a user. Click on Users in the
menu. Then click on Add User. Set the username to
user and click on Save. Then
click on Credentials. Enter a
password in the password and password confirmation fields.
Click on the toggle next to Temporary so it displays
OFF. This prevents the user from having to
reset the password on the next login. Now click on Role Mappings. To demonstrate
that this user is only allowed to invoke the secured endpoint, not the admin endpoint, we're going to only assign
the user role to this user. Under Realm Roles in Available Roles select
user and click
Add selected. Now we're done configuring Keycloak.
Install Client Adapter into WildFly
Next thing we're going to do is to install the Keycloak client adapter into WildFly. Download the WildFly client adapter keycloak-wf9-adapter.tar.gz (or keycloak-wf9-adapter.zip). Extract the archive into the root of your WildFly installation and run:
bin/jboss-cli.sh -c ':shutdown(restart=true)'Wait until WildFly has restarted then run:
bin/jboss-cli.sh -c --file=bin/adapter-install.cliFinally run:
bin/jboss-cli.sh -c ':shutdown(restart=true)'
Now we've installed the WildFly client adapter we can re-deploy the service with the Keycloak config. To do this run:
cd services-jaxrs mvn install wildfly:deploy
Once the services has been re-deployed now try the endpoints again:
Obtain Token and Invoke Service
First we need to create a client that can be used to obtain the token. Go to the Keycloak admin console again
and create a new client. This time give it the Client ID
curl and select
access type. Under Valid Redirect URIs enter
As we are going to manually obtain a token and invoke the service let's increase the lifespan of tokens slightly.
In production access tokens should have a relatively low timeout, ideally less than 5 minutes. To increase the
timeout go to the Keycloak admin console again. This time click on Realm Settings then on Tokens.
Change the value of Access Token Lifespan to
15 minutes. That should give us plenty of time to
obtain a token and invoke the service before it expires.
Now we're ready to get our first token using CURL. To do this run:
RESULT=`curl --data "grant_type=password&client_id=curl&username=user&password=password" http://localhost:8180/auth/realms/master/protocol/openid-connect/token`This is a bit cryptic and luckily this is not how you should really be obtaining tokens. Tokens should be obtained by web applications by redirecting to the Keycloak login page. We're only doing this so we can test the service as we don't have an application that can invoke the service yet. Basically what we are doing here is invoking Keycloaks OpenID Connect token endpoint with grant type set to password which is the Resource Owner Credentials flow that allows swapping a username and a password for a token.
Take a look at the result by running:
echo $RESULTThe result is a JSON document that contains a number of properties. There's only one we need for now though so we need to parse this output to retrieve only the value we want. To do this run:
TOKEN=`echo $RESULT | sed 's/.*access_token":"//g' | sed 's/".*//g'`This command uses sed to strip out everything before and after the value of the access token property. I'm afraid these instructions will only work on Linux or iOS so Windows users will have to figure out how to do this themselves or install Cygwin. If anyone knows how to do this on Windows I'd appreciate if you'd add the instructions as a comment to this post.
Now that we have the token we can invoke the secured service. To do this run:
curl http://localhost:8080/service/secured -H "Authorization: bearer $TOKEN"