401 Client Error: Unauthorized for url [mozilla-django-oidc – Keycloack]

Question:

I’m integrating keycloak authentication in my django app

After i am logged in into keycloak server i am facing error in the login callback
HTTPError at /oidc/callback/
401 Client Error: Unauthorized for url: http://keycloak:8080/realms/SquadStack/protocol/openid-connect/userinfo

here is my docker-compose.yml


services:
  db:
    image: postgres:13
    environment:
      - POSTGRES_DB=squadrun
      - POSTGRES_USER=postgres
      - POSTGRES_PASSWORD=123456
    volumes:
      - ~/.siq/pg_data:/var/lib/postgresql/data
    ports:
      - "5432:5432"
    networks:
      - local-keycloak
  redis:
    image: redis
    expose:
      - 6379
    networks:
      - local-keycloak
  celery:
    build:
      context: .
      dockerfile: Dockerfile
    command: celery --app=squadrun worker -Q voice_make_ivr_india_queue,voice_send_email_india_queue,voice_send_email_india_upstox_queue,voice_send_sms_india_queue,voice_workflow_india_queue,celery_voice,ivr_queue,voice_analytics_and_metrics_queue,voice_fsm_india_queue,dnd_queue,voice_bulk_sync,voice_dnd_and_compliance_actions_queue,voice_notifications_queue,voice_make_ivr_india_queue,voice_send_email_india_queue,voice_send_email_india_upstox_queue,voice_send_sms_india_queue,voice_sync_ivr_details_india_queue,voice_workflow_india_queue,voice_sync_sms_details_india_queue,voice_send_sms_india_upstox_queue,voice_dnd_and_compliance_actions_upstox_queue,voice_imports_queue --concurrency=3 --without-heartbeat --without-gossip -n celery.%%h --loglevel=INFO
    container_name: celery
    working_dir: /home/docker/code
    volumes:
      - .:/home/docker/code
    depends_on:
      - db
      - redis
    networks:
      - local-keycloak
  web:
    build:
      context: .
      dockerfile: Dockerfile
    command: python -Wignore manage.py runserver 0.0.0.0:8001
    container_name: django_web3
    volumes:
      - .:/home/docker/code
    ports:
      - "8001:8001"
    depends_on:
      - db
      - redis
    networks:
      - local-keycloak
  keycloak:
    image: quay.io/keycloak/keycloak:20.0.2
    command: start-dev
    container_name: keycloak
    ports:
      - "8080:8080"
    environment:
      - KEYCLOAK_ADMIN=admin
      - KEYCLOAK_ADMIN_PASSWORD=admin
    networks:
      - local-keycloak
networks:
  local-keycloak:
    driver: bridge

here is my setting.py

AUTHENTICATION_BACKENDS = [
    'apps.voice.voice_auth.auth.KeycloakOIDCAuthenticationBackend',
    'django.contrib.auth.backends.ModelBackend',
]

OIDC_RP_CLIENT_ID = "voice-dashboard"
OIDC_RP_CLIENT_SECRET = "rnX0eo0R43xnZficrZTkQQseyBip4V7t"
OIDC_RP_SIGN_ALGO = "RS256"
OIDC_OP_JWKS_ENDPOINT = "http://keycloak:8080/realms/SquadStack/protocol/openid-connect/certs"
OIDC_OP_AUTHORIZATION_ENDPOINT = "http://172.20.0.3:8080/realms/SquadStack/protocol/openid-connect/auth"
OIDC_OP_TOKEN_ENDPOINT = "http://keycloak:8080/realms/SquadStack/protocol/openid-connect/token"
OIDC_OP_USER_ENDPOINT = "http://keycloak:8080/realms/SquadStack/protocol/openid-connect/userinfo"
LOGIN_REDIRECT_URL = "/voice/dashboard/index/"
LOGOUT_REDIRECT_URL = "/voice/dashboard/index/"

here is auth.py


class KeycloakOIDCAuthenticationBackend(OIDCAuthenticationBackend):

    def create_user(self, claims):
        """ Overrides Authentication Backend so that Django users are
            created with the keycloak preferred_username.
            If nothing found matching the email, then try the username.
        """
        user = super(KeycloakOIDCAuthenticationBackend, self).create_user(claims)
        user.first_name = claims.get('given_name', '')
        user.last_name = claims.get('family_name', '')
        user.email = claims.get('email')
        user.username = claims.get('preferred_username')
        user.save()
        return user

    def filter_users_by_claims(self, claims):
        """ Return all users matching the specified email.
            If nothing found matching the email, then try the username
        """
        email = claims.get('email')
        preferred_username = claims.get('preferred_username')

        if not email:
            return self.UserModel.objects.none()
        users = self.UserModel.objects.filter(email__iexact=email)

        if len(users) < 1:
            if not preferred_username:
                return self.UserModel.objects.none()
            users = self.UserModel.objects.filter(username__iexact=preferred_username)
        return users

    def update_user(self, user, claims):
        user.first_name = claims.get('given_name', '')
        user.last_name = claims.get('family_name', '')
        user.email = claims.get('email')
        user.username = claims.get('preferred_username')
        user.save()
        return user

django logs

django_web3       | django 02/Jan/2023:14:02:14,278955 +0000 [ERROR] django.request: thread=281473135460832 extra_status_code=500&request=<WSGIRequest:GET'/oidc/callback/?state=R1OaaRk4jfr5nNNI4gr5CSdfX6sXoEqg&session_state=ea277a99-7da4-4904-abb6-63fa9bb7fb75&code=4b3c49b6-11c9-4cef-b847-fe356dfe86c3.ea277a99-7da4-4904-abb6-63fa9bb7fb75.bad660f7-7969-42d3-8475-536e4955c554'> Internal Server Error: /oidc/callback/
django_web3       | Traceback (most recent call last):
django_web3       |   File "/usr/local/lib/python3.5/site-packages/django/core/handlers/base.py", line 149, in get_response
django_web3       |     response = self.process_exception_by_middleware(e, request)
django_web3       |   File "/usr/local/lib/python3.5/site-packages/django/core/handlers/base.py", line 147, in get_response
django_web3       |     response = wrapped_callback(request, *callback_args, **callback_kwargs)
django_web3       |   File "/usr/local/lib/python3.5/site-packages/django/views/generic/base.py", line 68, in view
django_web3       |     return self.dispatch(request, *args, **kwargs)
django_web3       |   File "/usr/local/lib/python3.5/site-packages/django/views/generic/base.py", line 88, in dispatch
django_web3       |     return handler(request, *args, **kwargs)
django_web3       |   File "/usr/local/lib/python3.5/site-packages/mozilla_django_oidc/views.py", line 88, in get
django_web3       |     self.user = auth.authenticate(**kwargs)
django_web3       |   File "/usr/local/lib/python3.5/site-packages/django/contrib/auth/__init__.py", line 74, in authenticate
django_web3       |     user = backend.authenticate(**credentials)
django_web3       |   File "/usr/local/lib/python3.5/site-packages/mozilla_django_oidc/auth.py", line 242, in authenticate
django_web3       |     return self.get_or_create_user(access_token, id_token, verified_id)
django_web3       |   File "/usr/local/lib/python3.5/site-packages/mozilla_django_oidc/auth.py", line 259, in get_or_create_user
django_web3       |     user_info = self.get_userinfo(access_token, id_token, verified_id)
django_web3       |   File "/usr/local/lib/python3.5/site-packages/mozilla_django_oidc/auth.py", line 202, in get_userinfo
django_web3       |     user_response.raise_for_status()
django_web3       |   File "/usr/local/lib/python3.5/site-packages/requests/models.py", line 943, in raise_for_status
django_web3       |     raise HTTPError(http_error_msg, response=self)
django_web3       | requests.exceptions.HTTPError: 401 Client Error: Unauthorized for url: http://keycloak:8080/realms/SquadStack/protocol/openid-connect/userinfo
django_web3       | django 02/Jan/2023:14:02:14,398185 +0000 [INFO] qinspect.middleware: thread=281473135460832 extra: [SQL] 0 queries (0 duplicates), 0 ms SQL time, 201 ms total request time
django_web3       | [02/Jan/2023 19:32:14] "GET /oidc/callback/?state=R1OaaRk4jfr5nNNI4gr5CSdfX6sXoEqg&session_state=ea277a99-7da4-4904-abb6-63fa9bb7fb75&code=4b3c49b6-11c9-4cef-b847-fe356dfe86c3.ea277a99-7da4-4904-abb6-63fa9bb7fb75.bad660f7-7969-42d3-8475-536e4955c554 HTTP/1.1" 500 269936

This issue is same as this one
https://github.com/mozilla/mozilla-django-oidc/issues/481
As per it’s solution i think i have to change OIDC_OP_AUTHORIZATION_ENDPOINT to "http://keycloak:8080/realms/SquadStack/protocol/openid-connect/auth"
but i can’t reach this site http://keycloak:8080
I can’t reach keycloak server by using docker container name instead of localhost in hostname
i can’t figure out whether the problem is with docker, mozilla-django-oidc or Keycloak and i am stuck here
I am new to docker and keycloak might have done some naive mistake
Any help will be appreciated!

Asked By: Sagar Gupta

||

Answers:

I used kubernetes.docker.internal as domain name instead of localhost and keycloak which resolves my problem
My changed settings:

KEYCLOACK_BASE_URI = "http://kubernetes.docker.internal:8080"
KEYCLOACK_REALM_NAME = "SquadStack"
OIDC_AUTH_URI = KEYCLOACK_BASE_URI + "/realms/" + KEYCLOACK_REALM_NAME
OIDC_OP_JWKS_ENDPOINT = OIDC_AUTH_URI + "/protocol/openid-connect/certs"
OIDC_OP_AUTHORIZATION_ENDPOINT = OIDC_AUTH_URI + "/protocol/openid-connect/auth"
OIDC_OP_TOKEN_ENDPOINT = OIDC_AUTH_URI + "/protocol/openid-connect/token"
OIDC_OP_USER_ENDPOINT = OIDC_AUTH_URI + "/protocol/openid-connect/userinfo"
OIDC_OP_LOGOUT_ENDPOINT = OIDC_AUTH_URI + "/protocol/openid-connect/logout"
Answered By: Sagar Gupta