Logging in user through Django rest framework - django

I'm trying to log in a user through DRF view.
#list_route(methods=['post'])
def login(self, request, *args, **kwargs):
login_form = LoginForm(request, data=request.data or None)
if login_form.is_valid():
_login(request._request, login_form.get_user())
else:
raise forms.ValidationError("login wrong")
return Response({})
Above code runs without exception, but it doesn't seem to actually log in the user.
When I refresh the browser, the user is still not logged in.
How can I log in the user?

Please don't use DRF to log people in with session.
The warning a few lines after the SessionAuthentication gives you more details about it.

Seems that the _login() method doesn't do what you expect.
As you mention in your your comment. you were using:
from django.contrib.auth.views import login as _login
but this method:
Displays the login form and handles the login action.
You should (as you do) use:
from django.contrib.auth import login as _login
which:
Persist a user id and a backend in the request. This way a user doesn't
have to reauthenticate on every request. Note that data set during
the anonymous session is retained when the user logs in.

Related

how to check whether a user is logged in in django?

I know about request.user.is_authenticated() but i created a custom user(model) and created login page for it
now how can we know if the user is logged in?
In views.py once the user logs in redirect to a url that calls a view
#login_required()
def login_success(request):
and render a welcome message

Calling a method when the user login Django

On my Django site, I want to create an object when the user login the site. I searched it on the internet and I decided to write a method in a context_processors.py. so I wrote;
def check_online_status(request):
user_status = None
if request.user.is_authenticated():
user_status = UserStatus.objects.create(user_id=request.user.id)
user_status.status_type = "online"
user_status.save()
return {
'user_status': user_status,
}
Here it is the problem; my check_online_status() method is triggered in every request but I want to trigger my method at once, only when the user login.
Can you help me to do it?
I think what you're looking for is a signal, specifically an auth signal. These are already provided in django.
https://docs.djangoproject.com/en/dev/ref/contrib/auth/#module-django.contrib.auth.signals
from django.contrib.auth.signals import user_logged_in
def set_status_online(sender, user, request, **kwargs):
user_status = UserStatus.objects.create(user_id=request.user.id)
user_status.status_type = "online"
user_status.save()
user_logged_in.connect(set_status_online)

python-social-auth, logout without disconnect

Upon logout, wouldn't it be natural to remove the access tokens for social login?
(User will be able to login with different social account next time he logs in)
How do I do it with python-social-auth?
https://python-social-auth.readthedocs.org/en/latest/pipeline.html#disconnection-pipeline talks about disconnecting and I guess it is close to closing the account than to logout
Logout with django-social-auth asks the same question, but answers don't actually address his question.
Just use the default django logout for this, even for login if you see the code inside, it ask the data from OP and create adjango login from that data. and store all the info in socialauth user.
And also python-social-auth have class for socialauth user and storage at backend, which store manythings, so for reference you can check the models.py and storage.py inside social_django.
In my current project which use django and social-auth-app-django of python-social-auth, i am usein gthe default django logout,
from django.contrib.auth import logout as auth_logout
class logout(TemplateView):
next_page = settings.LOGOUT_REDIRECT_URL
redirect_field_name = REDIRECT_FIELD_NAME
template_name = None
extra_context = None
#method_decorator(never_cache)
def dispatch(self, request, *args, **kwargs):
auth_logout(request)
next_page = settings.LOGOUT_REDIRECT_URL
if next_page:
return HttpResponseRedirect(next_page)
return super().dispatch(request, *args, **kwargs)

Test that user was logged in successfully

How can I test that a user is logged in after submitting the registration form?
I tried the following but it returns True even before I added the login logic to my registration view.
def test_that_user_gets_logged_in(self):
response = self.client.post(reverse('auth-registration'),
{ 'username':'foo',
'password1':'bar',
'password2':'bar' } )
user = User.objects.get(username='foo')
assert user.is_authenticated()
The code that's being tested:
class RegistrationView(CreateView):
template_name = 'auth/registration.html'
form_class = UserCreationForm
success_url = '/'
def auth_login(self, request, username, password):
'''
Authenticate always needs to be called before login because it
adds which backend did the authentication which is required by login.
'''
user = authenticate(username=username, password=password)
login(request, user)
def form_valid(self, form):
'''
Overwrite form_valid to login.
'''
#save the user
response = super(RegistrationView, self).form_valid(form)
#Get the user creditials
username = form.cleaned_data['username']
password = form.cleaned_data['password1']
#authenticate and login
self.auth_login(self.request, username, password)
return response
You can use the get_user method of the auth module. It says it wants a request as parameter, but it only ever uses the session attribute of the request. And it just so happens that our Client has that attribute.
from django.contrib import auth
user = auth.get_user(self.client)
assert user.is_authenticated
This is not the best answer. See https://stackoverflow.com/a/35871564/307511
Chronial has given
an excellent example on how to make this assertion below. His answer
better than mine for nowadays code.
The most straightforward method to test if a user is logged in is by testing the Client object:
self.assertIn('_auth_user_id', self.client.session)
You could also check if a specific user is logged in:
self.assertEqual(int(self.client.session['_auth_user_id']), user.pk)
As an additional info, the response.request object is not a HttpRequest object; instead, it's an ordinary dict with some info about the actual request, so it won't have the user attribute anyway.
Also, testing the response.context object is not safe because you don't aways have a context.
Django's TestClient has a login method which returns True if the user was successfully logged in.
The method is_authenticated() on the User model always returns True. False is returned for request.user.is_authenticated() in the case that request.user is an instance of AnonymousUser, which is_authenticated() method always returns False.
While testing you can have a look at response.context['request'].user.is_authenticated().
You can also try to access another page in test which requires to be logged in, and see if response.status returns 200 or 302 (redirect from login_required).
Where are you initialising your self.client? What else is in your setUp method? I have a similar test and your code should work fine. Here's how I do it:
from django.contrib.auth.models import User
from django.test import TestCase
from django.test.client import Client
class UserTestCase(TestCase):
def setUp(self):
self.client = Client()
def testLogin(self):
print User.objects.all() # returns []
response = self.client.post(reverse('auth-registration'),
{ 'username':'foo',
'password1':'bar',
'password2':'bar' } )
print User.objects.all() # returns one user
print User.objects.all()[0].is_authenticated() # returns True
EDIT
If I comment out my login logic, I don't get any User after self.client.post(. If you really want to check if the user has been authenticated, use the self.client to access another url which requires user authentication. Continuing from the above, access another page:
response = self.client.get(reverse('another-page-which-requires-authentication'))
print response.status_code
The above should return 200 to confirm that the user has authenticated. Anything else, it will redirect to the login page with a 302 code.
There is another succinct way, using wsgi_request in response:
response = self.client.post('/signup', data)
assert response.wsgi_request.user.is_authenticated()
and #Chronial 's manner is also available with wsgi_request:
from django.contrib import auth
user = auth.get_user(response.wsgi_request)
assert user.is_authenticated()
Because response.wsgi_request object has a session attribute.
However, I think using response.wsgi_request.user is more simple.

Django Expired Session Message API

I'm currently using SESSION_COOKIE_AGE = 60*60 to expire a Django session in 1 hour. I need to give a message to the user saying their session has expired on the login page.
Is there a way to test if the session has been expired? Or is there a message api for expired sessions in Django?
I poked around and didn't see anything for setting an expired session message.
Thanks!
To display the message that session is expired you can check if session exists in your logout view and change the success message accordingly
class Logout(View):
def get(self, request):
if request.session:
messages.success(request, 'Successfully Logged Out')
else:
messages.error(request, 'Session Expired Please Login Again')
logout(request)
return redirect(reverse('login'))
The warning typically provided to a user is an invitation to login :-).
What you could do is check SESSION_COOKIE_AGE (which provides the age of the cookie in seconds) and, if the user's session is about to expire, provide a warning to that effect.
I encouraged same problem and solved shown as below:
Django redirecting to LOGIN_URL after session expired.
So I pointed login url to logout view in settings.py for show message to user
and redirect to our login view.
settings.py:
LOGIN_URL = reverse_lazy('account:logout')
views.py:
class LogoutView(RedirectView):
url = reverse_lazy('account:login') # Our login view
def get(self, request, **kwargs):
# If session expired django clean request.session object.
# If user came to this view by clicking to logout button request.session not comes empty
if request.session.is_empty():
messages.error(request, "Your session has expired. Please login again to continue checking out.")
logout(request)
if request.GET.get('next'):
self.url = '{}?next={}'.format(self.url, request.GET.get('next'))
return super(LogoutView, self).get(request, **kwargs)