Building a Secure Authentication System with Keycloak, React, and Flask
Hola
Building a Secure Authentication System with Keycloak, React, and Flask
Hola
During my previous role, I had the opportunity to work with Auth0. Seeing how effortlessly we could leverage a third-party provider to authenticate users before granting them access to our system was a revelation. However, I also understood the potential cost implications, which, if not appropriately managed, could significantly impact your budget.
With that experience, when I heard about Keycloak, it caught my attention because, after the first check, it fit the most important features that Auth0 has or the ones I used the most.
What is Keycloak?
From their website
Open Source Identity and Access Management
Add authentication to applications and secure services with minimum effort. No need to deal with storing users or authenticating users.
In a nutshell, Keycloak provides user federation, strong authentication, user management, fine-grained authorization, and more.
I decided to do a simple proof of concept to check how keycloak could work as an auth service. Everything is set in this Keycloak PoC Repository.
This is the diagram of what we are going to build.

I used mermaid.js and in the README of the project is the code behind
Setting Up Keycloak
I decided to go with the docker option using their image. The main instructions are here and it’s a simple docker-compose yaml.
version: '3.9'
services:
postgres:
image: postgres:15
environment:
POSTGRES_DB: ${POSTGRESQL_DB}
POSTGRES_USER: ${POSTGRESQL_USER}
POSTGRES_PASSWORD: ${POSTGRESQL_PASS}
volumes:
- postgres_data:/var/lib/postgresql/data
keycloak:
image: quay.io/keycloak/keycloak:${KEYCLOAK_VERSION}
environment:
KEYCLOAK_ADMIN: ${KEYCLOAK_ADMIN_USER}
KEYCLOAK_ADMIN_PASSWORD: ${KEYCLOAK_ADMIN_PASS}
KC_DB: postgres
KC_DB_URL: jdbc:postgresql://postgres/${POSTGRESQL_DB}
KC_DB_USERNAME: ${POSTGRESQL_USER}
KC_DB_PASSWORD: ${POSTGRESQL_PASS}
ports:
- 8080:8080
command:
- start-dev
volumes:
postgres_data:
As we could see there, I decided to use a Postgres database and a volume so we don’t lose the data after each docker restart. The basic information to set is defined in the .env.example and is the following:
KEYCLOAK_VERSION=20.0
PORT_KEYCLOAK=8080
POSTGRESQL_USER=keycloak
POSTGRESQL_PASS=keycloak
POSTGRESQL_DB=db-auth
KEYCLOAK_ADMIN_USER=admin
KEYCLOAK_ADMIN_PASS=change_me
If you just create a copy of it and name it .env, you will have a running version in port 8080 of version 20.0 with the credentials mentioned there.
Keycloak Setup
In order to set a basic environment in Keycloak, we will create a Realm which is a way to create isolated environments for managing users and applications, and then different clients, depending on the needs we we will have to allow connections through our authentication system
Realm Settings
Go to the realm section and create a new one. For this example, let’s called it “myrealm”

We will be using the default login provided by Keycloak, so we will also allow user registration. Go to Realm Settings and enable the User Registration option

Client Settings
For our Proof of concept, as we see in our initial design, we will have two clients: a Web Application and an API. The following instructions are the same for both cases, so go to the Clients section and create two new clients, one for the web and another for the api.
As a web example, let’s call it client-web

This will be the client for the web app, and we should provide the proper URLs allowed to interact with this client. For the sake of this test, we will use a wildcard (*), but in PROD, we should include the proper URLs here.

We will use wildcards () as testing purposes*
In the Capability config, we will only set Standard Flow and Direct Access grants.

Creating the React Application
The web app will be a classic React application, with Typescript and using Vite as a development server. It is placed here in the repository.
Configuration
The main thing here is to properly set up the environment variables, to do that, I set an example there, so you have to create a .env file and fill in the following data, I’m going to explain how it will be if we are using the default values
VITE_KEYCLOAK_URL=http://0.0.0.0:8080/
VITE_KEYCLOAK_REALM=myrealm
VITE_KEYCLOAK_CLIENT=client-web
Keycloak Connection
In this example, I used the keycloak-js library and set everything in a context called KeycloakContext. When I click Login, I use the keycloak client created and provided by the context and call the login method.
About the KeycloakContext it’s being initialized at the beginning of the app because it acts as a provider (check App.tsx ) and the main part is defined here:
const initKeycloak = async () => {
const keycloackConfig = {
url: import.meta.env.VITE_KEYCLOAK_URL as string,
realm: import.meta.env.VITE_KEYCLOAK_REALM as string,
clientId: import.meta.env.VITE_KEYCLOAK_CLIENT as string,
}
const keycloakInstance: Keycloak = new Keycloak(keycloackConfig)
keycloakInstance
.init({
onLoad: 'check-sso',
})
.then((authenticated: boolean) => {
setAuthenticated(authenticated)
})
.catch((error) => {
console.error('Keycloak initialization failed:', error)
setAuthenticated(false)
})
.finally(() => {
setKeycloak(keycloakInstance)
console.log('keycloak', keycloakInstance)
})
}
Using the configuration values, I’m making the connection with Keyloak using check-sso. The other option to use is login-required, and both do the same, except the first one redirects the user to the login page if it's not authenticated.
Running the App
By simply doing yarn run dev you will have a web page with a login button that will redirect you to the Keycloak default login screen (all of this is fully customisable, but I will let it for another post)



This is the flow you will see while doing login -> Register
If you follow all the steps after logging in you will something similar like the following

Developing the Flask API
I’m using the word simple a lot, which is not the exception. To run an API, I created a basic Flask application that will have three endpoints
- /api/public -> to test that the API is accessible without a token
- /api/private -> to test that the API is only accessible with a valid token
- /api/users/<string:user_id>/items -> to test a valid use case of getting items from a user
For this example, you could create a .env file and setting with the following values (if you are following the default ones). I'm also using a SQLite version to store the data.
KEYCLOAK_SERVER_URL=http://localhost:8080
KEYCLOAK_REALM_NAME=myrealm
KEYCLOAK_CLIENT_ID=client-api
SQLALCHEMY_DATABASE_URI=sqlite:///project.db
After that the main point here is how to validate the token, and to do that I’m using authlib library for the token validation. All the “magic” is defined in auth.py
import json
import os
from urllib.request import urlopen
from authlib.integrations.flask_oauth2 import ResourceProtector
from authlib.jose.rfc7517.jwk import JsonWebKey
from authlib.oauth2.rfc7523 import JWTBearerTokenValidator
from api.models import verify_user
KEYCLOAK_SERVER_URL = os.getenv("KEYCLOAK_SERVER_URL")
KEYCLOAK_REALM_NAME = os.getenv("KEYCLOAK_REALM_NAME")
KEYCLOAK_ISSUER = f"{KEYCLOAK_SERVER_URL}/realms/{KEYCLOAK_REALM_NAME}"
class ClientCredsTokenValidator(JWTBearerTokenValidator):
def __init__(self, issuer):
jsonurl = urlopen(f"{issuer}/protocol/openid-connect/certs")
public_key = JsonWebKey.import_key_set(json.loads(jsonurl.read()))
super(ClientCredsTokenValidator, self).__init__(public_key)
self.claims_options = {
"exp": {"essential": True},
"iss": {"essential": True, "value": issuer},
}
def validate_token(self, token, scopes, request):
super(ClientCredsTokenValidator, self).validate_token(token, scopes, request)
verify_user(token)
require_auth = ResourceProtector()
validator = ClientCredsTokenValidator(KEYCLOAK_ISSUER)
require_auth.register_token_validator(validator)
We are getting the open ID certs provided by our Keycloak instance (for the specific realm), and the library validates that the provided token is valid and has not expired.
As an extra point here, the user data we are seeing in the token lives in Keycloak. For this example, I decided to also have an API instance of the user where I will be storing extra information there (this is optional and will depend on how are you defining your architecture). So for that reason, I override validate_token method in the JWTBearerTokenValidator because I am checking if the user token is valid, and I do not have a record in my db, so I create a new one with the information provided by the JWT.
Integrating React with Flask
Now that our Web App and API are running with proper connections to Keycloak, let's move on to how both clients interact with each other.
All about this part is located in MyItems.tsx in the web project and we are using the keycloak context only when is authenticated and we will call to our api by using the bearer token provided.
useEffect(() => {
const fetchItems = async () => {
try {
if (!keycloak) {
return;
}
const userId = keycloak.idTokenParsed?.sub;
setLoading(true);
setError(null);
const response = await fetch(`/api/users/${userId}/items`, {
headers: {
Authorization: `Bearer ${keycloak.token}`
}
});
if (!response.ok) {
setError('Error fetching items.');
return;
}
const data = await response.json();
setItems(data.items);
} catch (error) {
console.error('Error fetching items:', error);
setError('Unexpected error occurred.');
} finally {
setLoading(false);
}
};
if (authenticated) {
fetchItems();
}
}, [keycloak, authenticated]);
More specifically in the following part we use the user token
const response = await fetch(`/api/users/${userId}/items`, {
headers: {
Authorization: `Bearer ${keycloak.token}`
}
});
If we go go the My Items section and open the dev tools you will see how the connection is made and we are getting a mock data

Conclusions
Keycloak works great as an auth service and provides a solid place to control and expand the clients you want to access your information. In this example, upcoming points should be to start creating real data (items), so that when you call the user’s items endpoint, you will get the proper info, but the main point of this article is to show the general overview.
This will work great if you have mobile apps, too, so you can control login access and the token lifecycle in a centralized place (and go beyond that and allow other developers to start connecting with your data by creating specific clients with more strict permissions for them).
Thank you for reading here, and I hope it likes you, the guide and explanation of the Keycloak usage.
메타데이터
- post_id
- 35aeee04e37a
- slug
- building-a-secure-authentication-system-with-keycloak-react-and-flask-35aeee04e37a
- url
- https://medium.com/@darkaico/building-a-secure-authentication-system-with-keycloak-react-and-flask-35aeee04e37a
- canonical_url
- https://medium.com/@darkaico/building-a-secure-authentication-system-with-keycloak-react-and-flask-35aeee04e37a
- author_url
- https://medium.com/@darkaico
- status
- ok
- fetched_at
- 2026-06-18 00:10:23