Secure React Routes & Component with Keycloak
There are several authentication services out there such as Firebase Authentication, Auth0, etc. In sum project, you might need more control over your authorization and user management with advanced features.
That’s where Keycloak is an excellent choice, It provides lots of features out of the box. Keycloak has built-in support for OpenID Connect and SAML 2.0 as well as a number of social networks such as Google, GitHub, Facebook, and Twitter.
I wanted to write this because I couldn't find good examples when I’m doing this implementation to restrict react routes/components/function with keycloak with minimal 3rd party libraries.
In this short guide, we will walk through the following steps
- Install Keycloack — Get Started with Keycloack Docker
- Setup Realm, Client Id in Keycloak
- Create React App
- Install keycloak-js & @react-keycloak/web
- Setup Keycloak instance as needed
- Wrap Basic app with KeycloakProvider
- Routes restrict based on Keycloak Authentication & Authorization,
- Component/Function level restriction based on Keycloak Authentication & Authorization
Example code:
https://github.com/cagline/react-router-component-keycloak-example
Let’s get started with the integration
First, we need to install the Keycloak server. There are a few ways of installing Keycloak like Docker, OpenJDK, Openshift, Podman, Kubernetes. I prefer to install Keycloak using Docker since I’m familiar with Docker.
Then we need to Create a Realm, Client ID, and a User on Keycloack side to start with the rest of the implementation. I’m not going to explain more detail regarding keycloak configuration. But you’ll find good resources regarding Keycloak configuration at the end of the article.
Then you need to login to the master console with admin/admin default credentials to Setup Realm, Client Id & a user in Keycloak side.
Create Client Id for your react app in your project.
Most importantly Access Type needs to be selected as public. we can’t use Authorisation features that come with the confidential Access Type in a client-side app. Read more
Web origins, Valid Redirect URLs, Root URL configuration needs to carefully filed out.
Let’s start with create a fresh react app with create-react-app
// npm uninstall -g create-react-app to ensure that npx always uses the latest version.
npm install -g create-react-appnpx create-react-app my-keycloak-app
cd my-keycloak-app
npm start
Install @react-keycloak/web library
Pre requires:
keycloak-js 9.0.2 or later
npm install --save keycloak-jsnpm install --save @react-keycloak/web
Create a keycloak.js
file in the src folder of your project with the following content to set up a Keycloak instance as needed.
Set up a Keycloak instance
Then Wrap your App inside KeycloakProvider
and pass the keycloak
instance as prop
Then Wrap your app inside KeycloakProvider
There are two ways of accessing Keycloak
in a component.
- Wrap it inside the
withKeycloak
HOC (Higher-Order Components). - Use the
useKeycloak
Hook (React >=16.8 required).
I prefer using useKeycloak
hook 🙃, but I use both ways in this to demonstrate🙄. You will find useKeycloak
hook is used in AppRouter
& HomePage
components and withKeycloak
HOC used in Menu
components.
Let's create following files with the following content.
src/routes/index.js
(Routes of homepage & protected page)src/page/HomePage.js
(Public page with keycloak JSON)src/page/Menu.js
(Login & Logout buttons)
src/routes/index.js
As you see in AppRouter
component useKeycloak
is being used without 1st param because we don't need that keycloak instance object to be used. But 2nd initialized
parameter is being used because PrivateRoute
is using keycloak instance, and keycloak object need to be available on PrivateRoute
initialization, that means until keycloak initialized Routes cannot be returned.
src/page/HomePage.js
The HomePage
will show the initialized Keycloak object as a formatted JSON string to get more understanding.
src/page/Menu.js
The Menu
is implemented with HOC (Higher-Order Components) just to demonstrate 🙄. It renders the login & logout button according to the value of keycloak.authenticated
attribute.
Let’s jump into the most wanted part which is restricting Routes & Components.
We will work through:
- Route restriction
- Component restriction (with a utility function)
- Component restriction (with wrapper component).
Routes restrict based on Keycloak Authorization
In the Keycloak there are two ways of assigning roles to the user. A role can assign to Realm or Client. In this example, I create a role called ReamAdmin
under Demo
realm and ClientAdmin
under react-app
client.
In that case, You have to consider checking authorization using both hasRealmRole()
& hasResourceRole()
methods available in Keycloak.
Create a PrivateRoute.js
file in src/utilities
folder of your project with the following content.
Then change routes/index.js
the file as below
If the login user doesn’t have a role called RealmAdmin,
it gets redirected to the homepage.
The user gets redirected back to the home page when the user tries to access the Protected page since user “chanuka” only has ClientAdmin
roles in the above scenario. ( ClentAdmin
role assign to react-app
client — as you see it’s included in resource_access
attribute.
In the below scenario the user can access the Protected page since user “Asanka” has a role called RealmAdmin
.
Here user able to access the protected page because of user “asanka” has RealmAdmin
role, and its assign to the current realm — as you see in its included in realm_access
attribute.
Let’s see how to restrict Components/Functions with a Keycloak.
Create a AuthorizedFunction.js
file in src/utilities
folder of your project with the following content.
Then change src/components/Menu.js
the file as below
The above solution is ideal for functional restriction and restricts rendering elements.
Also, there is another way of restrict with anther wrapper component as below example. (with wrapper component)
Create a AuthorizedElement.js
file in src/components
folder of your project with the following content.
Also, AuthorizedFunction utility function can be used to remove isAuthorized method duplication from AuthorizedElement & PrivateRoute as bellow example.
Then change src/pages/HopePage.js
the file as below
Above solution is ideal for restrict UI components as below
This is the look of the homepage before login with keycloak credentials. There are two buttons supposed to render based on the user role. Let’s see how it’s work after the user logged in.
The homepage looks like below when the user “chanuka” logged in as a ClientAdmin
The homepage looks like below when the user “Asanka” logged in as a RealmAdmin
I hope this Route/Component/Function restriction with roles is help full for your development.
- Route restriction
- Component restriction (with a utility function)
- Component restriction (with wrapper component).
Resources:
https://www.youtube.com/watch?v=XJYy6Aq-PJ8&t=639s
Still If you feel lazy to write your own utilities , Here is a library which wrap these utilities as a library for you.