Changing django.contrib.auth to make passwords optional

2,456 views
Skip to first unread message

Simon Willison

unread,
May 10, 2007, 6:30:54 PM5/10/07
to Django developers, si...@simonwillison.net
I'm working on a new component for my Django OpenID package which will
provide support for associating one or more OpenIDs with a
django.contrib.auth User. As part of this, I want to include the
ability to register for a new user account using an OpenID instead of
a password.

At the moment, django.contrib.auth does not support creating a user
account without setting a password.

OpenID is not the only use case where password-less accounts might be
warranted. Any application where authentication takes place against an
external source - for example, authenticating against an existing LDAP
provider - would also benefit from being able to create Django user
accounts without setting a password.

I propose the following changes:

1. The 'password' field in the User model should be altered to have
blank=True.

This would allow us to set blank passwords as an empty string. It
would not require existing installations to make any schema changes as
the column would still be NOT NULL.

2. user.set_password(password) should be altered to accept 'None'

If None is passed to the function, a blank string will be stored
instead of an encrypted password.

3. user.has_password() should be added

A simple utility method which returns True if the user has a password,
False if they do not.

4. check_password should ALWAYS return False if no password is set

This should help protect us from introducing any security issues with
these changes.

Does this sound like a workable plan? If so, I'd like to get the
changes in as soon as possible so I can wrap up work on the next
version of the OpenID package.

Cheers,

Simon

Ivan Sagalaev

unread,
May 11, 2007, 2:50:57 AM5/11/07
to django-d...@googlegroups.com
Simon Willison wrote:
> I'm working on a new component for my Django OpenID package which will
> provide support for associating one or more OpenIDs with a
> django.contrib.auth User. As part of this, I want to include the
> ability to register for a new user account using an OpenID instead of
> a password.
>
> At the moment, django.contrib.auth does not support creating a user
> account without setting a password.

Why not generate a random one? It won't break an ability to authenticate
using OpenID or any other backend for that matter. I've used this
approach with my OpenID auth backend and didn't encounter any downsides.

Mikhail Gusarov

unread,
May 11, 2007, 3:04:35 AM5/11/07
to django-d...@googlegroups.com

Twas brillig at 10:50:57 11.05.2007 UTC+04 when Ivan Sagalaev did gyre and gimble:

>> At the moment, django.contrib.auth does not support creating a user account
>> without setting a password.

IS> Why not generate a random one? It won't break an ability to authenticate
IS> using OpenID or any other backend for that matter. I've used this approach
IS> with my OpenID auth backend and didn't encounter any downsides.

Because generation of random password is an ugly workaround. Your solution
requires long comment which explains to the reader of code, why do random
password is needed in first place.

It's all about expressing intention in code, not the implementation details.

--
JID: dott...@jabber.dottedmag.net

Michael van der Westhuizen

unread,
May 11, 2007, 3:50:26 AM5/11/07
to django-d...@googlegroups.com
Hi Simon,

On 5/11/07, Simon Willison <swil...@gmail.com> wrote:
>
[snip]


> 1. The 'password' field in the User model should be altered to have
> blank=True.
>
> This would allow us to set blank passwords as an empty string. It
> would not require existing installations to make any schema changes as
> the column would still be NOT NULL.

Oracle (and possibly other databases?) treat an empty string as NULL,
so this would break.

Sorry to be the bearer of bad news :-)

Michael

Malcolm Tredinnick

unread,
May 11, 2007, 4:08:28 AM5/11/07
to django-d...@googlegroups.com

Certainly Oracle treats them empty string as equal to NULL. But does
that mean you can't put an empty string in a "not NULL" column in
Oracle? I can't remember the answer to that and I don't have an
installed Oracle around that I can test with at the moment.

Regards,
Malcolm


Ivan Sagalaev

unread,
May 11, 2007, 8:02:43 AM5/11/07
to django-d...@googlegroups.com
Mikhail Gusarov wrote:
> Because generation of random password is an ugly workaround. Your solution
> requires long comment which explains to the reader of code, why do random
> password is needed in first place.

A comment in my code is about username part, not password :-). I would
indeed be glad to have some generalized method to associate OpenID's
with User's but it's not the question of this thread.

"Ugliness" is a subjective thing and I personally don't see
'make_random_password' as particularly ugly. This is why I ask about
purely technical issues with this.

Mikhail Gusarov

unread,
May 11, 2007, 9:17:52 AM5/11/07
to django-d...@googlegroups.com

Twas brillig at 16:02:43 11.05.2007 UTC+04 when Ivan Sagalaev did gyre and gimble:

IS> A comment in my code is about username part, not password :-).

That means that your code lacks the comment required for understanding your
intent.

IS> I would indeed be glad to have some generalized method to associate
IS> OpenID's with User's but it's not the question of this thread.

See the subject: "Changing django.contrib.auth to make passwords optional".

IS> "Ugliness" is a subjective thing and I personally don't see
IS> 'make_random_password' as particularly ugly. This is why I ask about purely
IS> technical issues with this.

Exressing intention in the code IS technical issue: it influences maintenability
of the code. Not even mentioning that from the logical view having a password
for a account which does not have password is plainly broken.

--
JID: dott...@jabber.dottedmag.net

buriy

unread,
May 11, 2007, 9:42:43 AM5/11/07
to Django developers
On May 11, 5:30 am, Simon Willison <swilli...@gmail.com> wrote:
> I'm working on a new component for my Django OpenID package which will
> provide support for associating one or more OpenIDs with a
> django.contrib.auth User. As part of this, I want to include the
> ability to register for a new user account using an OpenID instead of
> a password.
Btw, yesterday I've finished this on current django-dev. I used your
library django-openid. Will write about this today.
Definitely, "Account" and "User" entities should be separated in
further versions of django, but I was much more interested in solution
for the current moment, so I added OpenIDUser class which mimics User,
has openid field, but which just do nothing on set_password and
get_password. Looking forward to following django core changes.

Simon Willison

unread,
May 11, 2007, 9:50:41 AM5/11/07
to Django developers
On May 11, 7:50 am, Ivan Sagalaev <Man...@SoftwareManiacs.Org> wrote:
> > At the moment, django.contrib.auth does not support creating a user
> > account without setting a password.
>
> Why not generate a random one? It won't break an ability to authenticate
> using OpenID or any other backend for that matter. I've used this
> approach with my OpenID auth backend and didn't encounter any downsides.

The problem with using a random password is that you can't answer the
question "does this account have a password set?". I need to be able
to answer that question because my OpenID implementation allows users
to associate mupltiple OpenIDs with a single account. I want to let
them delete associations as well, but prevent them from deleting the
very last association unless they have set a password for their
account (to stop them from locking themselves out).

Ivan Sagalaev

unread,
May 11, 2007, 10:24:10 AM5/11/07
to django-d...@googlegroups.com
Simon Willison wrote:
> The problem with using a random password is that you can't answer the
> question "does this account have a password set?". I need to be able
> to answer that question because my OpenID implementation allows users
> to associate mupltiple OpenIDs with a single account. I want to let
> them delete associations as well, but prevent them from deleting the
> very last association unless they have set a password for their
> account (to stop them from locking themselves out).

Got it, thanks!

Martin Winkler

unread,
May 11, 2007, 10:40:50 AM5/11/07
to django-d...@googlegroups.com
> Certainly Oracle treats them empty string as equal to NULL. But does
> that mean you can't put an empty string in a "not NULL" column in
> Oracle?

Exactly. If you want to insert something meaningless into a column that
has a NOT NULL constraint in oracle, then you have to put at least
one space character into it.
I know, it's sad. (And one of a couple things why I dislike using
Oracle)

Martin

Simon Willison

unread,
May 11, 2007, 10:51:09 AM5/11/07
to Django developers
On May 11, 3:40 pm, Martin Winkler <m...@agami.at> wrote:
> > Certainly Oracle treats them empty string as equal to NULL. But does
> > that mean you can't put an empty string in a "not NULL" column in
> > Oracle?
>
> Exactly. If you want to insert something meaningless into a column that
> has a NOT NULL constraint in oracle, then you have to put at least
> one space character into it.

So does that mean that I should store a single blank space in the
password field to represent "no password set"?

Malcolm Tredinnick

unread,
May 11, 2007, 11:07:06 AM5/11/07
to django-d...@googlegroups.com

The purists will be breaking out the pitchforks and flaming torches
( :-) ), but that would be a backwards-compatible solution, yes. All the
password types start with a "type" designator, so a single blank space
will never be a valid password entry. Or you could use the traditional
Unix password invalidator -- "!" -- which might be more mnemonic for
some people and is easier to pick out of a data dump than a space (and
will also never be a valid string, since we use '$' as a separator).

Regards,
Malcolm


Niels

unread,
May 11, 2007, 11:23:29 AM5/11/07
to Django developers
On May 11, 5:07 pm, Malcolm Tredinnick <malc...@pointy-stick.com>
wrote:

> > So does that mean that I should store a single blank space in the
> > password field to represent "no password set"?
>
> The purists will be breaking out the pitchforks and flaming torches
> ( :-) ), but that would be a backwards-compatible solution, yes. All the
> password types start with a "type" designator, so a single blank space
> will never be a valid password entry. Or you could use the traditional
> Unix password invalidator -- "!" -- which might be more mnemonic for
> some people and is easier to pick out of a data dump than a space (and
> will also never be a valid string, since we use '$' as a separator).
>

So how about "openid$" ??

Malcolm Tredinnick

unread,
May 11, 2007, 11:35:54 AM5/11/07
to django-d...@googlegroups.com

We want one "empty" value, surely. Not one per type of thing that uses
an empty value. Or am I missing a subtlety?

Malcolm


Simon Willison

unread,
May 11, 2007, 11:36:27 AM5/11/07
to Django developers
On May 11, 4:23 pm, Niels <niels.po...@gmail.com> wrote:
> > Or you could use the traditional
> > Unix password invalidator -- "!" -- which might be more mnemonic for
> > some people and is easier to pick out of a data dump than a space (and
> > will also never be a valid string, since we use '$' as a separator).
>
> So how about "openid$" ??

I think I'll go with ! (thanks Malcolm - I didn't know that piece of
Unix arcania). openid$ assumes that the only reason you would leave
the password field blank is if you were using OpenID, but there are
plenty of other reasons you might have a blank password field (using
LDAP or Yahoo! BBAuth or similar for example).

Cheesr,

Simon

Max Derkachev

unread,
May 11, 2007, 12:58:04 PM5/11/07
to Django developers
> The problem with using a random password is that you can't answer the
> question "does this account have a password set?". I need to be able
> to answer that question because my OpenID implementation allows users
> to associate mupltiple OpenIDs with a single account. I want to let
> them delete associations as well, but prevent them from deleting the
> very last association unless they have set a password for their
> account (to stop them from locking themselves out).

A slightly offtopic maybe, but...

So, the only way to know if the user is associated to an OpenID
identifier is the fact that the password is not set? Seems to be a
weak proof. If the User record can be without a password globally,
what will stop someone to register a passwordless account without
OpenID involved?
What would be the username? Does the user knows the username of the
User record associated with his OpenID set?
Why not to prevent the user from deleting the last association when
the association is actually *the last*?

BTW, I watch Your Django/OpenId progress closely because I'm building
the similar thing currently myself. We could cooperate if You're
interested.

Regards,
Max

Max Derkachev

unread,
May 11, 2007, 1:21:31 PM5/11/07
to Django developers
Sorry, I misunderstood.
The last association is only deleted when the password is set.
Got it.

Regards,
Max

buriy

unread,
May 11, 2007, 5:31:40 PM5/11/07
to Django developers

Jonathan Daugherty

unread,
May 11, 2007, 5:49:54 PM5/11/07
to django-d...@googlegroups.com
# Blog post here: http://www.buriy.com/2007/may/12/django-openid-users/
# Download here: http://www.buriy.com/media/openid-solution.zip

For anyone interested, the OpenID 2 release of our python OpenID
library[1] will include a Django app with an example consumer and
server, in addition to the example code previously provided.

[1] http://tinyurl.com/383plw

--
Jonathan Daugherty
http://www.b-tree.org

Jacob Kaplan-Moss

unread,
May 18, 2007, 12:54:35 PM5/18/07
to django-d...@googlegroups.com
On 5/10/07, Simon Willison <swil...@gmail.com> wrote:
> I propose the following changes:

I'm +1 on these changes, including using "!" as the "look somewhere
else for the password" designator.

Jacob

Marty Alchin

unread,
May 18, 2007, 1:20:03 PM5/18/07
to django-d...@googlegroups.com
On 5/18/07, Jacob Kaplan-Moss <jacob.ka...@gmail.com> wrote:
> I'm +1 on these changes, including using "!" as the "look somewhere
> else for the password" designator.

Would it really be "look somewhere else for the password" or would it
be more like "if you got this far (all other authentication methods
failed), and you got this value (no password is stored), you're out of
luck"? It may seem like a strange distinction to make, but it goes a
bit to the philosophy of it, which could affect its implementation in
general.

The way you worded it, I would expect the implementation to check try
a password first, and if it doesn't find one, it would then look at
other sources (like OpenID). This would mean that users would be
required to not have a password in order for other schemes to work.

I doubt that's the way you meant it, but it might be worth being clear
on the subject, especially if the process gets documented for the sake
of future authentication authors. I'm probably just splitting hairs
though, so disregard as necessary.

-Marty

Reply all
Reply to author
Forward
0 new messages