Django rest-framework does not store authenticated user between calls - python

For the project I'm currently working on, I decided to write a custom authentication model. I'm using both the rest-framework and all-access. The local authentication is not implemented yet (it is, but in a different version, so no issue there).
The User model registers properly and OAuth authentication works. Users get logged alright.
When working with views, authentication works properly if I use django's View as base class. Not so much if I use rest-framework's APIView.
In my ProfileView (LOGIN_REDIRECT_URL redirects here) I receice a rest_framework.request.Request instance, which is OK by me. The problem is:
request.user is an instance of AnonymousUser (even after login)
I would guess the problem lies with my implementation, but then:
request._request.user is an authenticated user
(Note that request._request is the original django request)
I tend to think this is a rest-framework bug, but maybe one of you knows of a workaround?
Edit
Just to note that the login is made in a non-APIView class, but instead in a class that inherits from View. That's probably the reason the User instance is not passed to the APIView instance.

You need to set the DEFAULT_AUTHENTICATION_CLASSES in your settings.py. Else DRF will default to None as request.auth and AnonymousUser as request.user
REST_FRAMEWORK = {
'DEFAULT_AUTHENTICATION_CLASSES': (
'rest_framework.authentication.BasicAuthentication',
'rest_framework.authentication.SessionAuthentication',
)
}
Once you set that and the user is logged in, ensure that the session_id is available in the request headers. Browsers typically store the session information and pass it in the headers of all requests.
If you are using a non-browser client (Eg. a mobile app), you will need to set the session id manually in the header.
Also since you mentioned you use oauth to sign-in the user, for oauth2 you need to specify the oauth2 authorization header instead of the session id.
"Authorization: Bearer <your-access-token>"
Refer to Authenticating in Django Rest Framework

Related

Directly authenticating a user when hitting django admin login page

I have a reactjs app that already has a user logged in. I attached a link to the web app that make the user able to access Django admin page, but for now it still requires the user to login.
I'd like to bypass the login as the user is already authenticated when logging into the react app.
How do I bypass the log in page and tell django that this user is already authenticated? What if I still want to get the email from request? where can I access the request object?
EDIT:
I should specify that I would like to check for auth token which I already have in my localStorage, then authenticate the external user directly. If the auth token is not present, I should still hit the django admin login page
EDIT2:
Created a custom page just to deal with Auth0 authentication. But I'm not sure what to do next. The request.user at this point is AnonymousUser which I can't really operate on. There is no way to identify who this is (but I can successfully check if this user has permission)
I plan to create a user and give it superuser permission? Is that the right approach?
EDIT3:
login(request, request.user, backend='django.contrib.auth.backends.RemoteUserBackend')
return HttpResponseRedirect("/my/url")
and i got
'AnonymousUser' object has no attribute '_meta'
Is it part of the auth problem?
You should not "bypass the login" you need to use authorized tokens... to identify that client whos is consuming the API is really you and not the anyone else
The process is really simple, once you send username and password to your backend (django) you will retorn one autorization token to your frontend (react) and every request from your frontend you will add it to header
Use django_rest_framework or something like that (as tastypie)
http://www.django-rest-framework.org/api-guide/authentication/

Authentication with Django channels using Tokens - How do I use this mixin

My website hosts a lot of APIs(CRUDs) using DJR. I am using DJR Token based authentication and for testing I would add this header to postman
Key : Authorization
value : Token 826fdf3067b07afdf9edd89a6c9facd9920de8b8
and Django Rest Framework would easily be able to authenticate the user.
Now I inlcuded Django channels constantly 1.1.5 and wanted to know how I could do token based authentication. I read this post and it suggests that I copy this mixin to the project. I just started with Django-channels and am not sure how to include that mixin to my code. Currently I have something like this
#channel_session_user_from_http
def ws_connect(message):
user = message.user
message.reply_channel.send({"accept": True}) #Send back Acceptance response.
#channel_session_user
def chat_join(message):
user = message.user #Get authenticated user
I have the following two questions
1-How do I include that mixin in my current project ? I know you include mixins in classes using class classname(SomeMixin). How would I go about including this mixin into my code ?
2-Will I need to include an authentication token in my json message that I send to the websocket ?
Any suggestions would be great.
`

In Django, after a login how can I detect which auth backend authenticated the user?

I'm trying to distinguish between a couple of Django authentication backends (which are external packages and I preferably don't want to modify them) in my views. django.contrib.auth docs says auth backends (settings.AUTHENTICATION_BACKENDS) will be tried in order and the first that does authenticate, will return and set request.user and if any raises an exception, authentication is refused. But it does not say how can I distinguish between requests depending on which backend has authenticated the user.
Is this possible? and how?
As explained in Django docs for authentication backends settings:
Once a user has authenticated, Django stores which backend was used to authenticate the user in the user’s session, and re-uses the same backend for the duration of that session whenever access to the currently authenticated user is needed. This effectively means that authentication sources are cached on a per-session basis
Actually, this information is stored when function login(request, user, backend=None) is used (see django.contrib.auth.__init__.py). After user has been authenticated, following session information are stored:
SESSION_KEY = '_auth_user_id'
BACKEND_SESSION_KEY = '_auth_user_backend'
HASH_SESSION_KEY = '_auth_user_hash'
# [...]
request.session[SESSION_KEY] = user._meta.pk.value_to_string(user)
request.session[BACKEND_SESSION_KEY] = backend
request.session[HASH_SESSION_KEY] = session_auth_hash
So, you should check current request's session for key BACKEND_SESSION_KEY to find the backend used to authenticate user.

How do I create a login API using Django Rest Framework?

I want to create a login api (or use an existing one if it is already pre-bundled) using django rest framework. However, I'm completely at a loss. Whenever I send a post request to the django rest framework "login" url, it just sends back the browsable api template page...
MY CONFIGURATION
urls.py
url(r'^api/v1/', include('rest_framework.urls', namespace='rest_framework'))
settings.py
REST_FRAMEWORK = {
'DEFAULT_AUTHENTICATION_CLASSES': (
'rest_framework.authentication.BasicAuthentication',
'rest_framework.authentication.SessionAuthentication',
)
}
WHAT I WANT
Request:
POST /api/v1/login username='name' pass='pass'
Response:
200 OK "{username: 'name', 'userId': '54321'}" set-cookie: sessionid="blahblah"
Take a look at the api view from django-rest-framework-jwt. It's an implementation for creating auth tokens rather than cookie sessions, but your implementation will be similar. See views.py and serializers.py. You can probably use the serializers.py unchanged, and just adjust your views to return the right parameters and possibly set the session cookie (can't recall if that's already performed in authentication).
If you want something like this I do the same thing however I use Token authentication.
Check out their token page here
This may not be what you want but the way I do it is (since I'm using it as a rest api endpoints for mobile clients)
I can do my url localhost:8000/api/users/ -H Authorization : Token
A browser could then use the regular login page that you create at the provided rest framework url
url(r'^api-auth/', include('rest_framework.urls', namespace='rest_framework')
and to get tokens for 'login-less' navigation
url(r'^api-token-auth/', 'rest_framework.authtoken.views.obtain_auth_token')
Then if you make calls and such you can pass the authorization tokens. Of course this is just how I do it and it's probably not the most efficient way but my goal was to create a way that I can provide users with session authentication for browsers and mobile access via tokens.
Then in your views.py make sure you add the authentication requirements for that view. Almost the same as session authentication section
permission_classes = (permissions.IsAdminUser,)
but also include
authentication_classes = (authentication.TokenAuthentication,)
I hope this helps but if not, good luck on your search.
Of course token is a good way to authenticate, but questioner is asking about session authentication.
Request:
POST /api/v1/login username='username' password='password'
Put csrftoken value at X-CSRFToken in header
Even though someone using email as username filed, username name parameter is required for email input (e.g. username='sample#domain.com')
Response:
302 FOUND sessionid="blahblah"
If you not specified next value, it will automatically redirect into /accounts/profile/ which can yield 404 error
Adding our views:
from rest_framework_jwt.views import refresh_jwt_token
urlpatterns = [
...
url(r'^rest-auth/', include('rest_auth.urls')),
url(r'^rest-auth/registration/', include('rest_auth.registration.urls')),
...
url(r'^refresh-token/', refresh_jwt_token),
]

Does SessionAuthentication work in Tastypie for HTTP POST?

I am able to do GET to work with SessionAuthentication and Tastypie without setting any headers except for content-type to application/json. HTTP POST however just fails even though the Cookie in the Header has the session id. It fails with a 401 AuthorizationHeader but it has nothing to do with Authorization. Changing SessionAuthentication to BasicAuthentication and passing username/password works too.
Has anyone ever got SessionAuthentication to work with POST with Tastypie?
Yes I have gotten it to work. All you need to do is to pass the csfr token:
SessionAuthentication
This authentication scheme uses the built-in
Django sessions to check if a user is logged. This is typically useful
when used by Javascript on the same site as the API is hosted on.
It requires that the user has logged in & has an active session. They
also must have a valid CSRF token.
This is how you do that in jQuery:
// sending a csrftoken with every ajax request
function csrfSafeMethod(method) {
// these HTTP methods do not require CSRF protection
return (/^(GET|HEAD|OPTIONS|TRACE)$/.test(method));
}
$.ajaxSetup({
crossDomain: false, // obviates need for sameOrigin test
beforeSend: function(xhr, settings) {
if (!csrfSafeMethod(settings.type)) {
xhr.setRequestHeader("X-CSRFToken", $.cookie('csrftoken'));
}
}
});
$.ajax({
type: "POST",
// ...
Notice the part that says $.cookie('csrftoken'). It gets the csrf token from a cookie that Django sets.
Update:
I had some problems with Django not setting the cookie on Firefox and Opera. Putting the template tag {% csrf_token %} in your template solves this. The right solution would probably be to use the decorator ensure_csrf_cookie().
Here are some additions to Dan's answer. Please, correct me if something is wrong, I am still a bit confused about it myself.
Before we continue, read about CSRF protection in Django. Read it carefully. You need to put the token from the cookie into the header X-CSRFToken. This will not work if the cookie is Httponly, that is, if you have set CSRF-COOKIE-HTTPONLY = True in settings.py. Then you have to embed the cookie in the document which, of course, creates further vulnerabilities and reduces protection the gained by using Httponly.
As far as I can tell, if the cookie is not Httponly, jQuery sets X-CSRFToken automatically. Correct me if I am wrong, but I've just spent several hours playing with it, and this is what I am consistently getting. This makes me wonder, what is the point of the advice in Django documentation? Is it a new feature in jQuery?
Further discussion:
Tastypie disables CSRF protection except with Session Authentication, where it has custom code in authentication.py. You have to pass both the cookie csrftoken cookie and the header X-CSRFToken for the authentication to work. (This is Tastypie's requirement.) Assuming same domain, the browser will pass the cookies. JQuery will pass the header for you unless the csrftoken cookie is Httponly. Conversely, if the cookie is Httponly, I was unable to even manually set the header in $.ajaxSetup{beforeSend.... It appears that jQuery automatically sets X-CSRFToken to null if the csrftoken cookie is Httponly. At least I was able to set the header X-CS_RFToken to what I wanted, so I know I passed the value correctly. I am using jQuery 1.10.
If you are using curl for testing, you have to pass two cookies (sessionid and csrftoken), set the headers X-CSRFToken and, if the protocol is HTTPS, also set the Referrer.
I found this in the tastypie source code. Basically implies that HTTP POST is not supported by SessionAuthentication.
class SessionAuthentication(Authentication):
"""
An authentication mechanism that piggy-backs on Django sessions.
This is useful when the API is talking to Javascript on the same site.
Relies on the user being logged in through the standard Django login
setup.
Requires a valid CSRF token.
"""
def is_authenticated(self, request, **kwargs):
"""
Checks to make sure the user is logged in & has a Django session.
"""
# Cargo-culted from Django 1.3/1.4's ``django/middleware/csrf.py``.
# We can't just use what's there, since the return values will be
# wrong.
# We also can't risk accessing ``request.POST``, which will break with
# the serialized bodies.
if request.method in ('GET', 'HEAD', 'OPTIONS', 'TRACE'):
return request.user.is_authenticated()
So answering my own question here but if someone can explain it better and suggest a good way to do this, that would be great too.
Edit
I am currently using a workaround from https://github.com/amezcua/TastyPie-DjangoCookie-Auth/blob/master/DjangoCookieAuth.py which basically is a custom authentication scheme that fetches the session_id from the cookie and checks with the backend if it is authenticated. Might not be the most full proof solution but works great.

Categories