我应该如何在Django应用程序中使用AAD实现用户SSO(使用Django Microsoft身份验证后端模块)? [英] How should I be implementing user SSO with AAD in a Django application (using the Django Microsoft Authentication Backend module)?

查看:95
本文介绍了我应该如何在Django应用程序中使用AAD实现用户SSO(使用Django Microsoft身份验证后端模块)?的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我正在使用 Django开发Django(2.2.3)应用程序已安装Microsoft Auth 以使用Azure AD处理SSO.我已经能够按照快速入门文档使用我的Microsoft身份或添加到Django用户表中的标准用户名和密码登录到Django Admin面板.所有这些都是开箱即用的,很好.

(真的)我提出的问题只是接下来我要做什么?".从用户的角度来看,我希望他们:

  1. 导航到我的应用程序(example.com/或example.com/content)-Django将意识到它们未通过身份验证,或者
    • 自动将它们重定向到同一窗口中的SSO门户,或者
    • 将他们重定向到example.com/login,这要求他们单击将打开SSO的按钮 窗口中的门户(默认情况下会发生这种情况)
  2. 允许他们登录并通过其Microsoft帐户使用MFA
  3. 成功将它们重定向到我的@login_required页面(example.com/content)

当前,在导航的根目录(example.com/)中,我有以下内容:

    def index(request):
        if request.user.is_authenticated:
            return redirect("/content")
        else:
            return redirect("/login")

我最初的想法是将redirect("/login")更改为redirect(authorization_url)-这就是我的问题所在.

据我所知,没有任何方法可以获取上下文处理器的当前实例(?)或microsoft_auth插件的后端来调用authorization_url()函数并从.

好...然后我想我要实例​​化生成身份验证URL的MicrosoftClient类.这没有用-不能100%确定原因,但是它认为这可能与后端/上下文处理器上的实际MicrosoftClient实例所使用的某些状态变量与我的实例不一致有关.

最后,我试图模仿自动/admin页的作用-呈现一个SSO按钮供用户单击,然后在单独的窗口中打开Azure门户.经过一番研究后,我意识到我从根本上遇到了同样的问题-身份验证URL作为内联JS传递到管理登录页面模板中,此后用于在客户端异步创建Azure窗口.

作为一个健全性检查,我尝试手动导航到admin登录页面中显示的身份验证URL,并且确实有效(尽管重定向到/content无效).

在这一点上,考虑到我认为我自己很难做到,我觉得我会完全以错误的方式来做这件事.遗憾的是,我找不到任何有关如何完成此过程部分的文档.

那么,我在做什么错了?!

解决方案

多花了几天的时间,我最终自己解决了这些问题,并了解了有关Django的工作原理的更多信息.

我所缺少的链接是(第三方)Django模块的上下文处理器如何/在何处将其上下文传递到最终呈现的页面.我没有意识到默认情况下,我也可以在任何模板中访问microsoft_auth包中的变量(例如其模板中使用的authorisation_url).知道了这一点,我能够实现管理面板使用的基于JS的相同登录过程的稍微简化的版本.

假设以后再读此书的任何人都正在经历与我相同的(学习)过程(尤其是此软件包),那么我也许可以猜到接下来的几个问题……/p>

第一个是我已经成功登录...我该如何代表用户做任何事情?!".可以假定您将获得用户的访问令牌以用于将来的请求,但是在编写此程序包时,默认情况下似乎并没有以任何明显的方式进行操作.该软件包的文档仅能使您登录到管理面板.

(在我看来,不是很明显)答案是必须将MICROSOFT_AUTH_AUTHENTICATE_HOOK设置为可以在成功进行身份验证时调用的函数.它将传递给已登录的用户(模型)及其令牌JSON对象,供您随意处理.经过深思熟虑,我选择使用AbstractUser扩展我的用户模型,并只保留每个用户的令牌及其其他数据.

models.py

class User(AbstractUser):
    access_token = models.CharField(max_length=2048, blank=True, null=True)
    id_token = models.CharField(max_length=2048, blank=True, null=True)
    token_expires = models.DateTimeField(blank=True, null=True)

aad.py

from datetime import datetime
from django.utils.timezone import make_aware

def store_token(user, token):
    user.access_token = token["access_token"]
    user.id_token = token["id_token"]
    user.token_expires = make_aware(datetime.fromtimestamp(token["expires_at"]))
    user.save()

settings.py

MICROSOFT_AUTH_EXTRA_SCOPES = "User.Read"
MICROSOFT_AUTH_AUTHENTICATE_HOOK = "django_app.aad.store_token"

请注意MICROSOFT_AUTH_EXTRA_SCOPES设置,这可能是您的第二个/附加问题-程序包中的默认范围设置为SCOPE_MICROSOFT = ["openid", "email", "profile"],如何添加更多的范围并不明显.我至少需要添加User.Read.请记住,该设置需要一串用空格分隔的范围,而不是列表.

获得访问令牌后,您就可以向Microsoft Graph API发出请求了.他们的图形资源管理器在解决此问题方面非常有用.

I'm developing a Django (2.2.3) application with Django Microsoft Auth installed to handle SSO with Azure AD. I've been able to follow the quickstart documentation to allow me to log into the Django Admin panel by either using my Microsoft identity, or a standard username and password I've added to the Django user table. This all works out of the box and is fine.

My question put (really) simply is "What do I do next?". From a user's perspective, I'd like them to:

  1. Navigate to my application (example.com/ or example.com/content) - Django will realise they aren't authenticated, and either
    • automatically redirect them to the SSO portal in the same window, or
    • redirect them to example.com/login, which requires them to click a button that will open the SSO portal in a window (which is what happens in the default admin case)
  2. Allow them to sign in and use MFA with their Microsoft Account
  3. Once successful redirect them to my @login_required pages (example.com/content)

Currently, at the root of my navigation (example.com/), I have this:

    def index(request):
        if request.user.is_authenticated:
            return redirect("/content")
        else:
            return redirect("/login")

My original idea was to simply change the redirect("/login") to redirect(authorization_url) - and this is where my problems start..

As far as I can tell, there isn't any way to get the current instance(?) of the context processor or backend of the microsoft_auth plugin to call the authorization_url() function and redirect the user from views.py.

Ok... Then I thought I'd just instantiate the MicrosoftClient class that generates the auth URL. This didn't work - not 100% sure why, but it think it may have something to do with the fact that some state variable used by the actual MicrosoftClient instance on the backend/context processor is inconsistent with my instance.

Finally, I tried to mimic what the automatic /admin page does - present an SSO button for the user to click, and open the Azure portal in a separate window. After digging around a bit, I realise that I fundamentally have the same problem - the auth URL is passed into the admin login page template as inline JS, which is later used to create the Azure window asynchronously on the client side.

As a sanity check, I tried to manually navigate to the auth URL as it is presented in the admin login page, and that did work (though the redirect to /content didn't).

At this point, given how difficult I think I'm making it for myself, I'm feel like I'm going about this whole thing completely the wrong way. Sadly, I can't find any documentation on how to complete this part of the process.

So, what am I doing wrong?!

解决方案

A couple more days at this and I eventually worked out the issues myself, and learned a little more about how Django works too.

The link I was missing was how/where context processors from (third party) Django modules pass their context's through to the page that's eventually rendered. I didn't realise that variables from the microsoft_auth package (such as the authorisation_url used in its template) were accessible to me in any of my templates by default as well. Knowing this, I was able to implement a slightly simpler version of the same JS based login process that the admin panel uses.

Assuming that anyone reading this in the future is going through the same (learning) process I have (with this package in particular), I might be able to guess at the next couple of questions you'll have...

The first one was "I've logged in successfully...how do I do anything on behalf of the user?!". One would assume you'd be given the user's access token to use for future requests, but at the time of writing this package didn't seem to do it in any obvious way by default. The docs for the package only get you as far as logging into the admin panel.

The (in my opinion, not so obvious) answer is that you have to set MICROSOFT_AUTH_AUTHENTICATE_HOOK to a function that can be called on a successful authentication. It will be passed the logged in user (model) and their token JSON object for you to do with as you wish. After some deliberation, I opted to extend my user model using AbstractUser and just keep each user's token with their other data.

models.py

class User(AbstractUser):
    access_token = models.CharField(max_length=2048, blank=True, null=True)
    id_token = models.CharField(max_length=2048, blank=True, null=True)
    token_expires = models.DateTimeField(blank=True, null=True)

aad.py

from datetime import datetime
from django.utils.timezone import make_aware

def store_token(user, token):
    user.access_token = token["access_token"]
    user.id_token = token["id_token"]
    user.token_expires = make_aware(datetime.fromtimestamp(token["expires_at"]))
    user.save()

settings.py

MICROSOFT_AUTH_EXTRA_SCOPES = "User.Read"
MICROSOFT_AUTH_AUTHENTICATE_HOOK = "django_app.aad.store_token"

Note the MICROSOFT_AUTH_EXTRA_SCOPES setting, which might be your second/side question - The default scopes set in the package as SCOPE_MICROSOFT = ["openid", "email", "profile"], and how to add more isn't made obvious. I needed to add User.Read at the very least. Keep in mind that the setting expects a string of space separated scopes, not a list.

Once you have the access token, you're free to make requests to the Microsoft Graph API. Their Graph Explorer is extremely useful in helping out with this.

这篇关于我应该如何在Django应用程序中使用AAD实现用户SSO(使用Django Microsoft身份验证后端模块)?的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆