= Platform HTTP security with Keycloak: A Camel Quarkus example
:cq-example-description: An example that shows how to secure platform HTTP with Keycloak

{cq-description}

TIP: Check the https://camel.apache.org/camel-quarkus/latest/first-steps.html[Camel Quarkus User guide] for prerequisites
and other general information.


== Prerequisites

The example application requires a Keycloak instance.

You do not need to provide the Keycloak instance yourself
as long as you play with the example code in dev mode (a.k.a. `mvn quarkus:dev` - read more https://quarkus.io/guides/getting-started#development-mode[here]
or as long as you only run the supplied tests (`mvn test`).
In those situations, Quarkus tooling starts a Keycloak image for you via https://quarkus.io/guides/security-openid-connect-dev-services[Quarkus Dev Services]
and it also configures the application so that you do not need touch anything in `application.properties`.

[[users-configuration]]
=== Users configuration
In all scenarios which we will cover, we will need two users `boss` (with role `admin-role` and password `boss-pass`) and `employee` (with role `regular-role` and password `employee-pass`). Employee user can be authenticated and access secured HTTP endpoints and boss user can in addition access also restricted HTTP resources.

=== Quarkus under the hood

We will use approach described in https://quarkus.io/guides/security-openid-connect[Quarkus Open ID Connect] (we want to use Keycloak as our OIDC provider) to protect service application. It will automatically protect (you can find more info in https://camel.apache.org/camel-quarkus/2.8.x/reference/extensions/platform-http.html#_securing_platform_http_endpoints[Securing platform-http endpoints]) our Camel Quarkus platform http routes, so all security configuration go through Quarkus extensions only.

== Start in Development mode
=== Run the app with Keycloak instance
Run the application in development mode with Keycloak client credentials secret of your choice (see environment variable `QUARKUS_OIDC_CREDENTIALS_SECRET`) which will be used later on.

TIP: If you want to use another running instance, in dev mode. Change `%prod` profile to `%dev` property `quarkus.oidc.auth-server-url` in `src/main/resources/application.properties`.

[source,shell]
----
$ export QUARKUS_OIDC_CREDENTIALS_SECRET=abcdefghijklmnopqrstuvwxyz # You can change it as you wish
$ mvn clean compile quarkus:dev
----

The above command compiles the project, starts the application, starts Keycloak instance via Dev Services and lets the Quarkus tooling watch for changes in your
workspace. Any modifications in your project will automatically take effect in the running application.

TIP: Please refer to the Development mode section of
https://camel.apache.org/camel-quarkus/latest/first-steps.html#_development_mode[Camel Quarkus User guide] for more details.

Now you can move on to <<playground>> section with assumption that `KEYCLOAK_URL=http://localhost:8082` and `APP_URL=http://localhost:8080`.

[[playground]]
=== Playground
First thing you need to do is to obtain Bearer token from the running Keycloak instance for each created user. Save those tokens for further authentication.

For employee user (extract value from response of key `access_token` and call it `EMPLOYEE_TOKEN`):
[source,shell]
----
$ curl -d "client_id=quarkus-client" -d "client_secret=$QUARKUS_OIDC_CREDENTIALS_SECRET" -d "username=employee" -d "password=employee-pass" -d "grant_type=password" $KEYCLOAK_URL/realms/quarkus/protocol/openid-connect/token
----
For boss user (extract value from response of key `access_token` and call it `BOSS_TOKEN`):
[source,shell]
----
$ curl -d "client_id=quarkus-client" -d "client_secret=$QUARKUS_OIDC_CREDENTIALS_SECRET" -d "username=boss" -d "password=boss-pass" -d "grant_type=password" $KEYCLOAK_URL/realms/quarkus/protocol/openid-connect/token
----

Now we can finally try to play with the application which have those endpoints configured:

- not-secured - anyone can access this endpoint
- secured/authenticated - authenticated users with Bearer token can access this endpoint
- secured/authorized - only users with role `admin-role` can access this endpoint

Try to access endpoints with various users:

- Employee accessing authenticated endpoint (you should receive `200 OK` + `You are authenticated user so you can perform this action.` message):
[source,shell]
----
$ curl -i -X GET -H "Authorization: Bearer $EMPLOYEE_TOKEN" $APP_URL/secured/authenticated
----
- Employee accessing authorized endpoint (you should receive `403 Forbidden`):
[source,shell]
----
$ curl -i -X GET -H "Authorization: Bearer $EMPLOYEE_TOKEN" $APP_URL/secured/authorized
----
- Boss accessing authenticated endpoint (you should receive `200 OK` + `You are authenticated user so you can perform this action.` message):
[source,shell]
----
$ curl -i -X GET -H "Authorization: Bearer $BOSS_TOKEN" $APP_URL/secured/authenticated
----
- Boss accessing authorized endpoint (you should receive `200 OK` + `You are authorized to perform sensitive operation.`):
[source,shell]
----
$ curl -i -X GET -H "Authorization: Bearer $BOSS_TOKEN" $APP_URL/secured/authorized
----

[[external-keycloak-instance-configuration]]
== Prerequisites for externally running Keycloak instance
For next steps we need to have externally running Keycloak instance. It can be done easily via Keycloak docker image:

=== Run the Keycloak
[source,shell]
----
$ docker run --name keycloak_test -p 8082:8080 \
        -e KEYCLOAK_ADMIN=admin -e KEYCLOAK_ADMIN_PASSWORD=admin \
        quay.io/keycloak/keycloak:latest \
        start-dev
----
=== Import preconfigured realm
Then go to `http://localhost:8082/` click on `Administrator console` and login with `admin:admin`. Then we are going to import already pre-configured realm (`realm-export.json`) stored within `config` folder placed at root of this example.
Navigate to left upper panel and click on `Add realm` select file `config/realm-export.json` and `create` it.

=== Setup users
You should create new users with credentials and roles based on <<users-configuration>>.

TIP: Don't use `temporary` passwords.

=== Get client credentials secret
Go to `Configure` left panel and select `quarkus-client` under `Clients`. Go to `Credentials` and `Regenerate Secret`. Save it as `QUARKUS_OIDC_CREDENTIALS_SECRET`.

== JVM mode

[source,shell]
----
$ export QUARKUS_OIDC_CREDENTIALS_SECRET=<insert-your-secret>
$ mvn clean package -DskipTests
$ java -jar target/quarkus-app/quarkus-run.jar
----

Now you can go to <<playground>> section (with assumption that `KEYCLOAK_URL=http://localhost:8082` and `APP_URL=http://localhost:8080`) and try it yourselves.

== Native mode

IMPORTANT: Native mode requires having GraalVM and other tools installed. Please check the Prerequisites section
of https://camel.apache.org/camel-quarkus/latest/first-steps.html#_prerequisites[Camel Quarkus User guide].

To prepare a native executable using GraalVM, run the following command:

[source,shell]
----
$ export QUARKUS_OIDC_CREDENTIALS_SECRET=<insert-your-secret>
$ mvn clean package -DskipTests -Pnative
$ ./target/*-runner
----

Now you can go to <<playground>> section (with assumption that `KEYCLOAK_URL=http://localhost:8082` and `APP_URL=http://localhost:8080`) and try it yourselves.

== Deploying to Kubernetes

You can build a container image for the application like this. Refer to the https://quarkus.io/guides/deploying-to-kubernetes[Quarkus Kubernetes guide] for options around customizing image names, registries etc.

This example uses Jib to create the container image for Kubernetes deployment.

=== Deploy Keycloak to Kubernetes
Follow https://www.keycloak.org/getting-started/getting-started-kube to install on Kubernetes cluster.

=== Configure Keycloak on Kubernetes
Use the same configuration as in <<external-keycloak-instance-configuration>> and obtain `QUARKUS_OIDC_CREDENTIALS_SECRET` and Kubernetes base URL (BASE_KEYCLOAK_KUBERNETES_URL) to your keycloak instance.

=== Deploy Camel Quarkus application to Kubernetes

TIP: Because we use `quarkus.kubernetes.env.secrets=quarkus-keycloak` in `application.properties` all properties from the secret `quarkus-keycloak` will be presented as ENV variables to the pod.

TIP: To trust self-signed certificates from Kubernetes API server use `-Dquarkus.kubernetes-client.trust-certs=true` in deploy command.

[source,shell]
----
$ kubectl create secret generic quarkus-keycloak --from-literal=QUARKUS_OIDC_CREDENTIALS_SECRET=<YOUR_SECRET>
$ mvn clean package -DskipTests -Dquarkus.kubernetes.env.vars.QUARKUS_OIDC_AUTH_SERVER_URL=$BASE_KEYCLOAK_KUBERNETES_URL/realms/quarkus -Dquarkus.oidc.tls.verification=none -Dquarkus.kubernetes.ingress.expose=true -Dquarkus.kubernetes.deploy=true -Dkubernetes
----

The `kubernetes` profile uses quarkus kubernetes and openshift-container extensions, as described in the `pom.xml`.

[source,xml]
----
<dependencies>
    <dependency>
        <groupId>io.quarkus</groupId>
        <artifactId>quarkus-kubernetes</artifactId>
    </dependency>
    <dependency>
        <groupId>io.quarkus</groupId>
        <artifactId>quarkus-container-image-jib</artifactId>
    </dependency>
</dependencies>
----

You can check the pods status:

[source,shell]
----
$ kubectl get pods
NAME                                                             READY   STATUS    RESTARTS   AGE
camel-quarkus-examples-platform-http-security-6f658784dd-kxcg8   1/1     Running   0          10m
keycloak-57d89d998-rfkk7
----

Find the app url KUBERNETES_APP_URL from Kubernetes ingress.

Then you can play with the example based on <<playground>> instructions (with assumption that `KEYCLOAK_URL=$BASE_KEYCLOAK_KUBERNETES_URL` and `APP_URL=$KUBERNETES_APP_URL`).

To clean up do:

[source,shell]
----
$ kubectl delete all -l app.kubernetes.io/name=camel-quarkus-examples-platform-http-security
$ kubectl delete secret quarkus-keycloak
----

== Deploying to OpenShift

=== Deploy Keycloak to OpenShift
Follow https://www.keycloak.org/getting-started/getting-started-openshift to install on OpenShift cluster.

=== Configure Keycloak on OpenShift
Use the same configuration as in <<external-keycloak-instance-configuration>> and obtain `QUARKUS_OIDC_CREDENTIALS_SECRET` and OpenShift route base URL to your keycloak instance as follows:
[source,shell]
----
$ export BASE_KEYCLOAK_OPENSHIFT_ROUTE_URL=$(oc get route keycloak --template='{{ .spec.host }}')
----

=== Deploy Camel Quarkus application to OpenShift

TIP: Because we use `quarkus.openshift.env.secrets=quarkus-keycloak` in `application.properties` all properties from the secret `quarkus-keycloak` will be presented as ENV variables to the pod.

TIP: To trust self-signed certificates from Kubernetes API server use `-Dquarkus.kubernetes-client.trust-certs=true` in deploy command.

[source,shell]
----
$ oc create secret generic quarkus-keycloak --from-literal=QUARKUS_OIDC_CREDENTIALS_SECRET=<YOUR_SECRET>
$ mvn clean package -DskipTests -Dquarkus.openshift.env.vars.QUARKUS_OIDC_AUTH_SERVER_URL=https://$BASE_KEYCLOAK_OPENSHIFT_ROUTE_URL/realms/quarkus -Dquarkus.oidc.tls.verification=none -Dquarkus.openshift.route.expose=true -Dquarkus.kubernetes.deploy=true -Dopenshift
----

The `openshift` profile uses quarkus openshift and openshift-container extensions, as described in the `pom.xml`.

[source,xml]
----
<dependencies>
    <dependency>
        <groupId>io.quarkus</groupId>
        <artifactId>quarkus-openshift</artifactId>
    </dependency>
    <dependency>
        <groupId>io.quarkus</groupId>
        <artifactId>quarkus-container-image-openshift</artifactId>
    </dependency>
</dependencies>
----

You can check the pods status:

[source,shell]
----
$ oc get pods
NAME                                                     READY   STATUS      RESTARTS   AGE
camel-quarkus-examples-platform-http-security-1-build    0/1     Completed   0          23h
camel-quarkus-examples-platform-http-security-1-deploy   0/1     Completed   0          23h
camel-quarkus-examples-platform-http-security-1-n6vx5    1/1     Running     0          3h56m
keycloak-1-9z2r9                                         1/1     Running     0          25h
keycloak-1-deploy                                        0/1     Completed   0          25h
----

Find the app url via

[source,shell]
----
$ export OPENSHIFT_APP_URL=$(oc get route camel-quarkus-examples-platform-http-security --template='{{ .spec.host }}')
----

Then you can play with the example based on <<playground>> instructions (with assumption that `KEYCLOAK_URL=https://$BASE_KEYCLOAK_OPENSHIFT_ROUTE_URL` and `APP_URL=$OPENSHIFT_APP_URL`).

To clean up do:

[source,shell]
----
$ oc delete all -l app.kubernetes.io/name=camel-quarkus-examples-platform-http-security
$ oc delete secret quarkus-keycloak
----

== Feedback

Please report bugs and propose improvements via https://github.com/apache/camel-quarkus/issues[GitHub issues of Camel Quarkus] project.
