Как отладить проблему аутентификации Okta SAML2 в приложении React + Django?

Я интегрирую аутентификацию Okta SAML2 в свое приложение React + Django. Однако, несмотря на отсутствие ошибок, когда я пытаюсь получить доступ к https://org.okta.com/app/org_lighthousedev_1/exk14dabkz9lmSTEq0x8/sso/saml, он перенаправляет меня на https://dev-lighthouse.corporate.org.com/accounts/login/?next=/sso/saml/ вместо того, чтобы завершить поток SAML-аутентификации.

Я не знаю, как отладить эту проблему. Я использую djangosaml2==1.9.2 и djangorestframework-simplejwt==5.3.1. Может ли кто-нибудь подсказать, что может быть упущено, просмотрев приведенный ниже фрагмент кода?

Вот что я указал в запросе okta на создание приложения

URL единой регистрации

https://dev-lighthouse.corporate.org.com/sso/saml/

URL получателя

https://dev-lighthouse.corporate.org.com/sso/saml/

URL назначения

https://dev-lighthouse.corporate.org.com/sso/saml/

URI аудитории (SP Entity ID) https://dev-lighthouse.corporate.org.com/

Подробности предоставлены командой Okta Team

URL-адрес единой регистрации поставщика удостоверений личности:

https://org.okta.com/app/org_lighthousedev_1/exk14dabkz9lmSTEq0x8/sso/saml

Эмитент поставщика удостоверений личности:

http://www.okta.com/exk14dabkz9lmSTEq0x8

X.509 Certificate:okta.cert.txt

Метаданные IDP : IDP_Metadata.txt

Метаданные.xml


    <?xml version="1.0" encoding="UTF-8"?><md:EntityDescriptor entityID="http://www.okta.com/exk14dabkz9lmSTEq0x22328" xmlns:md="urn:oasis:names:tc:SAML:2.0:metadata"><md:IDPSSODescriptor WantAuthnRequestsSigned="false" protocolSupportEnumeration="urn:oasis:names:tc:SAML:2.0:protocol"><md:KeyDescriptor use="signing"><ds:KeyInfo xmlns:ds="http://www.w3.org/2000/09/xmldsig#"><ds:X509Data><ds:X509Certificqate>MIIDojCCAoqgAqeqeqewIBqweqweqeAgIGAY9N9lbIMA0GsfsbfhsbfqwyegqyrqywqqCSqGSIb3DQEBCwUAMIGRMQswCQYDVQQGEwJVUzETMBEG
..................
94nXBpszjEGkSxsAJ1HxEzDdVdDcTRa4YIVGPazKIaiNbNgCha8oGQVgJaXv2EkKFgqhcZeQ0Mfq
O7jSBH/M2U7QoTMhj0NhUCdy9ZGwt95TPhpJ8HZ6f0Ynqw+TmsPiRJyp8EVgjyv9weHgeuGk81k3
3JNrh0k2UpnGECHngkDJWGRseXr+zg==</ds:X509Certificate></ds:X509Data></ds:KeyInfo></md:KeyDescriptor><md:NameIDFormat>urn:oasis:names:tc:SAML:1.1:nameid-format:unspecified</md:NameIDFormat><md:NameIDFormat>urn:oasis:names:tc:SAML:1.1:nameid-format:emailAddress</md:NameIDFormat><md:SingleSignOnService Binding="urn:oasis:names:tc:SAML:2.0:bindings:HTTP-POST" Location="https://org.okta.com/app/org_lighthousedev_1/exk14dabkz9lmSTEq0x8/sso/saml"/><md:SingleSignOnService Binding="urn:oasis:names:tc:SAML:2.0:bindings:HTTP-Redirect" Location="https://org.okta.com/app/org_lighthousedev_1/exk14dabkz9lmSTEq0x8/sso/saml"/></md:IDPSSODescriptor></md:EntityDescriptor>

setting.py

saml_settings.py

import saml2
from saml2.saml import NAMEID_FORMAT_EMAILADDRESS
from saml2.sigver import get_xmlsec_binary
import os

BASE_DIR = os.path.dirname(os.path.dirname(os.path.abspath(__file__)))

SAML_CONFIG = {
    'debug': True,
    'xmlsec_binary': get_xmlsec_binary(['/usr/bin/xmlsec1']),
    'entityid': 'http://www.okta.com/exk14dabkz9lmSTEq0x8',  # Updated to match the Audience URI
    'service': {
        'sp': {
            'name': 'Django SP',
            'endpoints': {
                'assertion_consumer_service': [
                    ('https://dev-lighthouse.corporate.org.com/sso/saml/', saml2.BINDING_HTTP_POST),
                    # Updated to match the Single Sign-On URL
                ],
                'single_logout_service': [
                    ('https://dev-lighthouse.corporate.org.com/sso/saml/logout/', saml2.BINDING_HTTP_REDIRECT),
                    ('https://dev-lighthouse.corporate.org.com/sso/saml/logout/', saml2.BINDING_HTTP_POST),
                    # Updated to match the Single Logout URLs
                ],
            },
            'allow_unsolicited': True,
            'authn_requests_signed': False,  # Okta metadata says 'WantAuthnRequestsSigned="false"'
            'logout_requests_signed': True,
            'want_assertions_signed': True,
            'want_response_signed': True,
            'name_id_format': NAMEID_FORMAT_EMAILADDRESS,
        },
        
    },
    'metadata': {
        'local': [os.path.join(BASE_DIR, 'saml/dev_metadata.xml')],
    },
    'ca_certs': os.path.join(BASE_DIR, 'saml/dev_okta.cert'),  # Path to Okta's certificate
}

urls.py


from django.contrib import admin
from django.urls import path, include
from base_app.views.CustomSAML2View import CustomSAML2View



urlpatterns = [
    path('sso/saml/', CustomSAML2View.as_view(), name='saml2_acs'),
    path('saml2/', include('djangosaml2.urls')),
    ]

CustomSAML2View.py

    from django.http import JsonResponse
from rest_framework_simplejwt.tokens import RefreshToken
from djangosaml2.views import EchoAttributesView
from django.views.decorators.csrf import csrf_exempt
import logging
# from django.contrib.auth.decorators import login_required
from django.utils.decorators import method_decorator

# Configure logging
logging.basicConfig(filename='OKTA_JWT.logs', level=logging.INFO, format='%(asctime)s - %(levelname)s - %(message)s')


@method_decorator(csrf_exempt, name='dispatch')
# @method_decorator(login_required, name='dispatch', login_url=None)  # Disables the redirection to the login page
class CustomSAML2View(EchoAttributesView):
    def post(self, request, *args, **kwargs):
        try:
            logging.info('Inside CustomSAML2View')
            # Call the superclass post method, which handles the SAML assertion processing
            response = super().post(request, *args, **kwargs)
            # Check if the user is authenticated after the SAML assertion is processed
            if request.user.is_authenticated:
                # Generate JWT tokens for the authenticated user
                refresh = RefreshToken.for_user(request.user)
                logging.info(f'User {request.user.username} logged in with email {request.user.email}')
                # Return JWT to the client
                return JsonResponse({
                    'refresh': str(refresh),
                    'access': str(refresh.access_token),
                })
            else:
                # Log authentication failure
                logging.warning('Authentication failed - user is not authenticated.')
                return JsonResponse({'error': 'Authentication Failed'}, status=401)
        except Exception as e:
            # Log any exceptions that occur during the process
            logging.error('Error during SAML2 processing: %s', str(e), exc_info=True)
            # Return a JSON response indicating an internal error
            return JsonResponse({'error': 'Internal Server Error'}, status=500)
        finally:
            logging.info("FINALLY .................")
Вернуться на верх