Как отладить проблему аутентификации 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 .................")