All Downloads are FREE. Search and download functionalities are using the official Maven repository.

se.guides.security-oidc.adoc Maven / Gradle / Ivy

There is a newer version: 4.1.4
Show newest version
///////////////////////////////////////////////////////////////////////////////

    Copyright (c) 2020, 2024 Oracle and/or its affiliates.

    Licensed under the Apache License, Version 2.0 (the "License");
    you may not use this file except in compliance with the License.
    You may obtain a copy of the License at

        http://www.apache.org/licenses/LICENSE-2.0

    Unless required by applicable law or agreed to in writing, software
    distributed under the License is distributed on an "AS IS" BASIS,
    WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
    See the License for the specific language governing permissions and
    limitations under the License.

///////////////////////////////////////////////////////////////////////////////

= Helidon SE OIDC Security Provider Guide
:description: Helidon OIDC Security Provider
:keywords: helidon, security, guide, oidc, provider
:rootdir: {docdir}/../..

include::{rootdir}/includes/se.adoc[]

This guide describes how to set up Keycloak and Helidon
to secure your application with OIDC security provider.

== What You Need

For this 20 minute tutorial, you will need the following:

include::{rootdir}/includes/prerequisites.adoc[tag=prerequisites]

In addition, you will need to install and configure the following:

* <>
* <>
* <>
* <>
* <>
* <>

== Introduction

This guide describes the steps required to protect your whole application or a specific area with Open ID Connect
(OIDC) security. OIDC is a secure mechanism for an application to contact an identity service.
It's built on top of OAuth 2.0 and provides full-fledged authentication and authorization protocols.

== Keycloak Installation [[Keycloak-Installation]]

=== On Docker

To install Keycloak with Docker, open a terminal and make sure the port 8080 is free.

[source,bash]
.Enter the following command
----
docker run -p 8080:8080 -e KEYCLOAK_USER=admin -e KEYCLOAK_PASSWORD=admin quay.io/keycloak/keycloak:11.0.2
----

This will start Keycloak on local port 8080. It will create the admin user with username `admin` and password `admin`
Feel free to modify 11.0.2 by any keycloak version of your wish.
If you are running docker behind a proxy server, make sure it is either configured into docker or
disabled. Otherwise, you might face a connection timeout because docker cannot download the required data.

To verify that Keycloak is running correctly, go to the admin console : http://localhost:8080/auth/admin
Log in using the username and password mentioned above: `admin`.

You should be logged in successfully, and it prompts the admin console.

=== On JDK

Download the last version of Keycloak from Keycloak website : https://www.keycloak.org/downloads
In the table Server choose Standalone server distribution. ZIP or Tar format are available, click on either
to download Keycloak.

After extracting the archive file, you should have a directory named keycloak followed by the version. For example,
if you chose version 11.0.2, the folder must be named keycloak-11.0.2.

Open keycloak folder to make it your current directory.
[source,bash]
.Run this command from command prompt to open the directory:
----
cd keycloak-11.0.2
----

== Start Keycloak

To start keycloak and have it ready for further steps, run the following command.

[source,bash]
.On Linux run:
----
bin/standalone.sh
----

[source,bash]
.On Windows run:
----
bin/standalone.bat
----

Keycloak runs on localhost:8080 by default.

=== Create an Admin User

You need to create an admin user because it does not come by default when installing Keycloak.
To do this, open  http://localhost:8080/auth in your favorite browser.

A window `Welcome to Keycloak` should be prompted. If not, check if any error appear in the terminal.

Fill the form by adding Username and Password. Click on `Create` to create the admin user.

Above Administration Console should be printed "User created" in a green rectangle.

To check that the admin user was created correctly, click on Administration user which should redirect you
to a Login form. Enter the Username and Password created earlier to log in.

After successfully logged in, the admin console is prompted.

== Setup Keycloak [[Setup-Keycloak]]

To set up Keycloak properly, go to the admin console: http://localhost:8080/auth/admin

If you are using Docker, use Username `admin` and password `admin` as it is the default admin user.
Otherwise, use the username and password you used to create the admin user.

=== Create a Realm

A realm is the place where groups of applications, and their environment, can be created. It gathers :

- One or several applications
- One or several users
- Sessions
- Events
- Clients and their scopes

By default, there is a realm called `Master`. It is used to manage Keycloak. It is not recommended to associate your
application with this realm as it could disturb Keycloak functioning.

To create a new realm to manage your application:

. Open Keycloak admin console http://localhost:8080/auth/admin.
. Hover the mouse over the dropdown in the top-left corner where it says `Master`, and press `Add realm`.
. Fill the form by adding the realm name, `myRealm` for example.
. Click on `Create` to create the new realm.

To verify that your realm is created, you should see your realm name (or `myRealm` if you followed the example) in the top-left
 corner where it said `Master` previously

To switch from a realm to another, hover the realm name, and the other realm created appear in the dropdown.
Click on any realm name to change the current realm. Make sure all configuration or modification are saved before changing
the current realm or be subject to lose your configuration.

=== Create a User

Initially there are no users in a new realm. An unlimited number of user can be created per realm.
A realm contains resources such as client which can be accessed by users.

To create a new user:

. Open the Keycloak admin console: http://localhost:8080/auth/admin
. Click on `Users` in the left menu
. Press `Add user`
. Fill the form (Username is the only mandatory field) with this value Username: `myUser`
. Click `Save`

A new user is just created, but it needs a password to be able to log in. To initialize it, do this:

. Click on `Credentials` at the top of the page, under `Myuser`.
. Fill `Password` and `Password confirmation` with the user password of your choice.
. If the `Temporary` field is set to `ON`, the user has to  update password on next login. Click `ON`
to make it `OFF` and prevent it.
. Press `Set Password`.
. A pop-up window is popping off. Click on `Set Password` to confirm the new password.

To verify that the new user is created correctly:

. Open the Keycloak account console: `http://localhost:8080/auth/realms/myRealm/account`.
. Login with `myUser` and password chosen earlier.

You should now be logged-in to the account console where users can manage their accounts.

=== Create a Client

To create your first client:

. Open the Keycloak admin console: http://localhost:8080/auth/admin.
. Make sure the current realm is `myRealm` and not `Master`.
. Navigate to the left menu, into configure section, click on `Clients`. This window displays a table with every client
from the realm.
. Click on `Create`.
. Fill the following:
.. `Client ID` : `myClientID`
.. `Client Protocol` : `openid-connect`
. Press `Save`
.. Modify `Access type` : `confidential`
.. Update `Valid Redirect URIs` : http://localhost:7987/*
.. Click on `+` to add the new URI.
. Click on `Save`.

A new tab named `Credentials` is created. Click on it to access this new tab.

- Select `Client Authenticator` : `Client ID and Secret`
- Click on `generate secret` to generate client secret.

Keycloak is now configured and ready. Keep keycloak running on your terminal and open a new tab to
set up Helidon.

== Setup Helidon [[Setup-Helidon]]

Use the Helidon SE Maven archetype to create a simple project. It will be used as an example
to show how to set up Helidon. Replace `{helidon-version}` by the latest helidon version.
It will download the quickstart project into the current directory.

[source,bash,subs="attributes+"]
.Run the Maven archetype
----
mvn -U archetype:generate -DinteractiveMode=false \
    -DarchetypeGroupId=io.helidon.archetypes \
    -DarchetypeArtifactId=helidon-quickstart-se \
    -DarchetypeVersion={helidon-version} \
    -DgroupId=io.helidon.examples \
    -DartifactId=helidon-quickstart-se \
    -Dpackage=io.helidon.examples.quickstart.se
----

[source,bash]
.The project will be built and run from the helidon-quickstart-se directory:
----
cd helidon-quickstart-se
----

=== Update Project Dependencies

Update the pom.xml file and add the following Helidon dependency to the `` section.

[source,xml]
.Add the following dependency to `pom.xml`:
----

    io.helidon.security.providers
    helidon-security-providers-oidc

----

=== Add OIDC Security Properties

The OIDC security provider configuration can be joined to helidon configuration file.
This file is located here: `src/main/resources/application.yaml`. It can be easily used to configure the web server
without modifying application code.

[source,yaml]
.Add the following line to application.yaml
----
server:
  port: 7987
  host: localhost
  features:
    security:
        # protected paths on the web server
        paths: # <1>
          - path: "/greet"
            methods: ["get"]
            authenticate: true
security:
  providers:
  - abac:
      # Adds ABAC Provider - it does not require any configuration
  - oidc:
      client-id: "myClientID" # <2>
      client-secret: "Client secret generated into Keycloak client credential" # <3>
      identity-uri: "http://localhost:8080/auth/realms/myRealm" # <4>
      audience: "account"
      header-use: "true"
      # proxy-host should be defined if you operate behind a proxy, can be removed otherwise
      proxy-host: ""
      frontend-uri: "http://localhost:7987" # <5>
      server-type: "oidc"
----
<1> `paths` section defines the protected application's path.
<2> `client-id` must be the same as the one configure in keycloak.
<3> The client secret generate by Keycloak during `Create a client` section.
<4> `identity-uri` is used to redirect the user to keycloak.
<5> `frontend-uri` will direct you back to the application.

Make sure keycloak and the application are not running on the same port.
The application port value can be changed into application.yaml.

If the port 7987 is already used, check what port is free on your machine.

[source,yaml]
.Replace the old port into application.yaml
----
server:
  port: "{Your-new-port}"

frontend-uri: "http://localhost:{Your-new-port}"
----

=== Configure Web Server

Once the properties are added, the web server must be setup.
The `Main#routing` method gather all configuration properties.

[source,java]
.Add the following to the `Main#routing` method
----
include::{sourcedir}/se/guides/SecurityOidcSnippets.java[tag=snippet_1, indent=0]
----
<1> Create and register `OidcFeature`.

That code is extracting security properties from application.yaml into two steps.
First the Security instance is used to bootstrap security, so the SecurityFeature instance
can integrate security into Web Server.
Then, OidcFeature instance registers the endpoint to which OIDC redirects browser after a successful login.

Helidon sample is now setup and ready.

== Build the Application

[source,bash]
.Build the application, skipping unit tests, then run it:
----
mvn package -DskipTests=true
java -jar target/helidon-quickstart-se.jar
----

The tests must be skipped, otherwise it produces test failure. As the `/greet` endpoint for GET request is
now protected, its access is limited, and the tests are not built to take oidc security in account.

. Open your favourite browser and try to access `http://localhost:7987/greet/Michael`.
. You should not be redirected and receive greeting from the application.
. Enter the following into URL : `http://localhost:7987/greet`.
. Keycloak redirect you to its login page.
. Enter the username and associated password:
.. `Username` : `myUser`
.. `Password`: `password`
. After successful log in, keycloak redirect you to the `http://localhost:7987/greet` endpoint and print Hello word.
. Press `Ctrl+C` to stop the application.

From the actual settings, the user needs to log in only once, then Keycloak saves all the connection data.

=== Test Keycloak Process with Postman [[Test-Keycloak-process-with-Postman]]

Keycloak supports many authentication and authorization flows, but only two of them will be shown. This section
describes another way you can get an access token or refresh a token or identity token. The identity token contains
information about the user. The access token contains access information that the application can use to determine what
resources the user is allowed to access. Once expired, the refresh token allows the application to obtain a new access
token. As these tokens contain sensitive information, they are valid for a very short period. It is possible to make them
last longer in order to let you manipulate them with Postman. To do so:

1. Open the Keycloak Console.
2. Click on the `Realm Setting` in the left menu.
3. Navigate to the `Tokens` tab.

You can increase the access token lifespan.

==== Authorization Code Flow

The Authorization Code flow is suitable for browser-based applications. It is composed of three main steps:

1. The browser visits the application. The user is not logged in, so it redirects the browser to Keycloak which requires
username and password for authentication.
2. Keycloak authenticates the user and returns a temporary authorization code as a query parameter in the URL.
3. The authorization code is used to get access and refresh token from Keycloak token endpoint.

For the first step, paste the following URL into your browser:
`http://localhost:8080/auth/realms/myRealm/protocol/openid-connect/auth?client_id=myClientID&response_type=code`.
The first part of the url `http:/../auth` is the Keycloak endpoint to request an authorization code. Two query
parameters are provided, the client id and the response type.
Press enter and Keycloak responds with different URL containing a query parameter `code`. You successfully received
the authorization code.

In order to achieve the third step, we can use Postman to exchange the authorization code for tokens. In Postman,
select the Http POST method. Keycloak endpoint to get token is the following:
`http://localhost:8080/auth/realms/myRealm/protocol/openid-connect/token`.
In the body of the request, select `x-www-form-urlencoded` type. Add the following data:

[source,json]
.Enter the key:value
----
[
  {"key":"grant_type","value":"authorization_code"},
  {"key":"client_id","value":"myClientID"},
  {"key":"client_secret","value":"client secret"},
  {"key":"code","value":"authorization code"}
]
----

Do not forget to replace the `client secret` by its value (generated during Create a Client), and `authorization code`
by the code value in the query parameter. Send the request by pressing `Send`. Keycloak returns an access token and
a refresh token.

==== Resource Owner Password Credentials Grant (Direct Access Grants)

The Direct Access Grants flow is used by REST clients that want to request tokens on behalf of a user.
To use Postman to make this request on behalf of `myuser`, select the GET method and enter this URL:
`http://localhost:7987/greet/`. Under `Authorization` tab, select authorization type`OAuth 2.0`. Under it, complete the
sentence  `Add authorization data to` with `Request Headers`, and complete the required fields.

Note:
Make sure your Helidon application is running. If it is not, please start it.

[source,json]
.Enter the following information:
----
[
  {"key":"Header Prefix","value":"bearer"},
  {"key":"Grant type","value":"Password  Credentials"},
  {"key":"Access Token URL","value":"http://localhost:8080/auth/realms/myRealm/protocol/openid-connect/token"},
  {"key":"Client ID","value":"myClientID"},
  {"key":"Client Secret","value":"client secret"},
  {"key":"Username","value":"myuser"},
  {"key":"Password","value":"password"},
  {"key":"Scope","value":"openid"},
  {"key":"Client Authentication","value":"Send as Basic Auth Header"}
]
----

Again, make sure to replace `client secret` by the actual client secret. Click on `Get New Access Token`. A popup
window appears with Authentication complete, click on proceed to display access, refresh and identity token.
Copy and paste the access token to `Access Token` field and press `Send`. Helidon greeting application sends back
`Hello World !`.

==== Update Tests to the Secure Environment

At this stage of the application, tests cannot pass because of OIDC security. The only way to authenticate a user is
through the front end of that server which can be accessed with the browser for example.

In order to keep security and test the application locally, a new security provider must be setup. By adding specific
configuration to the tests, it is possible to override the application configuration.

The following explains how to set a basic authentication instead of oidc security provider only for the tests. Which means,
at the end of this guide, the application will be secured by oidc security provider, and the tests will use basic authentication.

[source,xml]
.Add the following dependency to `pom.xml`:
----

    io.helidon.security.providers
    helidon-security-providers-http-auth
    test

----

In the test folder open the application.yaml file: `helidon-quickstart-se/src/test/resources/application.yaml`

[source,yaml]
.Copy these properties into application.yaml
----
app:
  greeting: "Hello"

server:
  port: 7987
  host: localhost

security:
  providers:
    - abac:
      # Adds ABAC Provider - it does not require any configuration
    - http-basic-auth:
        users:
          - login: "jack"
            password: "jackIsGreat"
    - oidc:
        client-id: "myClientID" # <1>
        client-secret: "Your client secret" # <2>
        identity-uri: "http://localhost:8080/auth/realms/myRealm"
        audience: "account"
        frontend-uri: "http://localhost:7987"
        server-type: "oidc"
  web-server:
    # protected paths on the web server - do not include paths served by Jersey, as those are protected directly
    paths:
      - path: "/greet"
        methods: ["get"]
        authenticate: true
----
<1> Replace this field by your Keycloak client ID.
<2> Replace this field by your Keycloak client Password.

Add the `http-basic-auth` properties in the security -> providers property section. This configuration will be used
by the tests instead of the `java/resources/application.yaml`.

In the `AbstractMainTest.java` file, tests need to be modified to check the application security when accessing `/greet` path with a
`GET` method.

[source,java]
.Replace the first webclient call by this one into testRootRoute method:
----
include::{sourcedir}/se/guides/SecurityOidcSnippets.java[tag=snippet_2, indent=0]
----

This piece of code uses the webclient to access the application on `/greet` path with a `GET` method. The http basic
authentication security protects this path, so the client should receive an HTTP 401 code for unauthorized.

Only `jack` user has access to this part of the application.

[source,java]
.Add new check to the testRootRoute method:
----
include::{sourcedir}/se/guides/SecurityOidcSnippets.java[tag=snippet_3, indent=0]
----

The username and password are encoded and placed inside the header in order to authenticate as jack to access the application.
If the authentication is successful, the application send the `Hello World` back as a `JsonObject`.

Now, the project can be built without skipping test.

[source,bash]
.Build the project
----
mvn clean install
----

==== Restrict Access to a Specific Role [[Restrict-access-to-a-specific-role]]

To give less access to an endpoint, it is possible to configure user role. So the application will only grant access
to the user with the required role.

Add a user and roles to the `helidon-quickstart-se/src/test/resources/application.yaml`.

[source,yaml]
.Add jack role and create a new user named john:
----
- http-basic-auth:
    users:
      - login: "jack"
        password: "jackIsGreat"
        roles: [ "admin", "user" ]
      - login: "john"
        password: "johnPassword"
        roles: [ "user" ]
----

Into the `web-server` section, the `roles-allowed` parameter defines which roles have access
to the protected path and method.

[source,yaml]
.Add `admin` role
----
web-server:
    # protected paths on the web server
    # do not include paths served by Jersey
    # as those are protected directly
    paths:
      - path: "/greet"
        methods: ["get"]
        roles-allowed: "admin"
        authenticate: true
----

Now, only Jack has access to secure endpoint as he has an admin role. John, as a simple user, can not access it.
Once it is done, go to the tests to check the application behavior.
The test from previous section is still passing as jack has access.

The user `john` has only the `user` role so when accessing protected endpoint, a 403 (Forbidden) http code is returned.

[source,java]
.Check that john does not have access
----
include::{sourcedir}/se/guides/SecurityOidcSnippets.java[tag=snippet_4, indent=0]
----

[source,bash]
.Build the project
----
mvn clean install
----

The tests pass, and your application is secured with specific roles in addition to user IDs.




© 2015 - 2025 Weber Informatics LLC | Privacy Policy