Lengthening Django Username - django

I'm using this snippet, that allows users to only need to enter an email address to register for my app: http://djangosnippets.org/snippets/686/
Things are almost working perfectly. The problem is when a user has an email address that's above 30 characters. I get a "Ensure this value has at most 30 characters (it has 40)." error.
This is because the username is only supposed to be 30 characters. Is there a simple way to just tell Django that the username can be longer? It seems like there should be a fairly straightforward override for this.

This actually isn't simple at all. This requires subclassing the User model and using that everywhere. I've never had to do it, for this case, but it would likely cause significant issues with the Admin interface. You could also edit django's source to pull it off (ick).
Or even use this solution:
Can django's auth_user.username be varchar(75)? How could that be done?
It's quite ugly though.
You're probably better off writing an authentication backend to use the email field for authentication rather than using the username field. To populate the username (which is required) then you'd just generate some sort of random unique username maybe by hashing or using a UUID.

Hopefully this solution should help you : http://www.micahcarrick.com/django-email-authentication.html

Related

Invalid Salt when using bcrypt.checkpw

I am working on a user authentification app using Django. The app allows the user to create an account then login. However, on the login side i am getting an error when checking for the user password using bcrypt.checkpw
I tried printing the value of the user password and the one saved on the data base. I am using SQLite for the database by the way
print(request.POST['login_password'].encode())
print(user.password)
here is the output:
b'87654321'
b'$2b$12$bQ6tEDKh.tOJnnPAj84Xe.BZnGi9kI.Sc6Q4gFPeTLw9x53VSVQOW'
I also tried:
print(request.POST['login_password'].encode())
print(user.password.encode())
b'87654321'
b"b'$2b$12$bQ6tEDKh.tOJnnPAj84Xe.BZnGi9kI.Sc6Q4gFPeTLw9x53VSVQOW'"
To creat user:
user = MasjeedUser.objects.create(first_name=request.POST['first_name'],last_name=request.POST['last_name'],password=bcrypt.hashpw(request.POST['password'].encode(), bcrypt.gensalt()),email=request.POST['email'])
to query password:
when i use
if bcrypt.checkpw(request.POST['login_password'].encode(),user.password)
i get as an error: Unicode-objects must be encoded before checking
when i tried
if bcrypt.checkpw(request.POST['login_password'].encode(),user.password.encode())
i get as an error: invalid salt
The reason you have an invalid salt is that you have an extra b'...' around the hashed password. That's because the result of hashpw is a bytestring, and Django will automatically call str on anything passed into a charfield that is not a string. You would need to decode it first.
But in any case you must not do any of this. Although bcrypt is a perfectly good hashing library, there is no reason for you to duplicate the functionality that Django gives you; by doing so, you will almost certainly introduce new security vulnerabilities. Don't do this. Inherit your MasjeedUser from the AbstractUser or AbstractBaseUser models in django.contrib.admin, and use their set_password and check_password methods.

Django rest_auth - how to add custom password validation rules

Can someone explain how to alter the default password validation rules for Django's rest_auth library?
I imagine this can be done by copying the existing serializers and adding custom validation there. (Link to rest_auth serializer documentation) I could dive into this myself, but I think this will cost me a lot of time, and it would be nice if someone can give me at least an explanation on a beginner level.
Some people may think that I am lazy to even ask such a question, but please let's leave personal opinions out of this. I am quite a beginner programmer, diving into a lot of different technologies. A bit of help here and there can safe quite some time.
Besides that I am quite sure there will be others who will be looking for this information.
On my (React) frontend I am using the following validation rules, and I would like the backend to have the same:
Password must contain at least:
- one uppercase letter (A-Z)
- one lowercase letter (a-z)
- one of the following characters: !##$&*
- one number (0-9)
Password must be at least 10 characters long
Password can not contain other characters than the ones above
In the meanwhile I found out that I can use Django's build in functionality for password validation:
https://docs.djangoproject.com/en/dev/topics/auth/passwords/#module-django.contrib.auth.password_validation
Password validators can be used / created, and pointed to in the Django settings. These validators will be used by rest_auth as well. The above link to the Django documentation explains this very well.

Integrate django_agent_trust with django_two_factor_auth

I have installed django_two_factor_auth successfully: token logins, backup tokens and SMS via Twilio all seem to work fine. My users will not tolerate having to enter their token for every login, though.
My needs are similar to those discussed in the following:
https://github.com/Bouke/django-two-factor-auth/issues/56
I wish to offer the user an option to defer OTP verification for 30 days after a successful verification.
To this end, I installed django_agent_trust. I patched AuthenticationTokenForm to add a BooleanField if django_agent_trust is installed:
(two_factor/forms.py, in AuthenticationTokenForm)
try:
from django_agent_trust import trust_agent
trust_this_agent = forms.BooleanField(label=_("Trust this browser for 30 days"),
required=False)
except:
pass
and I have been able to unconditionally set and reset the is_trusted flag by using django_agent_trust's django_agent_trust.trust_agent API.
The problem is figuring out where to capture the user's selected value of the BooleanField. I'm lost somewhere in the form wizard.
I would accept an answer questioning the wisdom of my overall approach if I think your argument makes sense. Is there something I'm missing here?
in the beginning
django_agent_trust seemed like a good shortcut for this use case. It already had secure cookie support, a feature of Django I'd never used before, plus all the convenience methods I thought I'd need.
I was able to get it working with a little extra work.
problem
The problem I ran into was that django_agent_trust validates the signed cookie only after the user is authenticated -- with an authenticated user from the request object. Since I was trying to minimize changes to django_two_factor_auth, I needed to decide whether or not to show the OTP form before authentication occurs.
solution
All the tools I needed were in django_agent_trust. I pulled the methods I needed out of its middleware and into a new utils.py, adding a 'user' argument to load_agent(). Then I was able to check the cookie against the validated-but-not-yet-logged-in user object from django_two_factor_auth's LoginView class.
Now django_two_factor_auth's LoginView can test for agent trust in has_token_step and has_backup_step, and everything works more or less as the author predicted 11 months ago...sigh.
I think adding this trust element might make sense as an enhancement to django_two_factor_auth. Juggling hacks to all these components seems like the wrong way to do it.
later
I took a cue from the django_otp project and added agent_trust as a "plugin" to two_factor. It seems usable and maybe a little easier to digest in this form. This worked for me, but I suspect there's a much better way to do it. Patches welcome.

Can I hash/encrypt or otherwise protect emails in my Django app from hackers?

Hoping for a simple function I can use to store emails securely and retrieve easily when required to send emails.
Kind of a general question, but here are a few solutions I'm familiar with:
use django-encrypted-fields, which has an EncryptedEmailField
you can override the save method for encrypting the email yourself, then override the post_init signal for decryption. See example here (which is based on this)
you can build your own encrypted email field, see django snippet here (uses pyCrypto)
you can use django-extension's EncryptedCharField
If none of the above seems good enough, try google-ing around by yourself. You're probably not the first to tackle this problem
good luck.

How secure should an account activation be?

I'm writing an account activation process from the ground up in Django, and here was my basic thought process:
Create a model like:
class UserAccountActivation(models.Model):
lock = models.CharField(max_length=16)
key = models.CharField(max_length=16)
Generate lock and key values when necessary using a function like this:
def generate_entry():
"""Generate a random alphanumeric string between 8 and 16 characters long."""
''.join(random.choice(string.ascii_lowercase + string.digits) for x in range(random.randint(8,16))
Compose a link like this:
r'^activate/(?P<lock>\w{8,16})/(?P<key>\w{8,16})/?$'
And send it out. When they hit the link, I activate the account and disable the activation key.
I was originally thinking of hashing the random strings as an extra precaution, but this seems unnecessary and it'd be pretty long to have two 32-length keys in my URL:
account/12345678/12345678
or
account/12345678901234567890123456789012/12345678901234567890123456789012
Is this a safe and recommended way of handling account activation? Is it necessary to even have the random length on the strings?
Well, to answer that question you have to consider why you have protection on your account activation. Likely it is to prevent people from guessing the activation code, so they would be able to use a false email address. As email addresses are very easy to get anyway, the activation process doesn't need to be much harder than it would take to register an email account somewhere on the web. Anything more is wasted effort, as the attacker will simply shift the attack to another weak point.
Using random strings is perfectly fine for this.
If you need more security you can consider putting a hashed account id in there, so you can count and then stop multiple failed attempts to guess the activation code.
It is a good thing to have variable length, lest it is susceptible to timing attacks.
Also, python's inbuilt random is not really cryptographicaly safe, so it is always preferable to use sha from hashlib or the system random generated via linux which you can obtain by making a sys call.