grafana / django-saml2-auth

Django SAML2 Authentication Made Easy. Easily integrate with SAML2 SSO identity providers like Okta, Azure AD and others.
Other
184 stars 55 forks source link

Getting 1106 Error on Dev Server #240

Closed PareekHiresure closed 6 months ago

PareekHiresure commented 9 months ago

I recently started working on SAML, through the previous answers I was able to successfully implement SAML auth, login via Active Directory IDP. But when I ran my code on a development server, I am getting error 1106.

Upon some debugging i have figured out that the reverse generation of acs url is the issue. acs_url = domain + get_reverse([acs, "acs", "django_saml2_auth:acs"]) # type: ignore

But I am unable to understand why the behaviour changes between a local system and on a server.

My django project urls: SAML URLs path('sso/signin/', saml_views.signin), path('sso/acs/', saml_views.acs), path('api/v1/redirect/auth', saml_triggers.saml_auth_redirect, name='saml_auth_redirect')

Settings These are my saml_settings and respective urls:

default_next_url = 'http://127.0.0.1:8003/api/v1/redirect/auth'
assertion_url = 'http://127.0.0.1:8003'
entity_id = 'http://127.0.0.1:8003/'
frontend_url = 'http://localhost:3000/auth/saml'

SAML2_AUTH = {

Metadata is required, choose either remote url or local file path

'KEY_FILE': os.path.join(BASE_DIR, 'appraisal/views/'),
'CERT_FILE': os.path.join(BASE_DIR, 'appraisal/views/'),

'DEBUG': False,  # Send debug information to a log file

# Optional settings below
'DEFAULT_NEXT_URL': default_next_url,
'ATTRIBUTES_MAP': {  # Change Email/UserName/FirstName/LastName to corresponding SAML2 userprofile attributes.
    'email': 'emailAddress',   # currently fetching user from email
},
'GROUPS_MAP': {  # Optionally allow mapping SAML2 Groups to Django Groups
    'SAML Group Name': 'Django Group Name',
},
'TRIGGER': {
    # Optional: needs to return a User Model instance or None
    # Trigger paths removed they are working
    'GET_USER': 'trigger_path',
    'CUSTOM_CREATE_JWT': 'trigger_path',
    'CUSTOM_TOKEN_QUERY': 'trigger_path',
    'GET_METADATA_AUTO_CONF_URLS': 'trigger_path',
},
'ASSERTION_URL': assertion_url,  # Custom URL to validate incoming SAML requests against
'ENTITY_ID': entity_id,  # Populates the Issuer element in authn request
'NAME_ID_FORMAT': 'urn:oasis:names:tc:SAML:2.0:nameid-format:email',  # Sets the Format property of authn NameIDPolicy element, e.g. 'user.email'
'USE_JWT': True,
# Set this to True if you are running a Single Page Application (SPA) with Django Rest Framework (DRF), and are using JWT authentication to authorize client users
'JWT_ALGORITHM': 'HS256',  # JWT algorithm to sign the message with
'FRONTEND_URL': frontend_url,  # Redirect URL for the client if you are using JWT auth with DRF. See explanation below
'LOGIN_CASE_SENSITIVE': True,  # whether of not to get the user in case_sensitive mode
'AUTHN_REQUESTS_SIGNED': True,  # Require each authentication request to be signed
'LOGOUT_REQUESTS_SIGNED': True,  # Require each logout request to be signed
'WANT_ASSERTIONS_SIGNED': True,  # Require each assertion to be signed
'WANT_RESPONSE_SIGNED': True,  # Require response to be signed
'ACCEPTED_TIME_DIFF': None,  # Accepted time difference between your server and the Identity Provider
'ALLOWED_REDIRECT_HOSTS': ['127.0.0.1:8003'],
'TOKEN_REQUIRED': False,

}

mostafa commented 8 months ago

Hey @PareekHiresure,

What are you importing as saml_views?

PareekHiresure commented 8 months ago

import django_saml2_auth.views as saml_views

mostafa commented 8 months ago

@PareekHiresure Then, the Entity ID and the Reply URL (assertion_url) should be: http://127.0.0.1:8003/sso/acs/, which I suppose are why you're redirected to the wrong place and hence the error you see.

PareekHiresure commented 8 months ago

And as per your suggestion, I had already tried it before, it turns out the acs reverse function already adds the sso/acs/ after my domain hence if I add it in the assertion url. The url becomes like: http://127.0.0.1:8003/sso/acs/sso/acs/

Yes, I understand the concern is the above code works fine perfectly on my local system but the same is not happening for development server. Can you explain why this might be happening?

mostafa commented 8 months ago

@PareekHiresure

What do you mean that it doesn't work on the development server? You probably should use the development server's IP address instead of 127.0.0.1.

PareekHiresure commented 8 months ago

Hey @mostafa,

I still couldn't figure out the issue but after numerous attempts it seems that in my project urls, I was only using the below two: path('sso/signin/', saml_views.signin), path('sso/acs/', saml_views.acs),

But after I changed it to the below urls, it worked fine on the dev server: path(r'sso/', include('django_saml2_auth.urls')), path('sso/signin/', saml_views.signin),

If it was mandatory to include all urls, the same issue should have persisted on my local system as well. If you could explain why this was happening, it would be really helpful.

And as per the server IP issue that you mentioned, I couldn't share dev server's IP hence I shared the entire settings of my local which are same as that of the dev server.

Thank you for the previous prompt responses, really appreciate them.

mostafa commented 8 months ago

It is possibly because you haven't mapped welcome and deny and only added acs and signin. The signin is a deprecated endpoint that is only used for SP-initiated SSO, and will be replaced by sp_initiated_login.

PareekHiresure commented 8 months ago

Thanks for the explanation and will try to move to sp_initiated_login endpoint.

github-actions[bot] commented 7 months ago

This issue is stale because it has been open 30 days with no activity. Remove stale label or comment or this will be closed in 5 days.

mateusz-wilk-booksy commented 7 months ago

Hello,

I am bumping this issue, as it seems that we have a similar problem in our project:

We have django_saml2_auth in INSTALLED_APPS and also have a saml2_auth app in our project:

saml2_auth.urls.py:


`from django.urls import re_path as url
from django_saml2_auth.views import signin as saml_signin

from django_app.apps.saml2_auth.views import custom_acs, saml_metadata, logout

urlpatterns = [
url(r'^acs', custom_acs, name="acs"),
url(r'^metadata/', saml_metadata, name='saml_metadata'),
url(r'^saml_login/$', saml_signin, name='saml_account_login'),
url(r'^saml_logout/$', logout, name='saml_account_logout'),
]

the main url file api/urls.py:

from django.urls import include, path
from grpc_health.v1 import health_pb2_grpc
from grpc_health.v1.health import HealthServicer

from django_app.apps.business_settings.grpc.servicer import BusinessSettingsServicer
from django_app.apps.business_settings.protobuf import business_settings_pb2_grpc
from django_app.apps.sales.admin import invoicing_admin_site

urlpatterns = [
    path('admin/', invoicing_admin_site.urls),
    path(
        'invoicing/',
        include('django_app.api.urls_prefixed'),
    ),
]

the relevant admin site:

class ProjectAdminSite(admin.AdminSite):
    def get_urls(self):
        urls = super().get_urls()
        custom_urls = [
            path('saml2_auth/', include(saml2_auth_urls)),
            path('health_check/', HealthCheckView.as_view(), name='admin_health_check'),
        ]
        return custom_urls + urls

The custom acs view:

`@csrf_exempt  # nosemgrep: no-csrf-exempt
@exception_handler
def custom_acs(request: HttpRequest):

    authn_response = decode_saml_response(request, acs)
    user_data = authn_response.get_identity()
    user_data['username'][0] = user_data['email'][0].split('@')[0]
    user = extract_user_identity(user_data)  # type: ignore

    relay_state = request.POST.get("RelayState")
    relay_state_is_token = is_jwt_well_formed(relay_state) if relay_state else False

    if relay_state and relay_state_is_token:
        redirected_user_id = decode_custom_or_default_jwt(relay_state)
        if get_user_id(user) != redirected_user_id:
            raise SAMLAuthError(
                "The user identifier doesn't match.",
                extra={
                    "exc_type": ValueError,
                    "error_code": USER_MISMATCH,
                    "reason": "User identifier mismatch.",
                    "status_code": 403,
                },
            )

    _, target_user = get_or_create_user(user)
    request.session.flush()

    if target_user.is_active:
        if hasattr(settings, "AUTHENTICATION_BACKENDS") and settings.AUTHENTICATION_BACKENDS:
            model_backend = settings.AUTHENTICATION_BACKENDS[0]
        else:
            model_backend = "django.contrib.auth.backends.ModelBackend"
        login(request, target_user, model_backend)
    else:
        raise SAMLAuthError(
            "The target user is inactive.",
            extra={
                "exc_type": Exception,
                "error_code": INACTIVE_USER,
                "reason": "User is inactive.",
                "status_code": 500,
            },
        )

The relevant settings snippet:

SAML2_CONF_BASE_URL = 'https://our.project.url/admin/saml2_auth/'

SAML2_AUTH = {
    'METADATA_AUTO_CONF_URL': SAML2_CONF_BASE_URL + 'metadata/',
    'CREATE_USER': False,
    'ATTRIBUTES_MAP': {
        'email': 'email',
        'username': 'username',
        'First name': 'firstName',
        'Last name': 'lastName'
    },
    'ENTITY_ID': SAML2_CONF_BASE_URL + 'acs/',
    'USE_JWT': False,
    'WANT_ASSERTIONS_SIGNED': True,
    'AUTHN_REQUESTS_SIGNED': False,
    'WANT_RESPONSE_SIGNED': False,
    'TOKEN_REQUIRED': False,
    'TRIGGER': {
        'GET_USER': 'django_app.apps.saml2_auth.hooks.get_user',
    },
}

All we get when trying to access any of the SAML views is 1108 (NoReverse, as if they didn't exist at all), or occasionally 1106 (no response from client), no matter if we try to enter the url by hand or click the Google SAML app in the Google Application menu. Surely we must have misconfigured something here, can you help us out?

Thank you!

Mateusz

mostafa commented 7 months ago

Hey @mateusz-wilk-booksy,

I highly recommend not creating your own ACS endpoint. Other than that, you are not receiving a (valid) SAMLResponse from the redirect to your ACS endpoint. It could be an error instead due to some misconfig on your SAML SSO app or SAML2_AUTH. Read this how to debug? section to see what the server is passing to your ACS endpoint.

github-actions[bot] commented 6 months ago

This issue is stale because it has been open 30 days with no activity. Remove stale label or comment or this will be closed in 5 days.

github-actions[bot] commented 6 months ago

This issue was closed because it has been stalled for 5 days with no activity.