Skip to content

Commit f0e8779

Browse files
committed
feat: New direct integration between the Access Manager platform and the OpenFGA with the new extension.
1 parent ea1a413 commit f0e8779

19 files changed

+324
-165
lines changed

Diff for: README.md

+11-20
Original file line numberDiff line numberDiff line change
@@ -1,10 +1,12 @@
11
# Keycloak integration with OpenFGA (based on Zanzibar) for Fine-Grained Authorization at Scale (ReBAC)
2-
This repository contains a PoC implemented with [Keycloak](https://www.keycloak.org/) integrated with [OpenFGA](https://openfga.dev/) on demostrating how to apply fine-grained access control in a high performance and flexible authorization.
2+
This repository contains a PoC implemented with [Keycloak](https://www.keycloak.org/) integrated with [OpenFGA](https://openfga.dev/) on demostrating how to apply fine-grained access (FGA) control in a high performance and flexible authorization.
3+
4+
In this new version of the PoC we have a direct integration between the Access Manager platform and the OpenFGA solution thanks to the use of the [OpenFGA Java SDK](https://github.com/openfga/java-sdk) to publish the events.
35

46
This workshop is based the following article [Keycloak integration with OpenFGA (based on Zanzibar) for Fine-Grained Authorization at Scale (ReBAC)](https://embesozzi.medium.com/keycloak-integration-with-openfga-based-on-zanzibar-for-fine-grained-authorization-at-scale-d3376de00f9a). You will find there full details about the authorization architecture guidelines and involved components.
57

68

7-
## Authorization Framework
9+
## Authorization Framework (New)
810

911
The following diagram illustrates the solution architecture of this workshop:
1012

@@ -13,23 +15,13 @@ The following diagram illustrates the solution architecture of this workshop:
1315
</p>
1416

1517
* Core:
16-
17-
* Keycloak (A) is responsible for handling the authentication with the standard OpenID Connect and is managing the user access with his Role Model
18-
19-
* Keycloak is configure with a custom extension (B) [keycloak-openfga-event-listener](https://github.com/embesozzi/keycloak-openfga-event-listener) which listens to the Keycloak events (User Role Assignment, Role to Role Assignment, etc), parses this event into an OpenFGA tuple based on the [Keycloak Authz Schema](openfga/keycloak-authorization-model.json) and publishes the event to Kakfa Cluster (C)
20-
21-
* Kakfa OpenFGA Consumer (D) that using the OpenFGA SDK will publish the tuples to the OpenFGA Solution
22-
23-
* OpenFGA (E) is responsible for applying fine-grained access control. The OpenFGA service answers authorization checks by determining whether a relationship exists between an object and a user
24-
18+
* Keycloak is responsible for handling the authentication with the standard OpenID Connect and manages user access with its Role Model.
19+
* Keycloak is configured with a new custom extension :rocket: [keycloak-openfga-event-publisher](https://github.com/embesozzi/keycloak-openfga-event-publisher) which listens to the Keycloak events (User Role Assignment, Role to Role Assignment, etc), parses this event into an OpenFGA tuple based on the [Keycloak Authorization Schema](model.dsl) and publishes them to OpenFGA over HTTP.
20+
* OpenFGA is responsible for applying fine-grained access control. The OpenFGA service answers authorization checks by determining whether a relationship exists between an object and a user.
2521
* Other components
26-
2722
* Store Web Application is integrated with Keycloak by OpenID Connect
28-
2923
* Store API is protected by OAuth 2.0 and it utilizes the OpenFGA SDK for FGA
3024

31-
So far we don’t have an official Java SDK OpenFGA client to publish the authorization tuples. This is why I decided to use an Apache Kafka cluster for managing the events. Nevertheless, the extension is prepared for the future to use an http client for publishing the events.
32-
3325
# How to install?
3426
## Prerequisites
3527

@@ -46,7 +38,7 @@ So far we don’t have an official Java SDK OpenFGA client to publish the author
4638
2. Execute following Docker Compose command to start the deployment
4739

4840
```sh
49-
docker-compose -f docker-compose.yml -f docker-compose-apps.yml up
41+
docker-compose -f docker-compose.yml -f docker-compose-apps.yml -f docker-compose-openfga.yml up
5042
```
5143

5244
3. To be able to use this environment, you need to add this line to your local HOSTS file:
@@ -61,7 +53,6 @@ So far we don’t have an official Java SDK OpenFGA client to publish the author
6153
| ------------------------- |:-----------------------------:|:-----------:|:-----------:|:-----------:
6254
| Keycloak Console | http://keycloak:8081 | admin | password | quay.io/keycloak/keycloak:19.0.2 |
6355
| OpenFGA Playground | http://localhost:3000/playground | | | openfga/openfga:latest |
64-
| OpenFGA API | http://localhost:8080 | | | confluentinc/cp-zookeeper:7.2.2<br />confluentinc/cp-kafka:7.2.2|
6556
| Store Portal | http://store:9090 | | | Custom image |
6657
| Store API | http://store-api:9091 | | | Custom image |
6758

@@ -85,7 +76,7 @@ So far we don’t have an official Java SDK OpenFGA client to publish the author
8576
* Open [administration console](http://keycloak:8081)
8677
* Choose realm
8778
* Realm settings
88-
* Select `Events` tab and add `openfga-events` to Event Listeners.
79+
* Select `Events` tab and add `openfga-events-publisher` to Event Listeners.
8980

9081
<img src="doc/images/kc-admin-events.png" width="80%" height="80%">
9182

@@ -103,7 +94,7 @@ So far we don’t have an official Java SDK OpenFGA client to publish the author
10394

10495
The password for all the users is `demo1234!`
10596

106-
Once these steps are finished, the Keycloak OpenFGA Event Listener extension has to proceed to publish these events to the Kafka topic called “openfga-topic”. Then, the Kafka consumer has published those events to the OpenGFA store using the SDK. Here are all tuples stored.
97+
Once these steps are finished, the Keycloak OpenFGA Event Publisher extension has proceed to send these events over HTTP to the OpenFGA solution. Here, are all tuples stored.
10798

10899
| User | Relation | Object |
109100
| ------------------------- |:-----------------------------:|:---------------------:|
@@ -116,7 +107,7 @@ So far we don’t have an official Java SDK OpenFGA client to publish the author
116107

117108
The users are identified by the value of the claim sub in the [OpenFGA Playground](http://localhost:3000/playground), see the Tuples tab:
118109

119-
<img src="doc/images/openfga-kc-authz-model.png" width="50%" height="50%">
110+
<img src="doc/images/openfga-tuples.png" width="50%" height="50%">
120111

121112

122113
3. Restart the apps (containers: `store` and `store-api`)

Diff for: doc/images/kafka-kc-admin-events.png

49.4 KB
Loading

Diff for: doc/images/kc-admin-events.png

155 KB
Loading

Diff for: doc/images/legacy-solution-architecture.png

92.3 KB
Loading

Diff for: doc/images/openfga-authz-model.png

-44.5 KB
Loading

Diff for: doc/images/openfga-tuples.png

457 KB
Loading

Diff for: doc/images/solution-architecture.png

-4.19 KB
Loading

Diff for: docker-compose-apps.yml

+2-1
Original file line numberDiff line numberDiff line change
@@ -13,7 +13,8 @@ services:
1313
VUE_APP_CLIENT_ID: portal
1414
depends_on:
1515
keycloak:
16-
condition: service_healthy
16+
condition: service_healthy
17+
1718
store-api:
1819
build: ./store-openfga-api
1920
image: embesozzi/store-openfga-api

Diff for: docker-compose-openfga.yml

+49
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,49 @@
1+
version: '3.8'
2+
3+
services:
4+
openfga-postgres:
5+
image: postgres:14
6+
container_name: openfga-postgres
7+
command: postgres -c 'max_connections=100'
8+
networks:
9+
- default
10+
ports:
11+
- "5432:5432"
12+
environment:
13+
- POSTGRES_USER=postgres
14+
- POSTGRES_PASSWORD=password
15+
healthcheck:
16+
test: [ "CMD-SHELL", "pg_isready -U postgres" ]
17+
interval: 5s
18+
timeout: 5s
19+
retries: 5
20+
21+
migrate:
22+
depends_on:
23+
openfga-postgres:
24+
condition: service_healthy
25+
image: openfga/openfga:v1.3.1
26+
container_name: migrate
27+
environment:
28+
- OPENFGA_DATASTORE_ENGINE=postgres
29+
- OPENFGA_DATASTORE_URI=postgres://postgres:password@openfga-postgres:5432/postgres?sslmode=disable
30+
command: migrate
31+
networks:
32+
- default
33+
34+
openfga:
35+
depends_on:
36+
migrate:
37+
condition: service_completed_successfully
38+
image: openfga/openfga:v1.3.1
39+
container_name: openfga
40+
command: run
41+
environment:
42+
- OPENFGA_DATASTORE_ENGINE=postgres
43+
- OPENFGA_DATASTORE_URI=postgres://postgres:password@openfga-postgres:5432/postgres?sslmode=disable
44+
- OPENFGA_DATASTORE_MAX_OPEN_CONNS=100
45+
networks:
46+
- default
47+
ports:
48+
- "8080:8080" #http
49+
- "3000:3000" #playground

Diff for: docker-compose.yml

+21-118
Original file line numberDiff line numberDiff line change
@@ -5,24 +5,29 @@ volumes:
55
driver: local
66

77
services:
8-
#
9-
# Keycloak IAM
10-
#
8+
119
keycloak-postgres:
12-
image: postgres:11
10+
image: postgres:14
1311
container_name: keycloak-postgres
14-
volumes:
15-
- postgres_data:/var/lib/postgresql/data
12+
# volumes:
13+
# - postgres_data:/var/lib/postgresql/data
1614
environment:
1715
POSTGRES_DB: keycloak
1816
POSTGRES_USER: keycloak
1917
POSTGRES_PASSWORD: password
18+
healthcheck:
19+
test: [ "CMD-SHELL", "pg_isready -U keycloak" ]
20+
interval: 5s
21+
timeout: 5s
22+
retries: 5
2023
ports:
2124
- 5433:5432
25+
2226
keycloak:
23-
build: ./keycloak
24-
image: embesozzi/keycloak
27+
image: quay.io/keycloak/keycloak:19.0.2
2528
container_name: keycloak
29+
command:
30+
- start-dev
2631
environment:
2732
KEYCLOAK_USER: admin
2833
KEYCLOAK_PASSWORD: password
@@ -35,117 +40,15 @@ services:
3540
KC_DB_PASSWORD: password
3641
KC_HOSTNAME_STRICT: 'false'
3742
KC_HTTP_ENABLED: 'true'
38-
KC_HOSTNAME_ADMIN: keycloak
39-
KC_HOSTNAME: keycloak
40-
KC_HEALTH_ENABLED: 'true'
41-
# Keycloak OpenFGA Event Listener SPI configuration
42-
KC_SPI_EVENTS_LISTENER_OPENFGA_EVENTS_SERVICE_HANDLER_NAME: KAFKA
43-
KC_SPI_EVENTS_LISTENER_OPENFGA_EVENTS_AUTHORIZATION_MODEL: '{"type_definitions":[{"type":"group","relationships":[{"relation":"assignee","object":"role"}]},{"type":"role","relationships":[{"relation":"assignee","object":"user"},{"relation":"parent","object":"role"},{"relation":"parent_group","object":"group"}]}]}'
44-
KC_SPI_EVENTS_LISTENER_OPENFGA_EVENTS_CLIENT_ID: keycloak-producer
45-
KC_SPI_EVENTS_LISTENER_OPENFGA_EVENTS_ADMIN_TOPIC: openfga-topic
46-
KC_SPI_EVENTS_LISTENER_OPENFGA_EVENTS_BOOTSTRAP_SERVERS: PLAINTEXT://kafka:19092
47-
KC_LOG_LEVEL: INFO,io.embesozzi.keycloak:debug
48-
healthcheck:
49-
test: ["CMD", "curl", "-f", "http://localhost:8080/health/ready"]
50-
interval: 5s
51-
timeout: 2s
52-
retries: 15
43+
KC_HOSTNAME_ADMIN: localhost
44+
KC_HOSTNAME: localhost
45+
KC_SPI_EVENTS_LISTENER_OPENFGA_EVENTS_PUBLISHER_API_URL: http://openfga:8080
46+
KC_LOG_LEVEL: INFO, com.twogenidentity.keycloak:debug,com.twogenidentity.keycloak.utils:debug
5347
ports:
5448
- 8081:8080
5549
- 8443:8443
50+
volumes:
51+
- $PWD/keycloak/lib/keycloak-openfga-event-publisher-1.0.0.jar:/opt/keycloak/providers/keycloak-openfga-event-publisher-1.0.0.jar
52+
- $PWD/keycloak/initialize-poc.sh:/opt/keycloak/initialize-poc.sh
5653
depends_on:
57-
- keycloak-postgres
58-
networks:
59-
default:
60-
aliases:
61-
- keycloak
62-
#
63-
# Kakfa Cluster
64-
#
65-
zookeeper:
66-
image: confluentinc/cp-zookeeper:7.2.2
67-
hostname: zookeeper
68-
container_name: zookeeper
69-
ports:
70-
- "2181:2181"
71-
environment:
72-
ZOOKEEPER_CLIENT_PORT: 2181
73-
ZOOKEEPER_SERVER_ID: 1
74-
kafka:
75-
image: confluentinc/cp-kafka:7.2.2
76-
hostname: kafka
77-
container_name: kafka
78-
ports:
79-
- "9092:9092"
80-
- "19092:19092"
81-
- "29092:29092"
82-
environment:
83-
KAFKA_ADVERTISED_LISTENERS: INTERNAL://kafka:19092
84-
KAFKA_LISTENER_SECURITY_PROTOCOL_MAP: INTERNAL:PLAINTEXT
85-
KAFKA_INTER_BROKER_LISTENER_NAME: INTERNAL
86-
KAFKA_ADVERTISED_HOST_NAME: kafka
87-
KAFKA_AUTO_CREATE_TOPICS_ENABLE: 'true'
88-
KAFKA_DELETE_TOPIC_ENABLE: 'true'
89-
KAFKA_CREATE_TOPICS: openfga-topic:1.1
90-
KAFKA_ZOOKEEPER_CONNECT: zookeeper:2181
91-
KAFKA_OFFSETS_TOPIC_REPLICATION_FACTOR: 1
92-
KAFKA_ADVERTISED_HOST_NAME: kafka
93-
depends_on:
94-
- zookeeper
95-
healthcheck:
96-
test: nc -z localhost 19092 || exit -1
97-
start_period: 15s
98-
interval: 5s
99-
timeout: 10s
100-
retries: 10
101-
#
102-
# OpenFGA
103-
#
104-
postgres:
105-
image: postgres:14
106-
container_name: postgres
107-
networks:
108-
- default
109-
ports:
110-
- "5499:5432"
111-
environment:
112-
- POSTGRES_USER=postgres
113-
- POSTGRES_PASSWORD=password
114-
healthcheck:
115-
test: [ "CMD-SHELL", "pg_isready -U postgres" ]
116-
interval: 5s
117-
timeout: 5s
118-
retries: 5
119-
migrate:
120-
depends_on:
121-
postgres:
122-
condition: service_healthy
123-
image: openfga/openfga:latest
124-
container_name: migrate
125-
command: migrate --datastore-engine postgres --datastore-uri 'postgres://postgres:password@postgres:5432/postgres?sslmode=disable'
126-
networks:
127-
- default
128-
openfga:
129-
depends_on:
130-
migrate:
131-
condition: service_completed_successfully
132-
image: openfga/openfga:latest
133-
container_name: openfga
134-
command: run --datastore-engine postgres --datastore-uri 'postgres://postgres:password@postgres:5432/postgres?sslmode=disable'
135-
networks:
136-
- default
137-
ports:
138-
- "8080:8080"
139-
- "3000:3000"
140-
#
141-
# Kafka OpenFGA Consumer
142-
#
143-
kafka-consumer-openfga:
144-
build: ./kafka-consumer-openfga
145-
image: embesozzi/kafka-consumer-openfga
146-
container_name: kafka-consumer-openfga
147-
depends_on:
148-
kafka:
149-
condition: service_healthy
150-
environment:
151-
KAFKA_ZOOKEEPER_CONNECT: zookeeper:2181
54+
- keycloak-postgres

Diff for: kafka-integration/README.md

+81
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,81 @@
1+
# Keycloak integration with OpenFGA via Kafka (Legacy)
2+
In this case the integration between [Keycloak](https://www.keycloak.org/) and [OpenFGA](https://openfga.dev/) is via Kafka. This was the first version of the PoC when it wasn't available an OpenFGA SDK for Java for publishing the event directly to OpenFGA - Luckly now there is :).
3+
4+
Therefore If you can follow the new approach with the direct integration between Keycloak and OpenFGA with the new extension describe [here](../README.md)
5+
6+
## Authorization Framework (Legacy)
7+
8+
In this case the following diagram illustrates the solution architecture of this workshop when you use the Kafka integration:
9+
10+
<p align="center">
11+
<img width="70%" height="70%" src="../doc/images/legacy-solution-architecture.png">
12+
</p>
13+
14+
* Core:
15+
16+
* Keycloak (A) is responsible for handling the authentication with the standard OpenID Connect and is managing the user access with his Role Model
17+
18+
* Keycloak is configure with a custom extension (B) [keycloak-openfga-event-listener](https://github.com/embesozzi/keycloak-openfga-event-listener) which listens to the Keycloak events (User Role Assignment, Role to Role Assignment, etc), parses this event into an OpenFGA tuple based on the [Keycloak Authz Schema](openfga/keycloak-authorization-model.json) and publishes the event to Kakfa Cluster (C)
19+
20+
* Kakfa OpenFGA Consumer (D) that using the OpenFGA SDK will publish the tuples to the OpenFGA Solution
21+
22+
* OpenFGA (E) is responsible for applying fine-grained access control. The OpenFGA service answers authorization checks by determining whether a relationship exists between an object and a user
23+
24+
* Other components
25+
26+
* Store Web Application is integrated with Keycloak by OpenID Connect
27+
28+
* Store API is protected by OAuth 2.0 and it utilizes the OpenFGA SDK for FGA
29+
30+
# How to install?
31+
## Prerequisites
32+
33+
* Install Git, [Docker](https://www.docker.com/get-docker) and [Docker Compose](https://docs.docker.com/compose/install/#install-compose) in order to run the steps provided in the next section<br>
34+
35+
## Deploy the PoC
36+
37+
1. Clone this repository
38+
````bash
39+
git clone https://github.com/embesozzi/keycloak-openfga-workshop
40+
cd keycloak-openfga-workshop
41+
````
42+
43+
2. Execute following Docker Compose command to start the deployment
44+
45+
```sh
46+
docker-compose -f ./kafka-integration/docker-compose-kafka.yml -f docker-compose-openfga.yml -f ../docker-compose-apps.yml up
47+
```
48+
49+
3. To be able to use this environment, you need to add this line to your local HOSTS file:
50+
51+
```sh
52+
127.0.0.1 keycloak openfga store store-api
53+
```
54+
55+
4. Access the following web UIs using URLs bellow via a web browser.
56+
57+
Described [here](../README.md)
58+
59+
## Post configuration steps
60+
61+
### OpenFGA
62+
1. Import the [OpenFGA authorization schema for Keycloak](openfga/keycloak-authorization-model.json):
63+
```bash
64+
cd openfga
65+
./import.sh
66+
```
67+
2. As the result you will see the following OpenFGA Authorization Model in the [OpenFGA Playground Console](http://localhost:8080/playground) :
68+
69+
![openfga-keycloak-authorization-model](../doc/images/openfga-authz-model.png)
70+
71+
### Keycloak
72+
1. Enable the Keycloak OpenFGA Event Listener extension in Keycloak:
73+
74+
* Open [administration console](http://keycloak:8081)
75+
* Choose realm
76+
* Realm settings
77+
* Select `Events` tab and add `openfga-events-kafka` to Event Listeners.
78+
79+
2. Proceed to initialize the PoC:
80+
81+
Follow the steps of the README.

0 commit comments

Comments
 (0)