diff --git a/contracts/templates/contracts/includes/contract_list.html b/contracts/templates/contracts/includes/contract_list.html
index 8f766dab0c808f996273275295a7528fb7bc4266..5ed2646f853d3a4c2d60565fd0dd3334b23d1a79 100644
--- a/contracts/templates/contracts/includes/contract_list.html
+++ b/contracts/templates/contracts/includes/contract_list.html
@@ -1,4 +1,4 @@
-<ul class="grid grid-cols-2 gap-4 flex">
+<ul class="grid grid-cols-1 md:grid-cols-2 gap-4 flex">
     {% for contract in page %}
         <li class="card elevation-10{% if not contract.is_public %} bg-red-100{% endif %}">
             <div class="card__body p-5">
diff --git a/env.example b/env.example
index 564842df14cd9adec8176efda99a39b4e58a7a13..8331a25b7c7f165b932989ecbd670dc132292111 100644
--- a/env.example
+++ b/env.example
@@ -23,3 +23,5 @@ DEFAULT_STAFF_GROUPS="sso_cen:f,sso_cen:neverejni,sso_cen:smlouvy_ao,sso_cen:sml
 
 NASTENKA_API_URL=http://localhost:8009/contracts/api/notices
 NASTENKA_API_TOKEN=cirno
+
+CHOBOTNICE_API_URL="https://chobotnice.pirati.cz/graphql/"
diff --git a/oidc/auth.py b/oidc/auth.py
index eb5f1b9a1fa6e47918a864e95f3f87ac2da64471..3bda3185cf7a09abaafa2148d2d6200f72e4f736 100644
--- a/oidc/auth.py
+++ b/oidc/auth.py
@@ -1,9 +1,13 @@
 import logging
 import typing
 
+import gql
 import jwt
 from django.conf import settings
 from django.contrib.auth.models import Group
+from django_http_exceptions import HTTPExceptions
+from gql.transport.exceptions import TransportQueryError
+from gql.transport.requests import RequestsHTTPTransport
 from pirates.auth import PiratesOIDCAuthenticationBackend
 
 logging.basicConfig(level=logging.DEBUG)
@@ -11,16 +15,13 @@ logging.basicConfig(level=logging.DEBUG)
 
 class RegistryOIDCAuthenticationBackend(PiratesOIDCAuthenticationBackend):
     def _assign_new_user_groups(
-        self, user, access_token: dict, user_groups: typing.Union[None, list] = None
+        self, user, new_user_groups: list, existing_user_groups=None
     ) -> None:
-        if user_groups is None:
-            user_groups = user.groups.all()
+        if existing_user_groups is None:
+            existing_user_groups = user.groups.all()
 
-        for group in access_token["groups"]:
-            if group.startswith("_"):  # Ignore internal Keycloak groups
-                continue
-
-            group_name = f"sso_{group}"
+        for group in new_user_groups:
+            group_name = f"chobo_{group}"
 
             group = Group.objects.filter(name=group_name)
 
@@ -30,19 +31,62 @@ class RegistryOIDCAuthenticationBackend(PiratesOIDCAuthenticationBackend):
             else:
                 group = group[0]
 
-            if group not in user_groups:
+            if group not in existing_user_groups:
                 user.groups.add(group)
 
+        user.save()
+
     def _remove_old_user_groups(
-        self, user, access_token: dict, user_groups: typing.Union[None, list] = None
+        self, user, new_user_groups: list, existing_user_groups=None
     ) -> None:
-        if user_groups is None:
-            user_groups = user.groups.all()
+        if existing_user_groups is None:
+            existing_user_groups = user.groups.all()
 
-        for group in user_groups:
-            if group.name.replace("sso_", "") not in access_token["groups"]:
+        for group in existing_user_groups:
+            if group.name.replace("chobo_", "") not in new_user_groups:
                 user.groups.remove(group)
 
+    def get_chobotnice_groups(self, access_token):
+        transport = RequestsHTTPTransport(url=settings.CHOBOTNICE_API_URL)
+        client = gql.Client(
+            transport=transport,
+            fetch_schema_from_transport=True,
+        )
+
+        query = gql.gql(
+            f"""
+                {{
+                    allPeople(
+                        filters: {{keycloakId: {{exact: "{access_token['sub']}"}}}}
+                    ) {{
+                        edges {{
+                            node {{
+                                groupMemberships {{
+                                    group {{
+                                        shortcut
+                                    }}
+                                }}
+                            }}
+                        }}
+                    }}
+                }}
+            """
+        )
+
+        try:
+            result = client.execute(query)
+        except TransportQueryError:
+            # rv_gid was not found
+            raise HTTPExceptions.BAD_REQUEST
+
+        groups = []
+
+        for person in result["allPeople"]["edges"]:
+            for group_membership in person["node"]["groupMemberships"]:
+                groups.append(group_membership["group"]["shortcut"])
+
+        return groups
+
     def get_or_create_user(self, access_token, id_token, payload):
         user = super().get_or_create_user(access_token, id_token, payload)
 
@@ -54,13 +98,18 @@ class RegistryOIDCAuthenticationBackend(PiratesOIDCAuthenticationBackend):
         )
 
         user.preferred_username = decoded_access_token["preferred_username"]
-        user_groups = user.groups.all()
+        existing_user_groups = user.groups.all()
+        new_user_groups = self.get_chobotnice_groups(decoded_access_token)
 
         self._remove_old_user_groups(
-            user, decoded_access_token, user_groups=user_groups
+            user,
+            new_user_groups=new_user_groups,
+            existing_user_groups=existing_user_groups,
         )
         self._assign_new_user_groups(
-            user, decoded_access_token, user_groups=user_groups
+            user,
+            new_user_groups=new_user_groups,
+            existing_user_groups=existing_user_groups,
         )
 
         user.update_group_based_admin()
diff --git a/registry/settings/base.py b/registry/settings/base.py
index a21c828a30449877496e93a9fb213ca01aafed4a..083ea5daaec5c902ccb547a016d8c31cfc85f372 100644
--- a/registry/settings/base.py
+++ b/registry/settings/base.py
@@ -160,6 +160,9 @@ OIDC_OP_AUTHORIZATION_ENDPOINT = OIDC_RP_REALM_URL + "protocol/openid-connect/au
 OIDC_OP_TOKEN_ENDPOINT = OIDC_RP_REALM_URL + "protocol/openid-connect/token"
 OIDC_OP_USER_ENDPOINT = OIDC_RP_REALM_URL + "protocol/openid-connect/userinfo"
 
+# Chobotnice - group source
+CHOBOTNICE_API_URL = env.str("CHOBOTNICE_API_URL")
+
 
 ## Internationalization
 # https://docs.djangoproject.com/en/4.0/topics/i18n/
diff --git a/requirements/base.txt b/requirements/base.txt
index 3a13eb48f3660711f4672cfcd4b6e3a44d3de508..29a988d65c6d27eabce8979a9a693082586246b3 100644
--- a/requirements/base.txt
+++ b/requirements/base.txt
@@ -9,8 +9,9 @@ django-dbsettings==1.3.0
 django-downloadview==2.3.0
 django-nested-admin==4.0.2
 django-ordered-model==3.7.1
-psycopg2-binary==2.9.5
 django-webpack-loader==1.8.0
+gql[requests]==3.4.0
+psycopg2-binary==2.9.5
 pirates==0.6.0
 django-markdownx==4.0.0b1
 django-environ==0.9.0