Skip to content

Commit

Permalink
Merge pull request #2122 from PrarthonaPaul/develop
Browse files Browse the repository at this point in the history
Added an example to configure Elytron OIDC Client with Additional Scope Values
  • Loading branch information
fjuma committed Apr 29, 2024
2 parents c78bbf1 + da1e73f commit 6b6006b
Showing 1 changed file with 339 additions and 0 deletions.
339 changes: 339 additions & 0 deletions _posts/2024-04-29-elytron-oidc-client-scope.adoc
@@ -0,0 +1,339 @@
---
layout: post
title: 'Securing a WildFly Application Using OpenID Connect With Additional Scope Values'
date: 2024-04-29
tags: oidc authentication scopes
synopsis: How to secure a WildFly application deployed to OpenShift with OpenID Connect using additional scope values.
author: prarthonapaul
---

:toc: macro
:toc-title:

WildFly 32 includes the ability to add additional scope values when securing applications using OpenID Connect (OIDC). This new feature is available at the https://docs.wildfly.org/32/Admin_Guide.html#Feature_stability_levels[Preview stability level]. https://openid.net/developers/how-connect-works/[ OpenID Connect] is an identity layer on top of the OAuth 2.0 protocol that makes it possible for a client to verify a user’s identity based on authentication that’s performed by an OpenID provider. The https://openid.net/specs/openid-connect-core-1_0.html#AuthRequest[OpenID Connect specification] indicates that there are other scope values which may be included in the authentication request to ask permission to access additional and specific resources. This guide shows how to configure additional scope values when securing a WildFly app with OpenID Connect on OpenShift.

toc::[]

== Prerequisites

To follow along with this guide, you will need:

* Roughly 15 minutes

* Access to an OpenShift cluster (try the Red Hat Developer Sandbox for free)

* https://docs.openshift.com/container-platform/4.15/cli_reference/openshift_cli/getting-started-cli.html[OpenShift CLI]

* https://helm.sh/docs/intro/install/[Helm Chart]

== Example Application

We will use a simple web application in this guide that consists of a https://github.com/wildfly-security-incubator/elytron-examples/blob/main/elytron-oidc-client-scope/src/main/java/org/wildfly/security/examples/SecuredServlet.java[single servlet]. We will secure this servlet using OIDC.

We will use the example in the https://github.com/wildfly-security-incubator/elytron-examples/tree/main/elytron-oidc-client-scope[elytron-oidc-client-scope] directory in this repo.

To obtain this example, clone the elytron-examples repository to your local machine:

```
git clone git@github.com:wildfly-security-incubator/elytron-examples.git
```

== Log Into the OpenShift Cluster
Before we can deploy our application, we need to log into an OpenShift cluster. You can log in via the OpenShift CLI:
```
oc login -u myUserName
```

Alternatively, to log in using the API token, navigate to Developer Sandbox, click on your username at the top right corner, and click on Copy login command. It will redirect you to a page where you can copy your login command and paste it on your terminal. The command will be in this format:
```
oc login --token=myToken --server=myServerUrl
```

Once you have your environment set up with the required tools, we can move on to the next step to build and deploy our application on OpenShift.

== Start Keycloak
We will be using Keycloak as our OpenID identity provider.

To start a Keycloak server in your project on OpenShift, use the following command:

```
oc process -f https://raw.githubusercontent.com/keycloak/keycloak-quickstarts/latest/openshift/keycloak.yaml \
-p KEYCLOAK_ADMIN=admin \ <1>
-p KEYCLOAK_ADMIN_PASSWORD=admin \ <2>
-p NAMESPACE=<PROJECT_NAME> \ <3>
| oc create -f -
```
<1> Replace admin with the user name you would like to use when accessing the Keycloak Administration Console.
<2> Replace admin with the password you would like to use when accessing the Keycloak Administration Console.
<3> Replace <PROJECT_NAME> with your project name.
After running the above command, you should see the following output:

```
service/keycloak created
route.route.openshift.io/keycloak created
Warning: apps.openshift.io/v1 DeploymentConfig is deprecated in v4.14+, unavailable in v4.10000+
deploymentconfig.apps.openshift.io/keycloak created.
```
It will take a few minutes for OpenShift to provision the Keycloak pod and its related resources.

You can use the OpenShift CLI or the OpenShift web console, depending on your preference, to check if your Keycloak server has been provisioned.

=== OpenShift CLI
To make sure your Keycloak server has been provisioned using the OpenShift CLI, run:
```
oc get pods
```

After a little while, check for a message similar to the following message that indicates the pod is ready:
```
NAME READY STATUS RESTARTS AGE
keycloak-1-deploy 0/1 Completed 0 1h
keycloak-1-l9kdx 1/1 Running 0 1h
```

Once the Keycloak server has been provisioned, use the following command to find the URL for your Keycloak instance’s Admin Console:
```
KEYCLOAK_URL=https://$(oc get route keycloak --template='{{ .spec.host }}') &&
echo "" &&
echo "Keycloak Admin Console: $KEYCLOAK_URL/admin" &&
echo ""
```

=== OpenShift Web Console
To make sure your Keycloak server has been provisioned using the OpenShift web console, navigate to the *Topology* view in the *Developer* perspective. You can click on your *keycloak* app to check its status. Once it is running, you can click on *Open URL* and then access Keycloak’s *Administration Console*.

== Configure Keycloak

. Log into the Keycloak Admin Console using the admin username and password you specified earlier.
. Next, create a realm called *myrealm* and register a client called *myclient* as follows:
* *General settings*:
** *Client type* (or *Client Protocol*, depending on your keycloak version): *OpenID Connect*
** *Client ID*: *myclient*
* *Capability config*:
** *Authentication flow*: *Standard flow, Direct access grants*
* *Login Settings*: For the *Valid Redirect URIs*, enter a * for now. We will come back to edit it later.

. Navigate to the *Client scopes* tab for *myclient* and change the *Assigned type* for all scope values from *Default* to *Optional*. When `Client scopes` are set to `Default`, the claims associated with them are automatically added to the access token you receive. Changing it to `Optional` ensures that they are not included by default and you will only get access to claims that you request using the scope values you configure. This list of scope are the values that Keycloak allows and it varies for different OpenID providers.

. Now, click on *Realm roles* and create two roles, *user* and *admin*.

. Finally, create a user called *alice* whose *First name* is *Alice* and *Last name* is *Smith* and assign her the *user* and *admin* roles. Steps for assigning roles can be found in the https://www.keycloak.org/docs/latest/server_admin/#proc-assigning-role-mappings_server_administration_guide[ Keycloak documentation]. We will be using different scopes to query these pieces of information about Alice.

. Next, navigate to the *Credentials* tab and create a password for Alice. Toggle the *Temporary* switch off so you are not prompted to update the password after the first login.

== Add Helm Configuration
* Obtain the URL for Keycloak.
```
KEYCLOAK_URL=https://$(oc get route keycloak --template='{{ .spec.host }}') &&
echo "" &&
echo "Keycloak URL: $KEYCLOAK_URL" &&
echo ""
```

* Switch to the charts directory in the `elytron-oidc-client-scope` example.
```
cd /PATH/TO/ELYTRON/EXAMPLES/elytron-oidc-client-scope/charts
```
Notice there’s a helm.yaml file in this directory with the following content:
```
build:
uri: https://github.com/wildfly-security-incubator/elytron-examples.git
contextDir: elytron-oidc-client-scope
deploy:
replicas: 1
env:
- name: OIDC_PROVIDER_URL
value: <KEYCLOAK_URL> <1>
- name: SERVER_ARGS
value: "--stability=preview"
```
<1> Replace <KEYCLOAK_URL> with the Keycloak URL obtained in the previous command.

== Stability Levels for OpenShift Deployment
The WildFly server now includes different stability levels, that can be associated with functionality. Users can use the `--stability` argument when staring the WildFly server. Depending on the value of the stability levels, different features are available. You can learn more about stability levels https://docs.wildfly.org/32/Admin_Guide.html#Feature_stability_levels[here].

The `scope` attribute under the `elytron-oidc-client` subsystem is a `preview` level feature, which means in order to access its functionality, the server's stability level must be set to `preview`. When applications are deployed to OpenShift, the WildFly Cloud Galleon Feature Pack is used to provision a server. Therefore, in order to use the scope attribute, we need to provision the server at the `preview` stability level. This is why we have added the environment variable named *SERVER_ARGS* with a value of *--stability=preview*, which specifies that the provisioned server should be started at the `preview` stability level. For more information about the server's stability levels, please refer to https://docs.wildfly.org/32/Admin_Guide.html#Feature_stability_levels[WildFly Docs].

Additionally, we have used the `stability` galleon option to specify the stability level used by the feature pack when deploying the application using the tags below:
```
<galleon-options>
<stability-level>preview</stability-level>
</galleon-options>
```

== Deploy the Example Application to WildFly on OpenShift
If you haven’t already installed the WildFly Helm chart, install it:
```
helm repo add wildfly https://docs.wildfly.org/wildfly-charts/
```
If you’ve already installed the WildFly Helm Chart, be sure to update it to ensure you have the latest one:
```
helm repo update
```
We can deploy our example application to WildFly on OpenShift using the WildFly Helm Chart:
```
helm install oidc-app -f /PATH/TO/ELYTRON/EXAMPLES/elytron-oidc-client-scope/charts/helm.yaml wildfly/wildfly
```
Notice that this command specifies the file we updated, `helm.yaml`, that contains the values needed to build and deploy our application.

The application will now begin to build. This will take a couple of minutes.

The build can be observed using:
```
oc get build -w
```
Once complete, you can follow the deployment of the application using:
```
oc get deployment oidc-app -w
```
Alternatively, you can check status directly from the OpenShift web console.

== Behind the Scenes
While our application is building, let’s take a closer look at our application.

Examine the https://github.com/wildfly-security-incubator/elytron-examples/blob/main/elytron-oidc-client-scope/pom.xml[pom.xml] file.

Notice that it contains an openshift profile. A profile in Maven lets you create a set of configuration values to customize your application build for different environments. The openshift profile in this example defines a configuration that will be used by the WildFly Helm Chart when provisioning the WildFly server on OpenShift.

```
<profiles>
<profile>
<id>openshift</id>
<build>
<plugins>
<plugin>
<groupId>org.wildfly.plugins</groupId>
<artifactId>wildfly-maven-plugin</artifactId>
<version>${version.wildfly.maven.plugin}</version> <1>
<configuration>
<feature-packs>
<feature-pack>
<location>org.wildfly:wildfly-galleon-pack:${version.wildfly}</location>
</feature-pack>
<feature-pack>
<location>org.wildfly.cloud:wildfly-cloud-galleon-pack:${version.wildfly.cloud.galleon.pack}</location>
</feature-pack>
</feature-packs>
<layers>
<layer>cloud-server</layer>
<layer>elytron-oidc-client</layer> <2>
</layers>
<galleon-options>
<stability-level>preview</stability-level> <3>
</galleon-options>
<filename>simple-webapp-oidc.war</filename>
</configuration>
<executions>
<execution>
<goals>
<goal>package</goal>
</goals>
</execution>
</executions>
</plugin>
</plugins>
</build>
</profile>
</profiles>
```
<1> *wildfly-maven-plugin* provisions a WildFly server with the specified layers with our application deployed.Version *7.0.0.Beta2* or later must be used to allow for stability levels.
<2> *elytron-oidc-client* automatically adds the native OIDC client subsystem to our WildFly installation.
<3> *stability-level* for the feature pack is set to *preview* since we are making use of a preview level feature.

Examine the https://github.com/wildfly-security-incubator/elytron-examples/blob/main/elytron-oidc-client-scope/src/main/webapp/WEB-INF/oidc.json[oidc.json] file, which is used to configure the OIDC client.
```
{
"client-id" : "myclient",
"provider-url" : "${env.OIDC_PROVIDER_URL:http://localhost:8080}/realms/myrealm",
"public-client" : "true",
"scope" : "profile email roles web-origins microprofile-jwt offline_access",
"principal-attribute" : "preferred_username",
"ssl-required" : "EXTERNAL"
}
```

Note that we have specified the scope values to be *profile*, *email*, *web-origins*, *microprofile-jwt*, *roles* and *offline_access* in a space delimited list inside the *oidc.json* file. `profile`, `email` and `offline_access` are OpenID built-in scopes, while `web-origin`, `microprofile-jwt` and `roles` are Keycloak specific scope values and allow access to additional claims. You can read the descriptions under the `Client scope` tab for `myclient` to learn more about the purpose of these scope values.

Next, navigate to the OIDC application's `web.xml` file and look for the following command:
```
<login-config>
<auth-method>OIDC</auth-method> <1>
</login-config>
```

<1> Setting the `auth-method` to `OIDC` specifies that our application will use OpenID Connect to authenticate users.

== Get the Application URL
Once the WildFly server has been provisioned, use the following command to find the URL for your example application:

```
SIMPLE_WEBAPP_OIDC_URL=https://$(oc get route oidc-app --template='{{ .spec.host }}') &&
echo "" &&
echo "Application URL: $SIMPLE_WEBAPP_OIDC_URL/simple-webapp-oidc" &&
echo "Valid redirect URI: $SIMPLE_WEBAPP_OIDC_URL/simple-webapp-oidc/secured/*" &&
echo ""
```

== Finish Configuring Keycloak
From your *myclient* client in the Keycloak Administration Console, in the client settings, set *Valid Redirect URI* to the Valid redirect URI that was output in the previous section and then click *Save*.

== Accessing the Application

Now, let’s try accessing our application using the application URL.

Click on *Access Secured Servlet*.

Now, you’ll be redirected to Keycloak to log in. If you click on the url on the search bar, you will see the scope values specified in the `redirect-uri` field with the different scope values separated by a `+`. You will also notice that a new scope value, `openid`, has been added. This indicates that we are going to be using OpenID Connect to authenticate the user.

Log in with *alice* and the password that you set when configuring Keycloak.

Next, you’ll be redirected back to our application and you should see the *Secured Servlet* page. That means that we were able to successfully log in to our application using the Keycloak OpenID provider!

This page will display the current principal, and a list of claim values obtained using the scope values you configured. This is what it will look like:

```
Current Principal 'alice'

Claims received using additional scope values:

By configuring the "profile" scope, the "given_name" and "family_name" claims are present in the access token and have values : Alice and Smith

By configuring the "email" scope, the "email_verified" claim is present in the access token and has value : false

By configuring the "roles" scope, the "realm_access" claim is present in the access token and has value : {roles=[default-roles-myrealm, offline_access, admin, uma_authorization, user]}

By configuring the "microprofile-jwt" scope, the "groups" claim is present in the access token and has value : [default-roles-myrealm, offline_access, admin, uma_authorization, user]

By configuring the "web-origins" scope, the "allowed-origins" claim is present in the access token and has value : [http://localhost:8090]
```
Note that the value for Current Principal may be different, and can be replaced by the unique client id assigned by keycloak.

Notice that there are no claims obtained using the `offline_access` scope. To learn more about what this scope value does, please refer to the https://openid.net/specs/openid-connect-core-1_0.html#OfflineAccess[ OpenID Documentation ].

=== Invalid Scope Values

Different OpenID providers have their own set of valid scope values and they vary depending on the OpenID provider. Try changing the scope values to `INVALID_SCOPE` inside the oidc.json file.

Deploy and access the webapp again using the command below:
```
helm upgrade oidc-app -f charts/helm.yaml wildfly/wildfly
```

Since `INVALID_SCOPE` is not one of the acceptable scope values, you will now see a `Bad request` page instead of being redirected to the Keycloak login page. You will notice that the url now contains
`error=invalid_scope&error_description=Invalid+scopes`
This indicates that your authentication request was rejected because it contains invalid scope values.

== Summary

This example has demonstrated how to secure a WildFly application deployed to OpenShift using additional scope values. For more details on the `elytron-oidc-client` subsystem, please check out the https://docs.wildfly.org/32/Admin_Guide.html#Elytron_OIDC_Client[ documentation ] and for more details on OpenID Connect, checkout the https://openid.net/specs/openid-connect-core-1_0.html#ScopeClaims[ OpenID documentation ] and the documentation of your OpenID provider.

== Resources

* https://docs.wildfly.org/32/Getting_Started_on_OpenShift.html[Getting Started with WildFly on OpenShift]
* https://docs.openshift.com/container-platform/4.15/cli_reference/openshift_cli/getting-started-cli.html[OpenShift CLI]
* https://docs.wildfly.org/32/Getting_Started_on_OpenShift.html#helm-charts[WildFly Helm Chart]
* https://www.keycloak.org/getting-started/getting-started-openshift[Getting started with Keycloak on OpenShift]
* https://www.keycloak.org/docs/latest/server_admin/index.html[Keycloak Server Administration Guide]
* https://www.keycloak.org/docs/latest/securing_apps/#_oidc[Using OpenID Connect to secure applications and services]
* https://docs.wildfly.org/32/Admin_Guide.html#Feature_stability_levels[Feature stability levels]
* https://docs.wildfly.org/32/Galleon_Guide.html#WildFly_Galleon_feature-packs[WildFly Galleon feature-packs]

0 comments on commit 6b6006b

Please sign in to comment.