I have a site that uses custom authentication in Django that authenticates to a web service using a username, password, and domain. I need to store this information for every subsequent view's request object.
All seems to be going well until I try to make a jquery $.ajax call, debugging the view that gets called from the ajax request I have no session information and the user is AnonymousUser.
I need the session variables to make subsequent calls to the web service for data to display. I've tried a few things in there but nothing seems to work correctly. I want to make sure users a logged in before being able to submit these web service queries as I want to keep the username/domain to lookup the password (and not have it on the client side) in the view and ensure the user is always logged in.
views.py
def login(request):
if settings.DEBUG == True:
print "views.login:Attempting loging at views.login(request)"
if request.method == 'POST':
if settings.DEBUG:
print "views.login: method is POST"
form = LoginForm(request.POST)
if form.is_valid():
if settings.DEBUG:
print "Form is valid, attepmting to authenticate"
Login(request, form.get_user())
str = reverse('cm_base.views.index')
request.session['username']=form.get_user()
request.session['domain']=form.get_domain()
return render_to_response('cm_base/index.html',
{"DEBUG": True,
"user":request.session.get('username'),
'tabs': settings.TAB_LIST},
context_instance=RequestContext(request))
else:
# Their password / email combination must have been incorrect
pass
else:
form = LoginForm()
return render_to_response('cm_base/login.html',
{"DEBUG": True,
'form' : form
},
context_instance=RequestContext(request))
#login_required()
def index(request):
if request.method == 'POST':
print "POSTING"
if settings.DEBUG == True:
print "views.index:Opening index"
return render_to_response('cm_base/index.html',
{"DEBUG": True,
"user":"user",
'tabs': settings.TAB_LIST},
context_instance=RequestContext(request))
#login_required()
def scorecard(request):
user = CustomUser.objects.get(username=request.session.get('username'),
domain=request.session.get('domain'))
*fails on the above line with DoesNotExist: CustomUser matching query does not exist. Lookup parameters were {'username': None, 'domain': None}
base.js
$.ajax({
url : path,
data: $(this).serialize(),
headers: {'X-CSRFToken':getCookie('csrftoken')
,'sessionid':getCookie('sessionid')
},
success : function(data) {
console.log($(this));
//refresh right div
$('#contentpane').empty();
$('#contentpane').html(data.rhtml);
console.log(data.rhtml);
}
});
Looks like my implementation of the backend that was the problem.
backends.py
class CustomBackend(object)
...
def get_user(self, username):
try:
return CustomUser.objects.get(username=username)
except CustomUser.DoesNotExist:
return None
This is called during session passing and actually uses a the primary key which is a userid, not username so this was always returning no user and defaulting to anonymous user. I misinterpreted when the documentation says the userid can be anything, including username, I thought I could pass that in as such but I have both an auto generated userid as well as a username on the object.
Related
I have React app and a Redux store. I am working on an authentication now. My backend is on Django RF and for JWT auth I use a simplejwt lib. The thing is, that this lib has an "out of the box" view (/token) that returns a JWT token on success.
The problem is that I need to have a user object in my app upon successful authentication. So when the user logs in it returns a token only. But I need to redirect the user to their page if logged in.
I sure can override this /token view and return whatever object I want, but why is this implemented this way right now?
Looks like there is no other way that to override validate() method in serializers.py like this:
class UserTokenObtainPairSerializer(TokenObtainPairSerializer):
def validate(self, attrs):
data = {
'token': super().validate(attrs),
'id': self.user.id,
'email': self.user.email,
'name': self.user.name,
}
return data
I am getting the Forbidden (CSRF token missing or incorrect.) error when I try to use login page.
The scenario is as follows:
An user has two tabs open.
Both tabs are login pages.
In tab 1, user successfully logged in, and was redirected to a new page where login is required.
In tab 2, user hasn't refreshed the page, and is still in the login page. In the Django backend, user is already authenticated, but the front-end template hasn't noticed it yet.
In tab 2, when I click on login button, I get Forbidden (CSRF token missing or incorrect.) error.
I made sure that csrf_token is in the form.
This error occurs only when I'm using two tabs.
I'm using AJAX
Why is this happening? How can I fix it?
I don't know this would help, but here is my views.py for login
class Login_View(LoginView):
template_name = 'login.html'
def post(self, request, *args, **kwargs):
username = request.POST['username']
password = request.POST['password']
user = authenticate(username=username, password=password)
response_data = {}
if user is not None:
if user.is_active:
login(request, user)
response_data['result'] = 'success'
else:
return HttpResponse("Inactive user.")
else:
response_data['result'] = 'fail'
return HttpResponse(json.dumps(response_data), content_type="application/json")
The reason is addressed in the documentation here:
For security reasons, CSRF tokens are rotated each time a user logs in. Any page with a form generated before a login will have an old, invalid CSRF token and need to be reloaded. This might happen if a user uses the back button after a login or if they log in a different browser tab.
As for fixing it, there's neither a straightforward way nor a great reason to do so. If the user encounters an error in this unlikely scenario all they have to do is reload the page. So I wouldn't bother if I were you.
if a scenario occurs like this where user opens 2 tabs and tries to login, which is not a real life scenario, still if you want you can do this, keeping in mind the user like to play like this
def post(self, request, *args, **kwargs):
username = request.POST['username']
password = request.POST['password']
if request.user.is_authenticated():
return redirect('to_some_page')
else:
user = authenticate(username=username, password=password)
response_data = {}
if user is not None:
if user.is_active:
login(request, user)
response_data['result'] = 'success'
else:
return HttpResponse("Inactive user.")
else:
response_data['result'] = 'fail'
i am working in a project, there is no user authentication and authorization. Bascially i am calling ajax in client side and it executes a view in django and return a json out. How can i validate this request is only coming from browser and how to restrict the if this not coming from the browser or any manual script?
You can use request.is_ajax() method
HttpRequest.is_ajax()
Returns True if the request was made via an XMLHttpRequest, by checking the HTTP_X_REQUESTED_WITH header for the string 'XMLHttpRequest'. Most modern JavaScript libraries send this header. If you write your own XMLHttpRequest call (on the browser side), you’ll have to set this header manually if you want is_ajax() to work.
If a response varies on whether or not it’s requested via AJAX and you are using some form of caching like Django’s cache middleware, you should decorate the view with vary_on_headers('HTTP_X_REQUESTED_WITH') so that the responses are properly cached.
docs
Im updating my answer to fit what we commented above
In your views
from django.core import signing
from django.views.generic import View, TemplateView
from django.http import HttpResponseBadRequest
class BrowserView(TemplateView):
template_name = 'yourtemplate.html'
def get_context_data(self, **kwargs):
ctx = super(BrowserView, self).get_context_data(**kwargs)
ctx['token'] = signing.dumps(self.request.session_id)
return ctx
class AjaxView(View):
def get(self, *args, **kwargs):
if self.request.is_ajax():
try:
sign = signing.loads(self.request.GET.get('token'), max_age=(20))
if sign == self.request.session_id:
## return ajax
return HttpResponseBadRequest('You are not authorized to see this page')
except signing.BadSignature:
return HttpResponseBadRequest('You are not authorized to see this page')
else:
return HttpResponseBadRequest('You are not authorized to see this page')
In your template
In this case I used a meta tag, but you get the idea
<meta name="validation" content="{{token}}" />
In your javascript
var t = document.querySelector("meta[name='validation']").getAttribute('content');
$.ajax({
url:'yoururl',
data: yourData + '&token=' + t,
type: 'get',
success: function(response){
// do whatever
},
error: function(e){
console.log(e);
}
});
I don't believe it's possible to 100% prevent this, but there are some things you can do:
a set-cookie header w/some unique ID on the page, but not on API responses.
if the cookie isn't received by your API, return a 401.
tracking API calls per unique ID could be a good indicator of "proper" usage.
associate IDs w/IPs.
the tracking metrics can be combined w/a threshold that blocks requests if exceeded.
you can check the referrer header (easy to spoof).
finally, lookup the is_ajax method of Django's, but this just checks for an XMLHttpRequest header (again, easy to spoof).
I am trying to test and AJAX view in my Django Project. When submit the post from JQuery the data is correctly accessible in the Django View but when I try to make queries for the Django test client it is emplty.
Here is the code I use:
The view
def add_item(request):
if request.is_ajax() and request.method == 'POST':
post_data = request.POST
print post_data ## <----- THIS IS EMPTY
name = post_data.get('name')
# Render the succes response
json_data = json.dumps({"success":1})
return HttpResponse(json_data, content_type="application/json")
else:
raise Http404
And the test
class TestAddItem(TestCase):
def test_some_test(self):
data = {
'description':"description",
}
response = self.client.post('theurl', data, content_type='application/json')
Any Idea what I might be doing wrong?
I tried also without content type and also using plain url like thurl/?name=name without succes.
Any help will be appreciated.
Olivier
After trying different combinations of parameter formating, content types, etc..
I found one solution that works :
response = self.client.post(reverse('theurl'), data,
**{'HTTP_X_REQUESTED_WITH': 'XMLHttpRequest'})
And I get the following dictionary on the POST parameters of the request:
<QueryDict: {u'name': [u'name']}>
Edit I love testing :)
I am using this
def ajax_create( request ):
if request.is_ajax():
form = SourceForm()
template = 'ajax_form.html'
data = {
'form': form,
}
return render_to_response( template, data,
context_instance = RequestContext( request ) )
I get this error
ajax_create didn't return an HttpResponse object
Are you sure your request is an ajax call ? It doesn't look like it.
Try:
if request.is_ajax():
...
else:
# If the request is not an ajax call, it will return a 400 Error.
return HttpResponseBadRequest()
Perhaps if request.is_ajax(): is returning False, if that's your entire view function?
Typically, you should follow this pattern when making Django view functions:
def my_view(request):
if request.method == 'POST':
form = MyForm(data=request.POST, files=request.FILES)
if form.is_valid():
# do something with form...
else:
initial = {...}
form = MyForm(initial=initial)
context = {'form':form, ...}
return render_to_response('path/to/template.html', context, context_instance=RequestContext(request))
It's not entering to your "if" scope. it returns None
https://docs.djangoproject.com/en/1.3/ref/request-response/#django.http.HttpRequest.is_ajax
Check your ajax call and make sure it returns something in case the request is not ajax