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 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, 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 'aog...@princeton.edu', 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 django-developers@googlegroups.com.
To unsubscribe from this group, send email to 
django-developers+unsubscr...@googlegroups.com.
For more options, visit this group at 
http://groups.google.com/group/django-developers?hl=en.

Reply via email to