---
title: "Using Cerbos with Keycloak for Identity/AuthN"
description: "In this tutorial, we'll use Keycloak for authentication and then implement Cerbos for fine-grained access control, all within a Django web application, though the same principle will work for any application. As a result we will ensure that only authenticated users can access certain parts of the application, and their actions are authorized with precision."
author: "Omu Inetimi"
date: "2024-05-16T13:00:00.000Z"
canonical: "https://www.cerbos.dev/blog/using-cerbos-with-keycloak-for-identity-auth-n"
image: "https://stylish-appliance-1c1cc1c30d.media.strapiapp.com/cover_image_dd65376180.jpg"
tags: ["guide","integration"]
source: "https://www.cerbos.dev/blog/using-cerbos-with-keycloak-for-identity-auth-n"
---

# Using Cerbos with Keycloak for Identity/AuthN

In this tutorial, we'll use Keycloak for authentication and then implement Cerbos for fine-grained access control, all within a Django web application, though the same principle will work for any application. As a result we will ensure that only authenticated users can access certain parts of the application, and their actions are authorized with precision.

Prerequisites

Before beginning the integration, you will need:

* A working Django web application.
* A Keycloak server.
* Docker installed in your development environment.

# Setting Up Keycloak

Let's start by installing KeyCloak using Docker. Type the following into your terminal to start Keycloak

```bash
docker run -p 8080:8080 -e KEYCLOAK_ADMIN=admin -e KEYCLOAK_ADMIN_PASSWORD=admin quay.io/keycloak/keycloak:24.0.1 start-dev

```

After running the command, open [http://localhost:8080](http://localhost:8080/) in your browser. You should see the Keycloak admin page. Login with the credentials you used in the Docker command (it should be `admin` for username and password).

![image](https://stylish-appliance-1c1cc1c30d.media.strapiapp.com/image_3_93dd5cbf8d.png)

Once logged in, we can now create a realm from the admin dashboard.

You’ll notice you're in the master realm; let's create a custom realm for our project. click on “**Add realm”** to create a new realm. Name the realm to match your Django project.

![image](https://stylish-appliance-1c1cc1c30d.media.strapiapp.com/image_5_c94fd321ba.png)

## Create a client

1. In your project realm, go to the “clients” section and click Create.
2. In the general settings, you’ll see a few fields, the `clientID` field being required.
3. Choose a client ID that represents your Django project. This ID is what Keycloak will use to authenticate your Django application.
4. In the capability config, turn on client authentication - this ensures that client credentials are required for authentication.
5. And finally in login settings, configure the Root URL to point to your Django application - usually `http://localhost:8000`.
6. Specify “Valid Redirect URIs” to include paths Keycloak will use for authentication callbacks, typically your Django login and post-login redirect URIs.

## Define User Roles

Navigate to the “roles” tab still within the clients section and click “create role.” Here you can define the necessary roles relevant to your project structure, but for the purpose of this article, we’ll keep it simple and define a few roles - admin, user and manager. Keycloak will assign these roles to users and manage them.

You can now create users by moving to the users section and adding new users. For each user, you can set up credentials and assign them roles under the “Role Mappings” tab assigning them the roles you created.

## Tying KeyCloak with Django

Now let's connect our KeyCloak instance to our Django application.

We’ll be using django-allauth which is a Django package that simplifies the entire process of authentication and then we can register Keycloak within it as an option for authN

* Install django all-auth via pip

` $ pip install django-allauth`

* Then go ahead and start a Django project.
* After setting up a Django project, we will configure it to work with Keycloak in the settings.py file. Add allauth and its related apps to `INSTALLED_APPS`:

```python
INSTALLED_APPS = [
   # ...
   'allauth',
   'allauth.account',
   'allauth.socialaccount',
   'allauth.socialaccount.providers.openid_connect',
   # ...
]

```

* Set authentication backends:

```python
AUTHENTICATION_BACKENDS = [
   # ...
   'allauth.account.auth_backends.AuthenticationBackend',
   # ...
]

```

* Update the `SOCIALACCOUNT_PROVIDERS` setting in settings.py:

  

```python
SOCIALACCOUNT_PROVIDERS = {
   "openid_connect": {
       "APPS": [
           {
               "provider_id": "keycloak",
               "name": "Keycloak",
               "client_id": "<insert-keycloak-client-id>",
               "secret": "<insert-keycloak-client-secret>",
               "settings": {
                   "server_url": "http://keycloak:8080/realms/<your-realm>/.well-known/openid-configuration",
               },
           }
       ]
   }
}

```

Replace `<insert-keycloak-client-id>` and `<insert-keycloak-client-secret>` with your actual Keycloak client ID and secret (your client ID was the name you put as your KeyCloak ID, and the secret can be found within the credentials tab in the clients section)

Ensure the `server_url` points to your KeyCloak server's OpenID Connect configuration endpoint, usually structured like this: `http://keycloak:8080/realms/<your-realm>/.well-known/openid-configuration`

Replace `<your-realm>` with your realm name.

* Add all-auth to middleware:

`'allauth.account.middleware.AccountMiddleware',`

* Finally, update your urls.py file by including django-allauth.

```python
from django.contrib import admin
from django.urls import path, include
urlpatterns = [
   path('admin/', admin.site.urls),
   path('accounts/', include('allauth.urls')),
   # Include other app URLs as needed
]

```

This ensures django-allauth handles all authentication routing.

* You can now start the Django development server to test if everything is working.

`$ python manage.py runserver`

Navigate to <http://localhost:8000/accounts/login/> in your browser to start the KeyCloak login flow. You’ll see the Django auth page, and under “other” means of login, click on “Login with Keycloak”. You will be redirected to your Keycloak server, where you can authenticate using your credentials. Upon successful login, you will be redirected to <http://127.0.0.1:8000/accounts/profile/> by default. This confirms that your login was successful.

After following these steps, you have successfully created the authentication for your Django application. Keycloak will allow you to keep track of who is allowed to access your application and what roles they have. Now we can tie this in with Cerbos to manage what these defined roles are allowed to do/access within your Django application.

<center>

[![BlogCTA - PDP](https://no-cache.hubspot.com/cta/default/20289770/interactive-168994186087.png)](https://cta-service-cms2.hubspot.com/web-interactives/public/v1/track/redirect?encryptedPayload=AVxigLKntt8fdQvjLtfXzPmwXqWXdClso1u%2FMwtyDq8%2Fjwp8XQC5MIuRht4cOw6DsxFsvcPCiFNbn57jrpHSb0a2cdTgR6BSX1w4YPJ42R0PvmaKh79f4FlDHlYmC6lMtFt7p8U4I17YrAI3dtqTAus%2BEHAApNn1h76t%2Bwyl2sc%3D&webInteractiveContentId=168994186087&portalId=20289770) 

</center>

# Integrating Cerbos for Authorization

## Set up a Cerbos policy repository

We’ll start by creating the Cerbos policy repository. This is where all policies will be defined for Cerbos to reference and then make authorization decisions.

Within your Django project's root directory, create a folder named ‘**cerbos**’, and within the cerbos folder, create a subfolder named ‘**policies**’.

Now, within the policies folder, we’ll create a YAML file to define the rules for authorization checks.

Still following our admin, user and manager role structure, let's create a YAML file that defines what these roles are allowed to do with ‘document’ resources. These roles are authorized as follows:

![image](https://stylish-appliance-1c1cc1c30d.media.strapiapp.com/image_20_0e1e3784de.jpg)

So our `documents.yaml` file would look like this:

```yaml
apiVersion: api.cerbos.dev/v1
resourcePolicy:
  version: default
  resource: document  # Applies to 'document' resources.

  rules:
    # Admins can view, edit, and delete any document.
    - actions: ['view', 'edit', 'delete']
      effect: EFFECT_ALLOW
      roles: ['admin']

    # Users can view and edit their own documents.
    - actions: ['view', 'edit']
      effect: EFFECT_ALLOW
      roles: ['user']
      condition:  # Condition ensures the user is the document's author.
        match:
          expr: request.resource.attr.author == request.principal.id


 # managers can view, edit and approve documents.
    - actions: ['view', 'edit',’approve’]
      effect: EFFECT_ALLOW
      roles: ['manager']

```

This policy allows admins to view, edit and delete documents. Users are only allowed to view and edit documents they authored (where the ‘**author**’ attribute matches the user id). Managers are allowed to view, edit and approve documents.

Let's also write a principal policy to help manage permissions in a more granular manner, allowing for configurations based on the user.

There can be multiple managers across various departments so we want to restrict approval permissions to only a single manager within their corresponding departments, for example - marketing department.

For this principal policy the manager of the marketing department is identified with the principal ID, “`john_doe`”.

We also want managers to only approve documents that have the status “`pending_approval`”.

  

![image](https://stylish-appliance-1c1cc1c30d.media.strapiapp.com/image_24_948e5daa01.jpg)

Create a `principal.yaml` file and add:

```yaml
apiVersion: api.cerbos.dev/v1
principalPolicy:
  version: default
  principal: "john_doe" 
  rules:
    - resource: document
      actions:
        - action: "approve"
          effect: EFFECT_ALLOW
          condition:
            match:
              all: 
                Of:
                  - expr: request.resource.attr.department == 'Marketing' 
                 
                  - expr: request.resource.attr.status == 'pending_approval'

```

This policy ensures that only one user from the department (the manager), `john_doe`, can approve documents, and only if those documents are from the marketing department and marked as "`pending_approval`".

## Set up the Cerbos PDP

The Cerbos policy decision point(PDP) is a standalone service responsible for evaluating requests against the defined authorization policies.

Still, within the cerbos directory, create a file named `conf.yaml` to specify the HTTP port Cerbos will listen for and the location of your policy repository:

```yaml
server:
  httpListenAddr: ":3592"
storage:
  driver: "disk"
  disk:
    directory: /policies
```

We’ll use Docker to start the Cerbos PDP in a container and run it locally:

```bash
docker run --rm --name cerbos -t \
  -v $(pwd)/Cerbos/policies:/policies \
  -v $(pwd)/Cerbos:/config \
  -p 3592:3592 \
  ghcr.io/cerbos/cerbos:latest server --config=/config/conf.yaml
```

### Set up the Cerbos client

We need to be able to interact with the Cerbos PDP from our Django application. Let's do that by installing the Cerbos Python client and configuring the client instance:

`$ pip install cerbos`

Within our root directory, we create a file called cerbos\_client.py containing the utility function we will use to communicate with the Cerbos PDP. In this file we set the Cerbos host to point to the instance we are running locally in Docker:

```python
from cerbos.sdk.client import CerbosClient
cerbos_host = "localhost:3592"
cerbos_client = CerbosClient(host=cerbos_host)
```

You can now create views within your Django apps that will perform authorization checks using the Cerbos client before executing sensitive operations on resources. We’ll be integrating the Cerbos client within our views to check permissions before allowing access to specific functionalities.

### Extracting roles from Keycloak 

We need to be able to retrieve our defined roles from Keycloak within our Django application before passing them to Cerbos for authorization checks. In a new file, we call `utils.py` Here’s a way to go about that:

```python
from jose import jwt
from django.conf import settings

def extract_roles_from_jwt(request):
    """Extract roles from JWT token."""
    auth_header = request.headers.get('Authorization')
    if not auth_header:
        return []

    token = auth_header.split()[1]
    try:
        # Decode JWT token. Use Keycloak's public key here, replacing 'your-public-key'
        decoded_token = jwt.decode(token, 'your-public-key', algorithms=['RS256'])
        # Extract roles. Adjust this based on how roles are stored in your JWT
        roles = decoded_token.get('realm_access', {}).get('roles', [])
        return roles
    except jwt.JWTError:
        return []

```

### Adding Cerbos to your views

Let's say we wanted to create a view function for the actions defined in our policies, ensuring the user is authenticated via Keycloak and then using Cerbos to enforce the permissions tied to the user's role. It would look something along these lines:

```python
from django.http import HttpResponseForbidden, HttpResponse
from .utils import extract_roles_from_jwt
from .cerbos_client import cerbos_client  # Ensure this is your configured Cerbos client instance
from cerbos.sdk.grpc.client import CerbosClient
from cerbos.engine.v1 import engine_pb2
from google.protobuf.struct_pb2 import Value

def manage_document(request, document_id, action):
    # Extract user roles from JWT
    user_roles = extract_roles_from_jwt(request)
    user = request.user

    # Setup the principal object for Cerbos
    principal = engine_pb2.Principal(
        id=user.username,
        roles=user_roles,
        attr={}  
    )

    # Setup the resource object for Cerbos
    resource = engine_pb2.Resource(
        id=document_id,
        kind="document",
        attr={
            "author": Value(string_value=user.username),
            "status": Value(string_value="pending_approval"),  # For 'approve' action condition
            "department": Value(string_value="Marketing")
        },
    )

    # Authorization check with Cerbos for the requested action
    if not cerbos_client.is_allowed(action, principal, resource):
        return HttpResponseForbidden(f"Not authorized to {action} this document.")

    # Handling each action explicitly
    if action == "view":
        # Logic for viewing the document could involve retrieving it from a database
        document_content = "Dummy content of the document."
        return HttpResponse(f"Viewing Document: {document_content}", status=200)

    elif action == "edit":
        # Logic for editing might involve showing an edit form or directly saving an edit
        return HttpResponse("Edit Document: Submit your edits.", status=200)

    elif action == "delete":
        # Logic for deleting might involve removing the document from the database
        return HttpResponse("Document deleted successfully.", status=200)

    elif action == "approve":
        # Logic for approving might involve changing the document's status and notifying the author
        return HttpResponse("Document approved successfully.", status=200)

    else:
        return HttpResponseForbidden("Invalid action requested.")

```

<center>

[![BlogCTA - Hub](https://no-cache.hubspot.com/cta/default/20289770/interactive-168996984756.png)](https://cta-service-cms2.hubspot.com/web-interactives/public/v1/track/redirect?encryptedPayload=AVxigLLcsDgffnFDNxbAukQV0SmAqw%2Bh1lAEElcTb3BQwSAIXmQ4WXfJgejBOSkzLZ7W7VbZmSrj7w25pmEjE1nho266aSJYw69kGnULNoeFa05RoQ7F7uWVzJwoPaT5KDyIEDJ3FnTp756bSD6DYLowFW3qMjOTWrrsWp4oc7yRzm%2Bvl5TdqcXJHVVu8Tlx&webInteractiveContentId=168996984756&portalId=20289770) 

</center>
In the `views.py` file of our Django application, we have a function called **manage\_document** that handles various actions like viewing, editing, deleting, and approving documents. The function starts by extracting user roles from the JWT provided by Keycloak to determine the user's permissions. It sets up a principal object representing the user and their attributes, and a resource object for the document being accessed, which includes details like the document's owner and status.

Using Cerbos, the function performs an authorization check to see if the user is allowed to carry out the requested action on the document. If they're not authorized, it immediately returns a forbidden response. Depending on the action, the function either displays the document, provides an editing interface, deletes the document, or updates its status as part of the approval process. It then sends a confirmation response for the action performed, ensuring that operations on documents are securely managed based on the user's roles.

# Conclusion

By setting up Keycloak, we established a secure authentication system, which, coupled with Cerbos' policy-driven authorization, provides a robust framework for managing user access and permissions. This integration ensures that only authenticated users can perform specific actions, enhancing the security and usability of Django applications. With these tools, developers can confidently build applications that protect sensitive operations and data, tailoring access to the needs and roles of different users.

Thanks for reading! If you enjoyed this blog, be sure to explore more integrations available in the [ecosystem on our website](https://www.cerbos.dev/features-benefits-and-use-cases/ecosystem). Don't forget to star(⭐️) [cerbos open-source](https://github.com/cerbos/cerbos) on GitHub – it really helps us. [Join our Slack Community](https://cerboscommunity.slack.com/ssb/redirect) for any questions. [Sign up for Cerbos Hub](https://hub.cerbos.cloud/) for free today.
