Skip to content

Commit

Permalink
feat: Add an example of access control at the API Gateway level with …
Browse files Browse the repository at this point in the history
…APISIX and the plugin authz-openfga, based on the article Mastering Access Control: Implementing Low-Code Authorization Based on ReBAC and the Decoupling Pattern.
  • Loading branch information
embesozzi committed Jun 30, 2024
1 parent 5e38783 commit 769e67a
Show file tree
Hide file tree
Showing 17 changed files with 2,257 additions and 6 deletions.
4 changes: 4 additions & 0 deletions .env
Original file line number Diff line number Diff line change
@@ -1,2 +1,6 @@
KC_VERSION=25.0.1
OPENFGA_VERSION=v1.5.5

AM_INTERNAL_URL=http://keycloak:8080
OPENFGA_HOST=http://openfga:8080
STORE_API=host.docker.internal:8091
13 changes: 10 additions & 3 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@ In this new version of the PoC we have a direct integration between the Access M

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.

In the latest version, I aimed to continue improving the authorization architecture and provide an agnostic approach for exposing and protecting APIs following best practices such as Policy as Code (PaC), decoupling authorization, and low-code authorization. Therefore, I added an identity-aware API gateway, Apache APISIX, as an API sidecar to the authorization architecture to enforce authorization and decouple it from the backend. The gateway uses a plugin I developed called [authz-openfga](https://github.com/embesozzi/apisix-authz-openfga) that supports Relationship-Based Access Control (ReBAC) policies because it's integrated with OpenFGA platform. The details are explained in the following article [Mastering Access Control: Implementing Low-Code Authorization Based on ReBAC and Decoupling Pattern](https://medium.com/@embesozzi/mastering-access-control-implementing-low-code-authorization-based-on-rebac-and-decoupling-pattern-f6f54f70115e)

## Authorization Framework (New)

Expand All @@ -23,7 +24,10 @@ The following diagram illustrates the solution architecture of this workshop:
* 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.
* Other components
* Store Web Application is integrated with Keycloak by OpenID Connect
* Store API is protected by OAuth 2.0 and it utilizes the OpenFGA SDK for FGA
* Store Authorization Gateway exposes and protects the Store API with ReBAC policies integrated with the OpenFGA Platform (Protection at API Gateway).

You can also have Store OpenFGA API is protected by OAuth 2.0 and it utilizes the OpenFGA SDK for FGA as example (Protection at API Level).


Another cool feature of custom extension is its capability to discover the OpenFGA authorization model and determine which events are handled. This gives you the flexibility to choose your authorization model, whether it’s RBAC, GBAC, or both 🙌.

Expand All @@ -49,7 +53,7 @@ Another cool feature of custom extension is its capability to discover the OpenF
3. To be able to use this environment, you need to add this line to your local HOSTS file:

```sh
127.0.0.1 keycloak openfga store store-api
127.0.0.1 keycloak openfga store store-openfga-api store-authz-gateway
```

4. Access the following web UIs using URLs bellow via a web browser.
Expand All @@ -59,7 +63,8 @@ Another cool feature of custom extension is its capability to discover the OpenF
| Keycloak Console | http://keycloak:8081 | admin / password | quay.io/keycloak/keycloak:25.0.1 |
| OpenFGA Playground | http://localhost:3000/playground | | openfga/openfga:v1.5.5 |
| Store Portal | http://store:9090 | | Custom image |
| Store API | http://store-api:9091 | | Custom image |
| Store Authorization Gateway | http://store-authz-gateway:9080 | | Custom image based Apache APISIX Gateway |
| Store OpenFGA API | http://store-openfga:9091 | | Custom image |



Expand Down Expand Up @@ -108,6 +113,8 @@ As an example, we will implement an Product Catalog web application that has the

You can follow the test cases described in the [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).

And the article [Mastering Access Control: Implementing Low-Code Authorization Based on ReBAC and Decoupling Pattern](https://medium.com/@embesozzi/mastering-access-control-implementing-low-code-authorization-based-on-rebac-and-decoupling-pattern-f6f54f70115e).

Nevertheless, the use cases are detailed below:

### Use case 1: Access to the Store for managing products as an Analyst (Paula)
Expand Down
32 changes: 29 additions & 3 deletions docker-compose-apps.yml
Original file line number Diff line number Diff line change
Expand Up @@ -9,13 +9,14 @@ services:
- "9090:8080"
environment:
VUE_APP_OIDC_PROVIDER_DOMAIN: http://keycloak:8081/realms/master
VUE_APP_API_URL: http://store-api:9091/api/products
# VUE_APP_API_URL: http://store-openfga-api:9091/api/products
VUE_APP_API_URL: http://store-authz-gateway:9080/api/products
VUE_APP_CLIENT_ID: portal
depends_on:
keycloak:
condition: service_healthy

store-api:
store-openfga-api:
build: ./store-openfga-api
image: twogenidentity/store-openfga-api
container_name: store-openfga-api
Expand All @@ -25,4 +26,29 @@ services:
ports:
- "9091:9091"
environment:
OIDC_PROVIDER_DOMAIN: http://keycloak:8081/realms/master
OIDC_PROVIDER_DOMAIN: http://keycloak:8081/realms/master

store-authz-gateway:
image: ghcr.io/embesozzi/apisix-authz-openfga:latest
container_name: store-authz-gateway
depends_on:
keycloak:
condition: service_healthy
volumes:
- $PWD/store-authz-gateway/routes.yml:/usr/local/apisix/conf/apisix.yaml:ro
- $PWD/store-authz-gateway/config.yml:/usr/local/apisix/conf/config.yaml:ro
environment:
KEYCLOAK_URL: ${AM_INTERNAL_URL}
KEYCLOAK_CLIENT_SECRET: jnxDqhu0GTaCCWuKxodUnSdKzEIBquKT
SESSION_SECRET: nFpaoPPxHGHJLFlE12qx8P5TGgJBDYdS
FGA_HOST: ${OPENFGA_HOST}
STORE_API: ${STORE_API}
ports:
- "9080:9080"

store-api:
build: ./store-api
image: ghcr.io/embesozzi/demo-store-api:1.0.0
container_name: store-api
# ports:
# - "7091:7091"
1 change: 1 addition & 0 deletions keycloak/import.sh
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@ echo "Creating PoC Users, Role Model, User Role Assigments and Clients"

# Clients
/opt/keycloak/bin/kcadm.sh create clients -r master -s clientId=portal -s publicClient=true -s 'redirectUris=["http://store:9090/callback"]' -s 'webOrigins=["http://store:9090"]' -s 'attributes={ "post.logout.redirect.uris": "http://store:9090/home?action=logout", "access.token.lifespan": 3600}' -o
/opt/keycloak/bin/kcadm.sh create clients -r master -s clientId=apisix -s 'redirectUris=["http://localhost:9980/callback"]' -s 'secret=jnxDqhu0GTaCCWuKxodUnSdKzEIBquKT' -o

# Users
/opt/keycloak/bin/kcadm.sh create users -r master -s username=paula -s firstName=Paula -s lastName=Von -s enabled=true -s [email protected]
Expand Down
2 changes: 2 additions & 0 deletions store-api/.gitignore
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
node_modules
.DS_Store
10 changes: 10 additions & 0 deletions store-api/Dockerfile
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
FROM node:16.0.0
# FROM --platform=linux/amd64 node:16.0.0
LABEL maintainer="[email protected]"

WORKDIR /app
COPY package*.json ./
RUN npm install
COPY . .

CMD [ "npm", "start"]
118 changes: 118 additions & 0 deletions store-api/openapi.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,118 @@
openapi: 3.0.1
info:
title: Products API
description: 'API Products (secured using OAuth) protected by ReBAC (Zanzibar)'
version: 1.0.0
servers:
- url: http://localhost:9980/api/products
tags:
- name: Product
description: Operations about identity and access platform
paths:
/products:
get:
tags:
- Product
summary: Get products
security: []
x-apisix-plugins:
authz-rebac:
object_type: role
object: products-view
responses:
401:
$ref: '#/components/responses/UnauthorizedError'
200:
description: successful operation
content:
application/json:
schema:
type: array
items:
$ref: '#/components/schemas/Product'
/product:
post:
tags:
- Product
summary: "Add a new product"
security: []
x-apisix-plugins:
authz-rebac:
object_type: role
object: products-editor
requestBody:
description: Information about a new user in the system
content:
application/json:
schema:
$ref: "#/components/schemas/Product"
responses:
"405":
description: "Invalid input"
"201":
description: Created
put:
tags:
- Product
summary: Update an existing product
description: Update an existing product by Id
x-apisix-plugins:
authz-rebac:
object_type: role
object: products-editor
requestBody:
description: Update an existent product in the store
content:
application/json:
schema:
$ref: '#/components/schemas/Product'
required: true
responses:
'200':
description: Successful operation
content:
application/json:
schema:
$ref: '#/components/schemas/Product'
'400':
description: Invalid ID supplied
'404':
description: Product not found
'/product/{product_id}':
delete:
tags:
- Product
summary: "Delete a product"
x-apisix-plugins:
authz-rebac:
object_type: role
object: products-editor
responses:
"200":
description: Created
parameters:
- schema:
type: integer
name: product_id
in: path
required: true
components:
responses:
UnauthorizedError:
description: Access token is missing or invalid
schemas:
Product:
type: object
properties:
id:
type: integer
format: int64
name:
type: string
xml:
name: User
securitySchemes:
bearerOAuth:
type: http
scheme: bearer
bearerFormat: JWT
Loading

0 comments on commit 769e67a

Please sign in to comment.