I'm looking for a way in which I can send out a user an email with a url that will log them into their user. This doesn't even necessarily have to expire. I have found tons of material on sessions however this is not what I'm after. What I need is this:
User submits something to the website
They have an account made in the background
An email is sent out containing a link "site.com/&token=foobar23124123"
The user can log in using that link (optional: for the next week)
Is there something out there that I'm missing that would help me or would I have to implement my own solution? Could I potentially just include the token from Django REST framework's Tokens?
Thank you for taking the time to read my question.
I'm Using Django 1.9 and Python 2.7
I don't think there is something for authenticating users using url get-parameters. AFAIK Django REST framework's Tokens uses HTTP headers for tokens.
You can write your own auth backend, it's quite easy. Here is an example
myproject/setting.py
AUTHENTICATION_BACKENDS = [
'myproject.backends.UrlTokenBackend',
'django.contrib.auth.backends.ModelBackend'
]
myproject/backends.py
class UrlTokenBackend(ModelBackend):
def authenticate(self, token):
try:
user = User.objects.get(token=token)
except User.DoesNotExist:
return None
if not user.is_active:
return None
return user
def get_user(self, user_id):
try:
return User.objects.get(pk=user_id)
except User.DoesNotExist:
return None
Now when you will call authenticate and login function Django will check your user against each of your backends. You can manually login user like this (this is view function):
from django.contrib.auth import authenticate, login
def user_auth(request):
token = request.GET.get('token')
user = authenticate(token=token)
login(request, user)
return redirect('index')
Update
Or you can use this hack and do only this (without custom backend):
def user_auth(request):
token = request.GET.get('token')
user = User.objects.get(token=token)
user.backend = 'django.contrib.auth.backends.ModelBackend'
login(request, user)
return redirect('index')
Related
I am trying to use django's inbuilt authentication system for my application.
I'm able to authenticate a user during login but, for the following requests request.user.is_authenticated is coming as False
login view:
user_name = request_body['username']
password = request_body['password']
user = authenticate(username=user_name, password=password)
if user is not None:
login(request, user)
request.session.set_expiry(86940)
I've written a decorator to check authentication, the code is:
from django.http import HttpResponse
def authenticate_request(f):
def check(request):
if request.user.is_authenticated: #coming as false even after authenication is done
return f(request)
else:
return HttpResponse(status=401)
return check
What am I missing while authenticating the request?
Check your current SESSION_COOKIE_AGE and make sure that you dont overwrite it somewhere else in you program. Make sure that you dont immediately get logged out. The default value of SESSION_COOKIE_AGE is 1209600 (2 weeks, in seconds).
In Django default user authentication is integrated through Username and Password. In my project profile page, I have an option to change Username. So, it is necessary to change my authentication system in back end and front end with email and password.
Using authentication backend i can change default authentication system through email and password in admin. Here is the code -
class EmailBackend(object):
def authenticate(self, username=None, password=None, **kwargs):
UserModel = get_user_model()
try:
user = UserModel.objects.get(email=username)
except UserModel.DoesNotExist:
return None
else:
if getattr(user, 'is_active', False) and user.check_password(password):
return user
return None
def get_user(self, user_id):
try:
return User.objects.get(pk=user_id)
except User.DoesNotExist:
return None
In settings.py -
AUTHENTICATION_BACKENDS = (
'apps.account.email-auth.EmailBackend',
)
I was wondering how could i do the authentication through in front end. Already, I prepared front login page through email and password.
But see form.errors and predict must be missing any front authentication like AUTHENTICATION_BACKENDS
Thank you very much for your help!
Actually answer is within the method -
def authenticate(self, username=None, password=None, **kwargs):
In front-end, naming the user input with email wouldn't be passed within the method then form errors show up with don't match credentials. So, simply taking input as username solves the trick.
There is a package django-allauth. It handles the authentication.
It allows using 'email' and 'password' or 'username' and 'password' for authentication. It includes forms and everything else needed.
Django-cookiecutter project template uses this package to handle authentication, so you can look there and use it as a sample.
I know there is documentation for this question here https://docs.djangoproject.com/en/1.9/topics/auth/customizing/, however I need some clarification on my use case.
I basically have an api that I created. What this api does is submit user form data to an external url, and once submitted, the data is saved to that external urls database.
I can then get a specific user through another api call. The user is returned as an object. Now I am trying to log that user in on my end using the django.contrib.auth through my system but what I have isn't working.
Here is my process:
A. I added a custom authentication backend to my settings.py file
AUTHENTICATION_BACKENDS =(
'new_app.backends.SettingsBackend',
'django.contrib.auth.backends.ModelBackend',
)
B. In my new_app's backend file, I have the following code:
class SettingsBackend(object):
def authenticate(self, username=None, password=None):
if username and password:
user = api.get_user()
#add pk to user object to satisfy django error warning
user.pk = user.unique_field
return user
return None
def get_user(self, user_id):
try:
if self.user.pk == user_id:
return self.user
except:
return None
In a signin view, I am trying to log the user in using this:
def login_user(request, username, password):
from django.contrib import auth
if not username and password:
raise ValueError("Invalid username and password")
auth_user = auth.authenticate(username=username, password=password)
if auth_user is None:
raise ValueError("Invalid username and password")
auth.login(request, auth_user)
return auth_user
I get the error saying:
'api.get_user()' object has no attribute 'save'
The error is occuring on the auth.login(request, auth_user) line.
I think the reason is because the django.contrib.auth thinks my user object is a django model. So, the question is how should I tackle this to make it work given my scenario? Thanks for the time.
I'm not an expert at django subtleties (as it's been a while I left django for microframeworks).
That said, if I were you, what I'd do is to make sure that whatever api.get_user() is returning, implement whatever API django is expecting from that object, which means starting with an empty save() method on it.
But I'd also do standard django stuff, like #RA123 suggests in a comment, by setting AUTH_USER_MODEL up.
In my django app, I have written a custom authentication backend following the example in the docs that appears to be working fine. (I email users a login url containing a signed token; the view below handles these token login urls.) I can confirm from the log messages that request.user is being set correctly.
However, the profile view, protected by the #login_required decorator, redirects to the login page as if the user were not logged in. It seems like the call to login is not working. Am I overlooking something? I'm not sure how to debug this further. (As this is my first django app, I decided to write my own login view, and that's working fine...) Thanks!
From views.py:
def token_login(request, token):
user = authenticate(token=token)
if user:
log.info("LOGGING IN {}".format(user))
login(request, user)
log.info(request.user)
return redirect('myapp:profile')
else:
return render(request, 'myapp/error.html', {
'title': 'Invalid Token',
'message': 'Sorry, this is not a valid login token.'
})
Custom Authentication Backend:
class TokenBackend:
"""
Authenticates against a signed token; creates a new user if none existed before.
"""
def get_user(self, username):
try:
User.objects.get(pk=username)
except User.DoesNotExist:
return None
def authenticate(self, token):
log.info("AUTHENTICATING WITH TOKENBACKEND")
try:
token_contents = signing.loads(token, salt=settings.SALT,
max_age=settings.PASSWORD_TOKEN_MAX_AGE)
log.info(token_contents)
except signing.BadSignature:
return None
try:
user = User.objects.get(email=token_contents['email'])
except User.DoesNotExist:
user = User(
username=self.username_from_email(token_contents['email']),
password=self.random_password()
)
user.save()
return user
def random_password(self):
return ''.join(random.choice(string.ascii_uppercase + string.digits)
for _ in range(10))
def username_from_email(self, email):
return email.split('#')[0]
from settings.py:
AUTHENTICATION_BACKENDS = (
'myapp.auth.TokenBackend',
'django.contrib.auth.backends.ModelBackend'
)
Oops. I wasn't returning the User in TokenBackend.get_user.
Hay I was wondering if anyone knew a better way to do this.
def login_user(request):
username = request.POST.get('username')
password = request.POST.get('password')
user = User.objects.filter(username=username)
if user:
user = user[0]
if user.password == generate_password(password):
return HttpResponse("password fine")
else:
return HttpResponse("password incorrect")
else:
return HttpResponse("no user found by that username")
and the generate_password function is just
generate_password(string):
return hashlib.sha224(str(string)).hexdigest()
Any ideas would be great.
Thanks
Why don't use Django auth default views ?
the only amelioration i see is use get instead of filter (it will save you one line)
user = User.objects.get(username=username)
Looking at the level of control you want to have, you'll want to make use of the authenticate and maybe login functions in django.contrib.auth. These are the main functions for accessing authentication. I should stress that you really, really should use these instead of finding the user and checking the password hash manually. There are a number of reasons why:
they will make use of whatever authentication backend you or someone else in the future have installed
your version will be far less tested than Django's and is more likely to open security holes
it's quicker, shorter, more flexible and more readable
Django's auth app will probably change in the near future, sticking to authenticate will help you migrate to the new auth app when it gets written/committed/released
If you do want to rewrite the way a user is found and authenticated, write your own Authenticate backend, which will be used when you call authentication (even in someone else's app, like the admin). This is the only place you should rewrite authentication in Django.
The following examples are from the Django auth docs.
1. Checking a user's password:
from django.contrib.auth import authenticate
user = authenticate(username='john', password='secret')
if user is not None:
if user.is_active:
print "You provided a correct username and password!"
else:
print "Your account has been disabled!"
else:
print "Your username and password were incorrect."
2. Custom authentication backend:
Here is a backend that uses the same authenticate method as Django's, which you can find at django.contrib.auth.backends.ModelBackend:
from django.contrib.auth.models import User
class MyBackend:
def authenticate(self, username=None, password=None):
try:
user = User.objects.get(username=username)
if user.check_password(password):
return user
except User.DoesNotExist:
return None
def get_user(self, user_id):
try:
return User.objects.get(pk=user_id)
except User.DoesNotExist:
return None
You should download django-registration and go through the code. It manages everything for you, including cleaning the code. Your original code will not handle empty submissions.
http://bitbucket.org/ubernostrum/django-registration/