django-social-auth没有重定向到为SAML登录提供的下一个查询参数



我有一个django应用程序,它使用django社交身份验证,并允许三种类型的SSO身份验证,即Google、Office 365和SAML2.0。在SAML2.0的情况下,应用程序允许最终用户指定他们自己的IdP,为了实现这一点,我们有一个自定义的数据库SAML Auth类,它允许我们将用户的IdP信息存储在数据库中,并让用户登录。这按预期工作,用户可以使用SAML、谷歌或Office 365登录,没有问题。

挑战在于登录完成后,我需要重定向到特定的URL。这对谷歌和Office 365来说是正常的,但对SAML登录来说不是。

例如,我有一个移动应用程序,它通过OAUTH对django web应用程序进行身份验证。当该移动应用程序启动oauth流时,它会转到授权URL,并被转发到登录。路径看起来像这样:

  1. https://myapp.com/oauth/authorize/?client_id=something
  2. https://myapp.com/login/?next=/oauth/authorize/?client_id=something
  3. (选择SAML登录方式登录后)https://myapp.com/login/subdomain/?next=/oauth/authorize/?client_id=something
  4. 重定向至IdP
  5. 重定向回断言使用者urlhttps://myapp.com/login/sso/saml/complete/
  6. 重定向到帐户页面https://myapp.com/manage/(不是https://myapp.com/oauth/authorize/?client_id=something如预期)

当然,令人沮丧的是,这破坏了我的移动应用程序的oauth流,因为用户登录到web应用程序,但从未授权移动应用程序。

根据python社交认证文档?next=应用于(如果存在)在成功身份验证后重定向用户。

我已经做了很多调试,在社交核心do_auth方法中可以看到,下一个参数确实按预期添加到了会话中。

但是,当我调试并查看IdP将用户发布回我的应用程序后社交核心do_complete方法中返回的会话时,该会话是空的,不包含任何数据,包括没有下一个参数。

在某一时刻,所有这些都如预期的那样奏效了。然而,它已经停止工作了,我找不到任何代码更改来说明为什么会出现这种情况。我向更大的社区提出的问题是,我是否错过了一些可能解决我的问题并使我回到SAML登录按预期重定向的路径上的东西。

一些可能有用的进一步细节:

  • Django版本2.2.24
  • Python 3.6
  • 已测试社交身份验证应用django 3.0.0和5.0.0版本
  • 测试了社交认证核心3.3.3和4.1.0版本

我的中间件(注意,作为我们应用程序的一部分,其中许多都是自定义中间件):

MIDDLEWARE = [
'instana.instrumentation.django.middleware.InstanaMiddleware',
'eb.middleware.HealthCheckMiddleware',
'eb.middleware.OriginalXForwardedProtoMiddleware',
'eb.middleware.DebugMiddleware',
'eb.middleware.ContentSecurityPolicy',
'django_cookies_samesite.middleware.CookiesSameSite',
'common.middleware.RequestIDMiddleware',
'common.middleware.APIErrorHandler',
'corsheaders.middleware.CorsMiddleware',
'django.contrib.sessions.middleware.SessionMiddleware',
'django.middleware.locale.LocaleMiddleware',
'django.middleware.common.CommonMiddleware',
'django.middleware.csrf.CsrfViewMiddleware',
'django.middleware.clickjacking.XFrameOptionsMiddleware',
'django.contrib.auth.middleware.AuthenticationMiddleware',
'accounts.middleware.TwoFactorMiddleware',
'accounts.middleware.PasswordChangeMiddleware',
'accounts.middleware.VerifyActiveMiddleware',
'django.contrib.messages.middleware.MessageMiddleware',
'django.middleware.gzip.GZipMiddleware',
'billing.middleware.CartMiddleware',
'accounts.middleware.SentryContextMiddleware',
'accounts.middleware.SocialAuthExceptionMiddleware',
'accounts.middleware.RealIPMethodMiddleware',
'common.sqla.middleware.SQLAlchemyMiddleware',
'django.middleware.security.SecurityMiddleware',
]

已尝试故障排除步骤:

  • 删除/重新排列中间件
  • 更改django应用程序中会话cookie和SOCIAL_AUTH安全设置的设置
  • 运行几个PDB会话以查看会话设置和上面提到的内容

感谢您提前提供的帮助。

我今天也偶然发现了这个问题,这可能是SameSite cookie的问题。正如您正确指出的,next参数是在用户会话中正确设置的。为了记住会话,在重定向到SAML-IDP之前,会话id存储在cookie中。一旦在SAML提供者上进行了身份验证,后者将重定向回django,但由于SameSite cookie的默认设置是Lax,cookie将不会与(重定向)请求一起发送,因为SAML提供者的域很可能不是"域";"同一站点";作为django网站。

本质上,这意味着next参数正确存储在会话中,但一旦用户通过IDP进行身份验证后来到django,django就无法关联回会话。

为了缓解这种情况,我们需要将SameSite cookie策略设置为None,从而允许cookie也与来自其他域/站点的请求一起发送,而不是与发出cookie的域/站点一起发送,或者找到另一种方法来保持两个请求之间的状态(登录后重定向到的路径)。SAML本身提供了实现这一点的功能,称为RelayState:

此RelayState参数是一个不透明的标识符,在没有任何修改或检查的情况下返回

理论上听起来很棒,但python social core/django social auth目前(ab-)使用RelayState只传递idp的名称,因为他们试图在一个站点上支持多个SAML idp。因此,需要调整内置的SAML提供程序,以便在传递idp名称的同时传递next参数,以使其正常工作。

最新更新