Skip to main content

Reusable Django app for SSO integration

Project description

Django-Keycloak Integration

Main Settings Configuration

KEYCLOAK_SERVER_URL = config('KEYCLOAK_SERVER_URL', default="http://localhost:8080", cast=str)
KEYCLOAK_ISSUER_PREFIX = config('KEYCLOAK_ISSUER_PREFIX', default="http://localhost:8080", cast=str)
KEYCLOAK_REALM = config('KEYCLOAK_REALM', cast=str)
KEYCLOAK_CLIENT_ID = config('KEYCLOAK_CLIENT_ID', cast=str)
KEYCLOAK_CLIENT_PK = config('KEYCLOAK_CLIENT_PK', cast=str)  # for when we need to read client roles
KEYCLOAK_CLIENT_TITLE = config('KEYCLOAK_CLIENT_TITLE', cast=str)
KEYCLOAK_CLIENT_NAME = config('KEYCLOAK_CLIENT_NAME', cast=str)
KEYCLOAK_CLIENT_SECRET = config('KEYCLOAK_CLIENT_SECRET', cast=str)
KEYCLOAK_ALGORITHMS = config('KEYCLOAK_ALGORITHMS', cast=str, default='RS256')
KEYCLOAK_OAUTH_REDIRECT_URI = config(
    'KEYCLOAK_OAUTH_REDIRECT_URI',
    default="http://127.0.0.1:8000/auth/callback/",
    cast=str
)
KEYCLOAK_DEFAULT_ADMIN_PANEL_PERMISSION_CLASSES = [
    'django_keycloak_sso.permissions.IsAuthenticatedAccess'
] # default permission to access keycloak admin data endpoints

ADMIN_GROUPS = config('ADMIN_GROUPS', cast=list) # for when using from GroupAccess

examples:

KEYCLOAK_SERVER_URL=https://sso.domain # if using in dokcer : https://<keycloak_container>:8443
KEYCLOAK_ISSUER_PREFIX=https://sso.domain
KEYCLOAK_REALM=main
KEYCLOAK_CLIENT_ID=ecommerce-back
KEYCLOAK_CLIENT_PK=<client_primary_key>
KEYCLOAK_CLIENT_SECRET=<client_secret_key>
KEYCLOAK_OAUTH_REDIRECT_URI=http://127.0.0.1:8000/auth/callback/ # for login in ssr sites
KEYCLOAK_CLIENT_NAME=ecommerce
KEYCLOAK_CLIENT_TITLE=ecommerce-back
KEYCLOAK_ALGORITHMS=RS256
ADMIN_GROUPS=ecommerce-back-admin,ecommerce-back-admin2

Authentication and Middlewares class usage

Mock default django or DRF authentication proccess

you can access to user in views like :

user = request.user
user.id
user.username
  • KeycloakAuthentication

  • KeycloakMiddleware

    REST_FRAMEWORK = {
        'DEFAULT_AUTHENTICATION_CLASSES': [
            'django_keycloak_sso.middlewares.KeycloakAuthentication',
        ]
    } # Usable for DRF services
    
    MIDDLEWARE = [
        'django_keycloak_sso.middlewares.KeycloakMiddleware',
    ] # Usable for SSR websites
    

note : KeycloakAuthentication is enough for DRF backend services

User Attrs and Properties

request.user is a instance of CustomUser class with these properties :

  • groups

  • groups_dict_list

  • groups_parent

  • group_roles

  • realm_roles

  • client_roles

  • roles

  • id

  • username

  • first_name

  • last_name

  • full_name

  • groups_id

  • All other keycloak user properties ...


Permission Decorators

These decorators provide fine-grained access control for your views using Keycloak user attributes. They can be used on Django views, DRF APIViews, or function-based views. All decorators rely on the check_user_permission_access function internally.

All decorators are stackable — combine multiple for more specific permission control.

Available Decorators

from django_keycloak_sso.decorators import (
    require_roles,
    require_any_role,
    require_groups,
    require_any_group,
    require_group_roles,
    require_all_permissions
)

@require_roles(*role_titles)

Checks if the user has all of the specified realm or client roles.

@require_roles('superuser', 'admin')
def view_func(request):
    pass

@require_any_role(*role_titles)

Checks whether the user has one of the specified realm or client roles.

@require_any_roles('superuser', 'admin')
def view_func(request):
    pass

@require_groups(*group_titles)

Checks if the user is a member of all specified group names.

@require_groups('group_1','group_2')
def view_func(request):
    pass

@require_any_group(*group_titles)

Checks whether the user is a member of one of the specified group names.

@require_any_group('group_1','group_2')
def view_func(request):
    pass

@require_group_roles(*group_roles, match_group_roles=False)

Checks if the user has at least one of the specified roles within any group. Use match_group_roles=True to only allow matches where the group name is also explicitly listed via @require_groups.

@require_group_roles('manager')  # Any group
@require_group_roles('admin', match_group_roles=True)  # Must match both group and role
def view_func(request):
    pass

@require_all_permissions(role_titles=[], group_titles=[], group_roles=[], match_group_roles=False)

Combined decorator that allows you to check all types of permissions in one call.

@require_all_permissions(
    role_titles=['superuser'],
    group_titles=['group_1'],
    group_roles=['manager'],
    match_group_roles=True
)
def view_func(request):
    pass

GroupAccess()

When the desired group is dynamic, you can check whether the user belongs to one or all of the groups by passing the request.user and group_names values to the methods of this class.

from django_keycloak_sso.permissions import GroupAccess
class TestAPIView(APIView):
    access = GroupAccess()

    def get(self,request):
        object_id = self.object.id
        self.access.require_all_groups(request.user,[f'group_1_{object_id}','group_2_{object_id}'])

        ...

    def post(self,request)
        object_id = self.object.id
        self.access.require_any_groups(request.user,[f'group_1_{object_id}','group_2_{object_id}'])

        ...

Stacking Decorators

You can combine decorators for more control:

@require_roles('superuser')
@require_groups('group_1')
@require_group_roles('manager')
def view_func(request):
    pass
All decorators raise PermissionDenied (403) if access checks fail.
Make sure your view expects an authenticated user (request.user.is_authenticated is checked internally).

Permission Classes

  • IsManagerAccess

  • IsSuperUserAccess

  • IsSuperUserOrManagerAccess

Usage Examples

class TestView(APIView):
    http_method_names = ('get',)
    permission_classes = (IsManagerAccess,) 

Predefined Model, Meta Class, Fields

  • SSOModelMeta

  • CustomMetaSSOModelSerializer

  • SSOGroupField

  • SSOUserField

  • SSOManyUserField

  • SSOManyGroupField

  • SSOManyField

Usage Examples

from django_keycloak_sso.sso import fields as sso_fields
from django_keycloak_sso.sso.meta import CustomMetaSSOModelSerializer, SSOModelMeta 


class Server(Model, metaclass=SSOModelMeta):
    user = sso_fields.SSOUserField(verbose_name=_("User"))
    group_id = sso_fields.SSOGroupField(verbose_name=_("Group"))
    delivery_users = sso_fields.SSOManyUserField(verbose_name=_("Delivery Users")) # New Many-to-Many Field

class ServerSerializer(CustomMetaSSOModelSerializer):
  # is limited : get ids or full data of assigned instances
  delivery_users = SSOManyField(source='delivery_users', field_type='user', is_limited=True) # New Many-to-Many Serializer Field
    class Meta:
        model = Server
        fields = (...)    

Benefits of using SSOModelMeta and SSO Fields:

  • access to sso field data

    mode_obj.fieldname_data # mock django relation fields behavior
    my_server.user_data # get a dict of user datas
    my_server.user_data.username # get a key from sso field data
    my_server.delivery_users.get_ids # get ids of saved m2m instances
    my_server.delivery_users.get_full_data # get full datas of saved m2m instances
    my_server.delivery_users.add # add an instance to m2m field
    my_server.delivery_users.remove # remove an instance to m2m field
    
    # NOTE : Do same with group fields
    
  • auto validation field object exists in keycloak

    when using CustomMetaSSOModelSerializer in a serializer and wants to create a instance with that serializer. it will automatically validate existence of data in keycloak and if not return proportionate error.


Define Endpoints

urlpatterns = [
    path('accounts/', include('django_keycloak_sso.urls')),
]
Endpoints List
  • /v1/auth/login/

  • /v1/auth/refresh/

  • /v1/auth/logout/

  • /v1/sso/profile/

  • /v1/sso/groups/

  • /v1/sso/groups/<group_id>/

  • /v1/sso/users/

  • /v1/sso/users/<user_id>/

Note : for more information about how to use them, check created swagger for your project


Usefull Utilities

get_serializer_field_data

    from django_keycloak_sso.sso.sso import SSOKlass

    sso_klass = SSOKlass()
    
    class TestSerializer(ModelSerializer):
        user_data = SerializerMethodField()
    
       def __init__(self, *args, **kwargs):
            super().__init__(*args, **kwargs)
            queryset = args[0] if len(args) >= 1 else None
            self.user_list_data = list()
        
            if queryset and (isinstance(queryset, QuerySet) or isinstance(queryset, list)):
                self.user_list_data = sso_klass.get_sso_data_list(
                    queryset,
                    'user', # field name defined in model
                    sso_klass.SSOFieldTypeChoices.USER
                )

        def get_user_data(self, obj) -> dict | None:
            return sso_klass.get_serializer_field_data(
                field_name='user', # field name defined in model
                field_type=sso_klass.SSOFieldTypeChoices.USER, # Or GROUP
                obj_=obj,
                list_data=self.user_list_data, # Optional for optimize list data caching
                get_from_list=True # True if you want use <list_data>
            )

send_request

integration with keycloak

keycloak_klass = KeyCloakConfidentialClient()
users_data = keycloak_klass.send_request(
            self.keycloak_klass.KeyCloakRequestTypeChoices.USER_ROLES, # has many options to integrate with keycloak
            self.keycloak_klass.KeyCloakRequestTypeChoices,
            self.keycloak_klass.KeyCloakRequestMethodChoices.GET,
            self.keycloak_klass.KeyCloakPanelTypeChoices.ADMIN,
            detail_pk='1234', # Additional data args
            extra_headers={} #Additional request headers
        )

Advanced Usage

For get more facilities and features go deep on these classes :

  • SSOKlass

  • SSOCacheControlKlass

  • KeyCloakBaseManager

  • KeyCloakConfidentialClient

  • KeyCloakInitializer

  • BaseKeycloakAdminView

  • CustomSSORelatedField

Note: To get most caching performance use REDIS as cache system (especially HiRedis)


Project details


Download files

Download the file for your platform. If you're not sure which to choose, learn more about installing packages.

Source Distribution

django_keycloak_sso-0.4.1.tar.gz (71.4 kB view details)

Uploaded Source

Built Distribution

If you're not sure about the file name format, learn more about wheel file names.

django_keycloak_sso-0.4.1-py3-none-any.whl (64.9 kB view details)

Uploaded Python 3

File details

Details for the file django_keycloak_sso-0.4.1.tar.gz.

File metadata

  • Download URL: django_keycloak_sso-0.4.1.tar.gz
  • Upload date:
  • Size: 71.4 kB
  • Tags: Source
  • Uploaded using Trusted Publishing? No
  • Uploaded via: twine/6.2.0 CPython/3.12.2

File hashes

Hashes for django_keycloak_sso-0.4.1.tar.gz
Algorithm Hash digest
SHA256 93af1e97eff441a357c98ca762d77503e0219106368e1542a80c80dee4bd8aa6
MD5 4cd56785b29919eefc8ff8efb6ed8411
BLAKE2b-256 9bf139f4048f03aa425af02a0ad8addc6681e69c1b72fb422476ee273d01d59b

See more details on using hashes here.

File details

Details for the file django_keycloak_sso-0.4.1-py3-none-any.whl.

File metadata

File hashes

Hashes for django_keycloak_sso-0.4.1-py3-none-any.whl
Algorithm Hash digest
SHA256 6cb85dbb0b8719dfeb7f10bf2f8008cbcfc5dbaae34eb05f80e72ac220cd34f0
MD5 cc6628a7ae84b99d8c45c0274ba726bd
BLAKE2b-256 0f127a12fd7f8e6d1755ae4b94a3d7b458e508f1b03f3df1e7f24d6dc1bd011c

See more details on using hashes here.

Supported by

AWS Cloud computing and Security Sponsor Datadog Monitoring Depot Continuous Integration Fastly CDN Google Download Analytics Pingdom Monitoring Sentry Error logging StatusPage Status page