[Prev] Thread [Next]  |  [Prev] Date [Next]

Re: auth.user refactor: the profile aproach Donald Stufft Mon Apr 02 20:00:44 2012

On Monday, April 2, 2012 at 10:56 PM, Alex Ogier wrote:
> I realize that arguing with a BDFL might get me nowhere, but I don't think 
> that multi-profile + select_related + proxy attributes on the user model is 
> the proper approach for users going forward. The proposal makes some basic 
> sense as an incremental improvement on the current status quo of a built-in 
> user with fixed 30-character identifier and single one-to-one profile. But in 
> the larger scheme of things it moves further in the wrong direction, in my 
> opinion, towards a model that addresses people's specific 80% questions 
> (email-as-identifier, packageable extra fields) but not the general 
> best-of-all-worlds mechanisms.
> Consider the other batteries-included frameworks out there. I think the three 
> biggest ones are probably Ruby on Rails, ASP.NET (http://ASP.NET) MVC, and 
> Play Framework. Something all three have in common, something I think would 
> be valuable for Django, is that their User identity is absent, and 
> authentication and authorization modules are added to a developer-created 
> User model ad-hoc. I think it's fair to say, the ecosystem of third-party 
> authentication mechanisms surrounding all three of these competitor 
> frameworks is MUCH healthier than Django's, for the simple reason that it is 
> much more flexible and sane to define your own user and plug in an 
> authentication module then it is to plug authentication into a fixed user 
> model that magically proxies back (even one as simple as a varchar identity + 
> password). 
> The basic idea is that "authentication" is something that can be provided for 
> any model you like. When a user authenticates, they are providing some sort 
> of authentication token to your project that proves they are who they say 
> they are, and as a developer you are free to attach this authentication token 
> to *whatever you like,* not only contrib.auth.models.User. There is a rich 
> ecosystem of third party authentication apps for our competitor frameworks 
> that all work on this principle. They can provide authentication flows that 
> bounce the user back and forth to twitter.com (http://twitter.com), their 
> oauth provider, browserid, etc. and it is precisely because they don't demand 
> anything from a central object. Even something as simple as "all 
> user.identifiers that start with 'oauth2$' belong to our auth mechanism" 
> starts to open all sorts of security holes. What if someone wants to 
> authenticate by email+password OR by third party proof of email ownership (my 
> university has a mechanism 
like this)? Well, if someone can manage to register an account belonging to 
'[EMAIL PROTECTED] (mailto:[EMAIL PROTECTED])', maybe by triggering some 
obscure email change recovery form wizard or something, then suddenly they 
possess my account on Django. 
> This is why I think the only sane long-term solution involves distinct and 
> pluggable authentication modules, and a concept of users that doesn't enforce 
> any brand of identity. The second stipulation is very important for social 
> reasons, if only to ensure that the path of least resistance for third-party 
> authentication doesn't involve trying to overload identity mechanisms for 
> disparate and incompatible purposes. 
> JKM, you seem concerned that the notion of pluggable Users by necessity 
> involves magical settings.
> > I'm convinced that such an idea is ultimately a bad idea: it allows apps 
> > exert action at a distance over other apps. It would allow the idea of a 
> > user to completely change without any warning simply by modifying a setting.
> I am not convinced that this must be so. Asking a developer to write their 
> own User model is not the same thing as automagically reshaping 
> contrib.auth.models.User based on settings. A developer-defined notion of 
> identity is a thing that should be codified in software by constructing a 
> model. This community seems fixed on the idea that whatever model the 
> developer comes up with to satisfy the bizarre constraints of his particular 
> website, it must eventually be mounted at auth.User lest the world come 
> crashing down as foreign keys break, middleware throws exceptions and 
> California slides into the pacific ocean. 
> Nearly every other framework out there does this the opposite way: to 
> authenticate with a third-party service, you add fields to your identity 
> model to support whatever credentials are necessary. Not the other way round, 
> where auth modules define models that have OneToOneFields to auth.User that 
> uses an AUTH_PROFILES setting to magically proxy back. 
> I get that Django's core is very accustomed to the relational database mode 
> of thinking: "If a User might own a Twitter handle, then let's create a table 
> of twitter handles in the twitter-auth app, and foreign key back to the 
> default User model". It's really not that bad to go the other way for a 
> moment, and say "If a User might own a Twitter handle, then let's add a 
> twitter_handle field to Users." The reason being that the latter is *so much 
> more flexible*. You can simulate the first with the second, but not vice 
> versa. Twitter-auth might not need its own table (in fact, it shouldn't need 
> its own table). If you really wanted to, you could make one, and foreign-key 
> *from* the user model which gives you everything the first solution has, with 
> no need to created magical .prof1, .prof2 proxy attributes. You could even 
> let users sign in with multiple handles with a many-to-many. Heck, maybe your 
> blog posts have their own twitter credentials, I don't know. 
> So here's the short version of my proposal, which I plan on writing up in 
> full tomorrow: Break auth as it stands into reusable pieces. Let people write 
> their own user models, optionally using those pieces. Provide straightforward 
> settings for any contrib apps that absolutely *must* have a specific model to 
> key on (ideally none, but comments and admin probably need shims). Document 
> the new wave of best practices, borrowed straight from the frameworks that 
> got this right from the start: Write your own user, decide what identity 
> means (and hence, what your login forms look like), add in whatever 
> authentication mechanisms you like, add in whatever authorization mechanisms 
> you need (with specific instructions on what contrib.admin demands from your 
> model), and run with that. 
> Sorry for the rant, hopefully I'm not burning too many bridges,
> Alex Ogier
> -- 
> You received this message because you are subscribed to the Google Groups 
> "Django developers" group.
> To post to this group, send email to [EMAIL PROTECTED] (mailto:[EMAIL 
> To unsubscribe from this group, send email to [EMAIL PROTECTED] 
> (mailto:[EMAIL PROTECTED]).
> For more options, visit this group at 
> http://groups.google.com/group/django-developers?hl=en.
Identity doesn't have anything to do with automatically dispatching users. All 
it is is a unique identifier. That's all this proposal honestly enforces that 
your users have. Some single piece of identifiable data that can be used to 
differentiate. This could be a username, or an email address. It could be a 
random string. Anything you want. 

In your example you might have a TwitterProfile that provides 2 fields, 
"authenticated_by_twitter" and "twitter username". Then if you want to check 
how a person authenticated to your site, you'd merely check if 
user.data["authenticated_by_twitter"] was True. The identifier doesn't need to 
have that data codified in it, (but it could!) and I honestly do not think the 
statement "all users must have 1 single string of any length that uniquely 
identifies them" is that big of a burden.

You received this message because you are subscribed to the Google Groups 
"Django developers" group.
To post to this group, send email to [EMAIL PROTECTED]
To unsubscribe from this group, send email to [EMAIL PROTECTED]
For more options, visit this group at