django User registration and authentication by email - django

I want to make users active by sending them an activation email to click. I guess it is currently not incorporated in Django 1.6.The user-registration app coded in Django seems to serve this purpose. But I have some doubts with regard to the DefaultForm it provides in forms.py. I want to have more fields included in it. How can I achieve that in class RegistrationForm(forms.Form) implemented there. If I install this app, is it a good idea to change include more fields directly there, is there a better way to achieve the same.
In the views.py, I see some methods such as the following are not implemented. I dont have a clear picture of what these methods need to do. should I redirect the url here to the pages?
def register(self, request, **cleaned_data):
raise NotImplementedError
def activate(self, request, *args, **kwargs):
raise NotImplementedError
def get_success_url(self, request, user):
raise NotImplementedError

You need to first let them sign up and mark them as is_active=False for the time being. Something like this:
from django.contrib.auth.models import User
from django.core.mail import send_mail
from django.http import HttpResponseRedirect
def signup(request):
# form to sign up is valid
user = User.objects.create_user('username', 'email', 'password')
user.is_active=False
user.save()
# now send them an email with a link in order to activate their user account
# you can also use an html django email template to send the email instead
# if you want
send_mail('subject', 'msg [include activation link to View here to activate account]', 'from_email', ['to_email'], fail_silently=False)
return HttpResponseRedirect('register_success_view')
Then once they click the link in the email it takes them to the next view (note: you need to put a link in the email so that you know which user it is. This may be 16-digit salt or something. The below view uses the user.pk:
def activate_view(request, pk):
user = User.objects.get(pk=pk)
user.is_active=True
user.save()
return HttpResponseRedirect('activation_success_view')
Hope that helps. Good Luck!

Basically you can use django's user model(https://docs.djangoproject.com/en/1.9/ref/contrib/auth/). However, in user model email is not a required field. You need to modify the model to make email an required field.
In your views, you might need the following method:
1) Sign up: after sign up, set user.is_active=False and call function send_email to include an activation link in the email. In the link, you might want to include the user's information (for example, user.id) so when the user click the link, you know which user to activate.
2) send_email: send a link to user's email address for verification. The link includes user's id. For example:
http://127.0.0.1:8000/activation/?id=4
3) Activate: get the id information from the URL using id=request.GET.get('id'). Query user=user whose id is id. set user.is_active=True.
Actually I implemented an reusable application like your request, check this(https://github.com/JunyiJ/django-register-activate) if you are interested.
Hope that helps. Good luck!

check this out... i Hope it helps out with not only the solution u need but also the explanation.. Because i think django-registration app is meant for default Django User. So if u want to have extra fields in your registration form, Start thinking of customizing ur Django User and Its authentication yourself. You dont need the django-registration app here..
Here are some tutorials thats will help
http://www.caktusgroup.com/blog/2013/08/07/migrating-custom-user-model-django/
and many more...

Related

Force Django User To Pick A Username After Verifying Their Email And Logging In For The First Time

I'm looking for ideas on the most elegant way to force a user to define a username immediately after they verify their email and log in for the first time. Alternatively, they click the verification email, are not logged in, and must enter a username first to be able to log in for the first time.
My requirements are that the username not be in the registration form, but instead be on its own template page immediately after the user logs in for the first time. Once they define a username, they would not see this page again.
I'm using class-based views so I think that rules out decorators.
I've researched:
User-level Permissions (can't view any page until you provide a username)
Using the is_active boolean (user is not considered active until they provide a username)
PermissionRequiredMixin (add to every class that a logged-in user could potentially see)
UserPassesTestMixin (add to every class that a logged-in user could potentially see)
AccessMixin (add to every class that a logged-in user could potentially see)
Add my own boolean field to my custom User model
In every view, check if username is null, if it is, redirect to username form page (doesn't seem like an elegant approach)
user_logged_in signal (couldn't someone still bypass the username form page)
Middleware somehow?
My Concern
User verifies email, logs in for the first time, lands on the username page. I want them to create a username on this page before being allowed to go on to any other page. I don't want them logging in, and potentially pasting a different URL in the address bar and side-stepping this step.
I'd like to avoid...
Adding validation to every single view that an authenticated user would have access to.
What I'm trying to do is similar to forcing someone to agree to a "Terms of Service" before continuing to use a website. I need them to choose a username.
Just hoping that someone experienced with this would lend some advice. I'd like to avoid a discussion of "why don't you just add the username field to the registration form". The answer is, it's not what I want.
I fully realize this question is broad and asking for suggestions, not code-specific. I usually ask detailed code-specific questions but this one, I just don't know the best way to approach. Sorry in advance.
The answer was definitely middleware. It basically looked like this. I made a new Python file named middleware.py in my app, made this class, then modified my settings.py MIDDLEWARE area to include this class.
from django.shortcuts import redirect, reverse
class SimpleMiddleware:
def __init__(self, get_response):
self.get_response = get_response
# One-time configuration and initialization.
def __call__(self, request):
# Code to be executed for each request before
# the view (and later middleware) are called.
response = self.get_response(request)
if request.user.is_authenticated:
if request.user.username is None:
print(request.path)
if not request.path == reverse('choose_username'):
return redirect(reverse('choose_username'))
return response

register user by email or mobile number in django

i have custom user model with email as unique identifier for auth instead of username. i want register a user by email or mobile number. if user enter email address, then register user by activation link, and if user enter phone number then register by SMS OTP.
something like instagram registration:
https://www.instagram.com/accounts/emailsignup/
i found this topic but the answer is not explained well.
A username is required for a User object. But, you can make that username their email too, so it's not a problem (at least in django 2.x, not sure about 1.x). You didn't describe what the app you're making was for, it's purpose, etc., so before you skip to the code, read the following warnings and thought process of why I am giving you a better option.
It's a very bad idea to force the person to have the username equal to their email, because in the future you might want to add some other functionality.
For example: Maybe you'd want to make a message board so people can talk to each other. But because of bad planning from the beginning, everyone would see each other by their email. Technically, you could asign them all a bunch of random usernames they didn't make up, but that's not a good idea cause they're less likely to remember it, and they might not like it. The only good use of a name like sPaRkLe_DaNcEr12 or Poothtaste, is for making people rage quit in video games.
So if you wanted a future ability for users to talk to each other, it would be better to only show their username to other users, but ALLOW people to log in with their email or phone number if they wanted to. This way, now they can login with their (username) or (email) or (phone_number), and they only have to remember one of those. I will show you how soon.
Some downsides of this: It makes more queries to your database, which can make it slower if you have tons of users, but that's your call. Personally, I say it's worth it because it's negligible, and easier for users, and it's them you should cater to. So pay for a faster server, or no??? Ultimately, you always ought to design around having as few queries as possible, while caching certain pages that are heavy on the database so it doesn't have to do the same thing X number of times.
Let's begin:
Remember that the following is an example of what I would do for django 2.x, with a better functionality than you're asking for. If you're using 1.x, just use url() instead of path(), and any other requirements.
Assume we have an app called accounts_app.
Also assume that we put path('accounts/', include('accounts_app.urls')), inside our project level urls.py.
I'm also going to assume you know how to use templates... Now, create a urls.py inside that app:
accounts_app/urls.py:
app_name = 'accounts_app'
urlpatterns = [
...
path('signup/', views.signup, name='signup'),
]
accounts_app/models.py:
The user attribute below enables you to extend the User model, so you can have their phone_number too. In this example, I allowed that be blank in case they don't want to give it. But if they did, you would have make a separate view for it. To make this whole thing much more simple, I'm not going to include that, nor tell you how to overwrite the whole User model. I'm only going to show you how to log in with an email with the regular User model. After that, doing so with a phone number shouldn't be hard at all. A reminder on what you will need is at the very end of this post.
class ExtendedUserExample(models.Model):
user = models.OneToOneField(settings.AUTH_USER_MODEL, on_delete=models.CASCADE)
created = models.DateTimeField(auto_now_add=True)
phone_number = models.IntegerField(blank=True)
settings.py.... NOTE THAT THIS IS IN THE PROJECT LEVEL FOLDER
The order of these backends matters. Always do ModelBackend first, or it will break.
AUTHENTICATION_BACKENDS = [
'django.contrib.auth.backends.ModelBackend',
'accounts_app.authentication.EmailAuthBackend', # to be able to login with email, described next
]
accounts_app/authentication.py:
This is an example to be able to login by their email. If you wanted someone to login by their phone number too, the principle is the same, but in this file, you would also need to import the ExtendedUserExample model above, add that to the above settings AUTHENTICATION_BACKENDS at the bottom of it, and make a new class for PhoneAuthBackend, that searches ExtendedUserExample for the phone_number. Again, to keep this more simple, I am not completely overwriting the User model, rather I only extend it, so if a user made an account and wanted to login by phone number later on, they would have to sign up with a username and email first, and once in, they could add a phone number (with this example, you will need another view for that).
So try this email example first until you get the hang of it. You also don't need to import this file anywhere else because the settings.py file takes care of it.
Here's what's happening: On your template for logging in, it will first search for the username field inside your User model, because your settings.py file has the AUTHENTICATION_BACKENDS variable to check the ModelBackend first.
But let's say a user entered their username as aaa#aaa.com. Now since you didn't allow anyone to sign up with an email as their username, when aaa#aaa.com is not found as a username in the User model, your settings file now says to go check the same User object a second time, but search their input by the email field / column instead. If their input exists in that column, authenticate() logs them in by their email if the password is right.
from django.contrib.auth.models import User
class EmailAuthBackend(object):
""" Authenticate using an email address """
def authenticate(self, request, username=None, password=None):
try:
user = User.objects.get(email=username) # gets the email by the 'username' they entered
if user.check_password(password):
return user
return None
except User.DoesNotExist:
return None
def get_user(self, user_id):
try:
return User.objects.get(pk=user_id)
except User.DoesNotExist:
return None
acounts_app/views.py:
I'll assume you know how to make django forms. If not, check here for model forms.
from .forms import ExtendedUserForm
def signup(request):
form = ExtendedUserForm(request.POST or None)
if request.method == 'POST':
if request.POST['password1'] == request.POST['password2']:
potential_user = request.POST.get('username', False).lower()
try:
user = User.objects.get(username=potential_user)
return render(request, 'accounts_app/signup.html', {'error': 'Username has already been taken. Please try another'})
except User.DoesNotExist:
if form.is_valid():
new_user = User.objects.create_user(username=potential_user, email=request.POST['email'], password=request.POST['password1'])
# backend argument required cause we are making the ability to LOGIN by email.
# Remember, I only extended the User model.
auth.login(request, new_user, backend='django.contrib.auth.backends.ModelBackend')
return redirect('some_app:some_view')
else:
return render(request, 'accounts_app/signup.html', {'error': "Password's must match."})
return render(request, 'accounts_app/signup.html', {'form': form})
Now that your login by email should be working, it's not that hard to follow these principles to create the same ability for logging in by phone. If you continue with this example, you will need to make a new view to save a phone number to ExtendedUserExample.phone_number. After that, add another line at the bottom of AUTHENTICATION_BACKENDS, write a new class in authentication.py, and you'd be set... So long as you have <input type="text" name="username" required> when they use your login view.

django-allauth - Critical login required : Allow users stored in a database only to login

I have implemented login form for username/password method, and that works perfect.
I want user to be also able to login using their social accounts.
I am using django-allauth to map social users to django-users.
Now I want to allow only those social accounts to login, that are mapped to django-users and not everyone.
Is there a way to override callback view? or something else can be done?
To simply disable registration, you have to overwrite the default account adaptor. If you also want to support social login, you also need to overwrite the default soculaaccount adapter. Add the following code somewhere in one of your apps (e.g. adapter.py):
from allauth.account.adapter import DefaultAccountAdapter
from allauth.socialaccount.adapter import DefaultSocialAccountAdapter
from allauth.exceptions import ImmediateHttpResponse
class NoNewUsersAccountAdapter(DefaultAccountAdapter):
def is_open_for_signup(self, request):
return False
class SocialAccountWhitelist(DefaultSocialAccountAdapter):
def pre_social_login(self, request, sociallogin):
u = sociallogin.user
print('User {0} is trying to login'.format(u.email))
# Write code here to check your whitelist
if not_in_your_list(u):
raise ImmediateHttpResponse(HttpResponseRedirect('/account/login'))
and then add the following to your settings:
ACCOUNT_ADAPTER = 'path_to_app.adapter.NoNewUsersAccountAdapter'
SOCIALACCOUNT_ADAPTER = 'path_to_app.adapters.SocialAccountWhitelist'
After that, all you need to do is manually create an Account from the Admin pages, and manually create an EmailAddress. For the social login, you will need to write code to somehow check if the email is allowed
I would recommend you add a Staff-Only form to make this easy on you, where you can ask for username, email (and even password) and then do
new_user = Account.objects.create_user(email=email, username=username, password=password)
EmailAddress.objects.create(email=email, user=new_user, verified=True, primary=True)
You can also develop an Invitation scheme, but that is a lot more complicated but quickly googled and found the following project, which I have not personally used, but looks like what you need:
https://github.com/bee-keeper/django-invitations
Finally After reading the documents thoroughly and doing a lot of trials and errors I got to what I was looking for.
I had to set following parameters as a part of configuration specified in docs.
ACCOUNT_EMAIL_REQUIRED (=False)
The user is required to hand over an e-mail address when signing up.
and
SOCIALACCOUNT_QUERY_EMAIL (=ACCOUNT_EMAIL_REQUIRED)
Request e-mail address from 3rd party account provider? E.g. using OpenID AX, or the Facebook “email” permission.
I had to set ACCOUNT_EMAIL_REQUIRED = True as it was required to check if that email id is already registerd with us.
and then finally I overridden pre_social_login like below.
from allauth.socialaccount.adapter import DefaultSocialAccountAdapter
class NoNewSocialLogin(DefaultSocialAccountAdapter):
def pre_social_login(self, request, sociallogin):
try:
cr_user = auth_user.objects.get(email=sociallogin.user.email)
if cr_user and cr_user.is_active:
user_login = login(request, cr_user, 'django.contrib.auth.backends.ModelBackend')
raise ImmediateHttpResponse(HttpResponseRedirect(reverse('protect')))
else:
raise ImmediateHttpResponse(render_to_response("account/authentication_error.html"))
except ObjectDoesNotExist as e:
raise ImmediateHttpResponse(render_to_response("socialaccount/authentication_error.html"))
except Exception as e:
raise ImmediateHttpResponse(HttpResponseRedirect(reverse('protect')))

How to get notified when a user changes password or requests a password reset?

For a password change I am using auth_views.password_change and for the password reset auth_views.password_reset.
How can I be notified when a user successfully changes their password? I do not need to know old nor new password. Just that the event took place, and for which user.
Similarly, I would like to get notified when someone requested a password reset and also when they successfully completed the reset procedure.
Can i do the above with signals or some simple patching? Or do I need to write my own views to do this?
Create a decorator:
def notify_admins(func):
def wrapper(request, *args, **kwargs):
# send email to admins
return func(request, *args, **kwargs)
return wrapper
Then, just add wrap it around the appropriate views in your urls.py:
urlpatterns = patterns('',
...
(r'^password_change/done/$', notify_admins(auth_views.password_change_done)),
(r'^password_reset/done/$', notify_admins(auth_views.password_reset_done)),
(r'^reset/done/$', notify_admins(auth_views.password_reset_complete)),
...
)
Keep in mind that sending email directly from a view, or in this case a decorator, will tie up the request. Instead of sending the email there directly, it would be better to create a custom signal and a handler that will fire off a thread to actually send the email. Then, in the decorator, you simply send the signal.
You could write a custom password_change_form that you pass to password_change. This form would extend django's PasswordChangeForm overriding its save method to first notify you of the change and then call it's parent PasswordChangeForms save method.
Docs on password_change view:
https://docs.djangoproject.com/en/dev/topics/auth/#django.contrib.auth.views.password_change
Docs on ChangeForm:
https://docs.djangoproject.com/en/dev/topics/auth/#django.contrib.auth.forms.PasswordChangeForm
Code for PasswordChangeForm:
https://code.djangoproject.com/browser/django/trunk/django/contrib/auth/forms.py
Beginning in Django 1.9, you can define your define your own password validators. You could even simply re-define an existing one, if you like. When you do, add a method:
from django.contrib.auth.password_validation import MinimumLengthValidator
class MyPasswordValidator(MinimumLengthValidator):
def password_changed(self, password, user):
# put your password changed logic here
Be sure to include your new class in your settings as follows:
AUTH_PASSWORD_VALIDATORS = [
{
'NAME': 'my_package.password_validators.MyPasswordValidator',
'OPTIONS': {
'min_length': 8,
}
},
...
]
Now, each time a password is changed by the user, your class MyPasswordValidator will be notified. In my experience, this is the best way to do this because:
When using signals to capture these events, you will also capture events where the system re-encoded an existing password due to a change in hashing parameters, in most cases, you would not want to capture these events and there's no obvious way to prevent it with signals.
You could simple add a function-call in the save() method of all your password-handling forms, but this becomes difficult when you want to do the same with the built in admin change password form and will not help you if password changes are made programmatically outside of a form.
I will caution you to be aware that the password parameter in password_changed() is in fact the user's raw password. Take care when handling this and absolutely never store this anywhere unencrypted/unhashed.
If you are already using the auth_views.password_change built in view, then it would be easy to notify yourself once they are redirected after a successful change:
https://docs.djangoproject.com/en/dev/topics/auth/#django.contrib.auth.views.password_change
password_change(request[, template_name, post_change_redirect, password_change_form])
If you set the post_change_redirect url to redirect to one of your own views, then you simply take whatever action you want in that view to send a notification (email, database updates, etc).
You could even, in your redirect view, just do your notification and then return password_change_done(request[, template_name])
You could also capture the signal and check if the password has changed. Just keep in mind that this code will run every time a user changes.
#receiver(pre_save, sender=User)
def record_password_change(sender, **kwargs):
user = kwargs.get('instance', None)
if user:
new_password = user.password
try:
old_password = User.objects.get(pk=user.pk).password
except User.DoesNotExist:
old_password = None
if new_password != old_password:
# do what you need here

need an example of doing authorization using django-tastypie

I am relatively new with Django and it's ecosystem. I am writing REST api for our mobile client using django-tastypie. I have gone through almost all the examples on the web about how to use tastypie for creating REST interfaces. but none of them are specific to POSTing the data from client and how would you authorize a client.
I used the from tastypie.authentication.BasicAuthentication as show in the example. It opens a pop up asking username and password and works fine on the browser. But I am not sure, if it will do the same thing on mobile (to be specific, native IOS app). I am not quite getting when a user will make a request to login how this popup will be shown there on his/her mobile device if he or she is not using the browser but the native app.
I am totally lost on this, I would really appreciate your help.
You can check out source and use for example ApiKeyAuthentication.
You just have to POST username and api key to authentificate user.
It looks like usable for ios app.
Here is the part of the checking code.
def is_authenticated(self, request, **kwargs):
"""
Finds the user and checks their API key.
Should return either ``True`` if allowed, ``False`` if not or an
``HttpResponse`` if you need something custom.
"""
from django.contrib.auth.models import User
username = request.GET.get('username') or request.POST.get('username')
api_key = request.GET.get('api_key') or request.POST.get('api_key')
if not username or not api_key:
return self._unauthorized()
try:
user = User.objects.get(username=username)
except (User.DoesNotExist, User.MultipleObjectsReturned):
return self._unauthorized()
request.user = user
return self.get_key(user, api_key)
https://github.com/toastdriven/django-tastypie/blob/master/tastypie/authentication.py#L128
https://github.com/toastdriven/django-tastypie/blob/master/tastypie/authorization.py#L42
Thanks for the help.
I used similar approach mentioned by #Iurii. Here is my solution.
I wrote a class for handling the authentication and override is_authenticated method. and then I can use this class in Meta definition of tastypie resource classes.
from tastypie.authentication import BasicAuthentication
from tastypie.resources import Resource, ModelResource
# class for handling authentication
class MyAuthentication(BasicAuthentication):
def is_authenticated(self, request, **kwargs):
# put here the logic to check username and password from request object
# if the user is authenticated then return True otherwise return False
# tastypie resource class
class MyResource(ModelResource):
class Meta:
authentication = MyAuthentication()
this will ensure a request to access the resource will go through your authentication code.