Ajax Django Login/Authentication without re-direct - ajax

I am trying to have an authentication set-up similar to that of StackOverflow, where the normal browsing is never affected unless there are some privileged actions which requires authentication (Do not bother users until then).
It should be as "Log In" if not logged in or "UserName" if logged in.
The relevant part of base.html (from fallr.net) (extended by index.html) looks like :
<script type="text/javascript">
//<![CDATA[
$(document).ready(function(){
var methods = {
forms : function(){
var login = function(){
var user = $(this).children('form').children('input[type="text"]').val();
var pass = $(this).children('form').children('input[type="password"]').val();
var dataString = '&username=' + $('input[name=username]').val() + '&password=' + $('input[name=password]').val();
if(user.length < 1 || pass.length < 1){
alert('Invalid!\nPlease fill all required forms');
} else {
alert('username: '+user+'\npassword: '+pass);
$.ajax({
type: "POST",
url: "/login",
dataType: "html",
data: {
username : user,
password : pass,
csrfmiddlewaretoken : '{{ csrf_token }}'
},
success: function(json){alert (json.server_response);},
error: function(xhr,errmsg,err) { alert(xhr.status + ": " + xhr.responseText); }
});
$.fallr('hide');
return false;
}
}
$.fallr('show', {
icon : 'secure',
width : '320px',
content : '<h4>Sign in</h4>'
+ '<form>'
+ '<input name="username" placeholder="username" type="text"/'+'>'
+ '<input name="password" placeholder="password" type="password"/'+'>'
+ '</form>',
buttons : {
button1 : {text: 'Submit', onclick: login},
button4 : {text: 'Cancel'}
}
});
}
};
//button trigger
$('a[href^="#fallr-"]').click(function(){
var id = $(this).attr('href').substring(7);
methods[id].apply(this,[this]);
return false;
});
// syntax highlighter
hljs.tabReplace = ' ';
hljs.initHighlightingOnLoad();
});
//]]>
</script>
The urls.py looks like :
from django.conf.urls import patterns, include, url
#from triplanner.views import *
# Uncomment the next two lines to enable the admin:
# from django.contrib import admin
# admin.autodiscover()
urlpatterns = patterns('',
url(r'^$', main_page),
url(r'^login$',ajax_login),
url(r'^login/$','django.contrib.auth.views.login'),
url(r'^logout/$', logout_page),
# our application page
url(r'^account/',include('tripapp.urls')),
)
Also, '^login/$' is the previous implementation for learning which I want to replace with Ajax login.
And my views.py:
# -*- coding: utf-8 -*-
from django.contrib.auth import logout
from django.http import HttpResponseRedirect
from django.shortcuts import render_to_response
from django.contrib.auth import authenticate, login
#from django.http import HttpResponse
from django.template import RequestContext
#from django.utils import simplejson
def main_page(request):
return render_to_response('index.html', context_instance=RequestContext(request))
def logout_page(request):
"""
Log users out and redirect them to the main page
"""
logout(request)
return HttpResponseRedirect('/')
def ajax_login(request):
"""
This view logs a user in using the POST data.
"""
if request.method == 'POST':
print request.POST['username']
username = request.POST['username']
password = request.POST['password']
print username
print password
user = authenticate(username=username, password=password)
if (not user is None) and (user.is_active):
login(request, user)
response_dict = {}
response_dict.update({'server_response': username})
#return HttpResponse(simplejson.dumps(response_dict),mimetype='applicaion/javascript')
return render_to_response('index.html',{'username' : user}, context_instance=RequestContext(request))
# Set Session Expiry to 0 if user clicks "Remember Me"
#if not request.POST.get('rem', None):
# request.session.set_expiry(0)
#data = username
else:
return render_to_response('index.html', context_instance=RequestContext(request))
I am getting a 403 Error like "[20/Aug/2013 00:29:20] "POST / HTTP/1.1" 403 2294"
UPDATE NUMBER 1:
With the changed urls.py, views.py and javascript I am able to get a 200 response, but it gives alert window saying undefined and alerting me "Prevent this page from creatng dialog boxes"

The approach I use is to have a Tastypie api layer and require authentication for the APIs. If the API call fails because of authentication, the client can request the user to log-in via the ajax login method.
You can log-in a user via ajax using this gist

So, it looks like your current problem is with this: alert (json.server_response);. You may want to look into changing your $.ajax dataType parameter to json.
To quote the docs:
The type of data that you're expecting back from the server. If none is specified, jQuery will try to infer it based on the MIME type of the response (an XML MIME type will yield XML, in 1.4 JSON will yield a JavaScript object, in 1.4 script will execute the script, and anything else will be returned as a string). The available types (and the result passed as the first argument to your success callback) are:...
"html": Returns HTML as plain text; included script tags are evaluated when inserted in the DOM.
"json": Evaluates the response as JSON and returns a JavaScript object. The JSON data is parsed in a strict manner; any malformed JSON is rejected and a parse error is thrown. As of jQuery 1.9, an empty response is also rejected; the server should return a response of null or {} instead. (See json.org for more information on proper JSON formatting.)

Related

Django CSRF Token Missing Only in Production

I am getting a missing CSRF_Token error that only occurs in production mode on my server. However everything works great when I am running it from my computer terminal using the runserver command. I've read through many of the other questions pertaining to this with no luck. It seems that my case is slightly different than others, since it works locally but not in production.
I get the error when submitting an Ajax form that submits to the "submit" in views.py. Does anybody know what could be causing this? Also, looking at my cookies in Production mode, the CSRF_Token is not even there to begin with. Locally it is. Thanks for any help.
Here is my views.py
from django.shortcuts import render
from django.http import HttpResponse
def index(request):
return render(request, 'index.html')
def submit(request):
#Receive Request
inputone = request.POST['randominfo']
inputtwo = request.POST['randominfo2']
#Some more code here that setups response.
#Deleted since Im posting to StackOverflow
return response
Code Pertaining to the Ajax Submit
$(function () {
$.ajaxSetup({
headers: { "X-CSRFToken": getCookie("csrftoken") }
});
});
function getCookie(c_name)
{
if (document.cookie.length > 0)
{
c_start = document.cookie.indexOf(c_name + "=");
if (c_start != -1)
{
c_start = c_start + c_name.length + 1;
c_end = document.cookie.indexOf(";", c_start);
if (c_end == -1) c_end = document.cookie.length;
return unescape(document.cookie.substring(c_start,c_end));
}
}
return "";
}
function submitAjax(event){
$.ajax({
type:'POST',
url:'/submit/',
data:{
randominfo:document.getElementById('Random').innerHTML,
randominfo2:document.getElementById('Random2').innerHTML,
},
dateType: 'json',
success:function() {
# Url here
}
})
};
Solution that fixed this problem.
Adding "from django.views.decorators.csrf import ensure_csrf_cookie" in views.py and then "#ensure_csrf_cookie" above the view that returns the html file that contained the ajax form
The error ocurs because you are not setting the csrf token, to prevent this we have to check some details
First of all, you have to set the csrf token to your form, in your html you have to set some as follow:
<form id="id" name="form">
{% csrf_token %}
<!-- Form body here -->
</form>
Second the approach to set the csrf cookie to your request header is ok, i only suggest that instead you set your data field one by one, use method serialize of jquery
data: $("#your-form-id").serialize()
I would like to recommend you to read this post about ajax request with django that is very helpful
There are 2 things you can do:
1.) Submit a CSRF token in your ajax call. You have to use a getCookie() javascript function to get it. Luckily the django documentation has some code you can copy and paste.
javascript
$.ajax({
type:'POST',
url:'/submit/',
data:{
randominfo:document.getElementById('Random').innerHTML,
randominfo2:document.getElementById('Random2').innerHTML,
'csrfmiddlewaretoken': getCookie('csrftoken'), // add this
...
2.) Disable csrf for your /submit view. You can do this with a decorator. Note that this is less secure so make sure there's no confidential data.
views.py:
from django.views.decorators.csrf import csrf_exempt
...
#csrf_exempt
def your_submit_view(request):
#view code

Django, Ajax- HttpResponse does not send json

Django 1.7.2/ python 3.4
this code is about 'like'.
if user click the 'like' button, ajax calls 'pushLike'.
if the user has liked the article before(saved inside Mysql), delete the row on table(DB).
or if the user is not liking the article, create a row and insert it on the table(DB).
after that, count how many like has beed clicked on that article.
I would like to pass the likeCnt(count) to ajax, and write it on the button.
The likeCnt has the right value(I checked it on the server mysql table).
The button color does change(white to blue, and vise versa), but the text does not change.
It seems like json does not pass to ajax. I tried passing data by 'text' type and it did worked, but i want it by json.
I've tried simplejson, json, mimetype, content_type on HttpResponse.
please help me.
view
#login_required
def pushLike(request):
pk = request.GET['writing_id']
try:
la = LikeArticles.objects.get(user = User.objects.get(username=request.user.username), article_id=pk)
if(la.is_like()):
la.delete()
likeCnt = LikeArticles.objects.filter(article_id=pk).count()
FreeBoards.objects.filter(id=pk).update(like = likeCnt)
else: #Never happens
la.like = True
la.save()
likeCnt = LikeArticles.objects.filter(article_id=pk).count()
FreeBoards.objects.filter(id=pk).update(like = likeCnt)
except ObjectDoesNotExist:
la = LikeArticles(user = User.objects.get(username=request.user.username),
article = FreeBoards.objects.get(id=pk),
like = True,
)
la.save()
likeCnt = LikeArticles.objects.filter(article_id=pk).count()
FreeBoards.objects.filter(id=pk).update(like = likeCnt)
data = {'likeCnt': likeCnt}
# return render(request, url, context)
return HttpResponse(simplejson.dumps(data), mimetype='application/javascript')
javascript
<script type="text/javascript">
$(document).ready(function(){
$('#btn-like').click(function(){
var e = $('#btn-like').css('background-color');
$.ajax({
url : '/sle/freeboards/pushLike/',
data : {'writing_id':{{writing_id}},
},
dataType : "json",
success:function(data){
alert(data.likeCnt);
if(e == 'rgb(59, 89, 152)') {
$('#btn-like').css('background-color', '#ffffff').css('color', '#000000');
$('#btn-like').text(data.likeCnt);
} else {
$('#btn-like').css('background-color', '#3b5998').css('color', '#ffffff');
$('#btn-like').text(data.likeCnt);
}
},
failure: function(data){
alert('fail!!')
}
});
});
});
</script>
you'll want to be sure to set the proper mimetype in your HttpResponse
#login_required
def pushLike(request):
...
# return json -- !!not javascript!!
return HttpResponse(simplejson.dumps(...), mimetype="application/json")
--or--
#login_required
def pushLike(request):
...
# return json -- !!not javascript!!
return JsonResponse({"your": "context dictionary"})
If that doesn't work, have you tried parsing the json with your Jquery code?
ie:
$.ajax({
...
success: function(data){
var response = $.parseJSON(data);
...
}
});
javascript might actually receiving bytes back from whatever you are serving your django app with... so instead of getting JSON back, you're actually getting string that looks like JSON. http://api.jquery.com/jquery.parsejson/

Ajax Authentication with Tastypie Django

I followed this to create my ajax authentication. The ajax does not send the POST data; it sends an empty querydict. If I explicitly write my username and password in my ajax view, it logins in and out perfectly... but that is useless.
I found the answer. This has been updated for Django 1.5.1. The code below works.
#Ajax_Views.py
from django.contrib.auth.models import User
from django.contrib.auth import authenticate, login, logout
from django.http import HttpRequest
from django.conf.urls import url
from django.utils import simplejson
from tastypie.http import HttpUnauthorized, HttpForbidden
from tastypie.utils import trailing_slash
from tastypie.resources import ModelResource
from tastypie.authorization import Authorization
class UserResource(ModelResource):
class Meta:
queryset = User.objects.all()
fields = ['first_name', 'last_name', 'email']
allowed_methods = ['get', 'post']
resource_name = 'user'
authorization = Authorization()
def prepend_urls(self):
return [
url(r"^(?P<resource_name>%s)/login%s$" %
(self._meta.resource_name, trailing_slash()),
self.wrap_view('login'), name="api_login"),
url(r'^(?P<resource_name>%s)/logout%s$' %
(self._meta.resource_name, trailing_slash()),
self.wrap_view('logout'), name='api_logout'),
]
def login(self, request, **kwargs):
self.method_check(request, allowed=['post', 'ajax'])
data = self.deserialize(request, request.body, format=request.META.get('CONTENT_TYPE', 'application/json'))
username = data.get('username', '')
password = data.get('password', '')
user = authenticate(username=username, password=password)
if user:
if user.is_active:
login(request, user)
return self.create_response(request, {
'success': True
})
else:
return self.create_response(request, {
'success': False,
'reason': 'disabled',
}, HttpForbidden )
else:
return self.create_response(request, {
'success': False,
'reason': 'incorrect',
}, HttpUnauthorized )
def logout(self, request, **kwargs):
self.method_check(request, allowed=['get'])
if request.user and request.user.is_authenticated():
logout(request)
return self.create_response(request, { 'success': True })
else:
return self.create_response(request, { 'success': False }, HttpUnauthorized)
#Jquery/Ajax
$('#send').click(function(e){
e.preventDefault();
data = {
"username": $('#username').val(),
"password": $('#password').val()
};
$.ajax({
type: "POST",
url: "http://127.0.0.1:8000/api/user/login/",
data: JSON.stringify(data),
dataType: "json",
contentType: "application/json",
success: function(data) {console.log(data)},
error: function (rs, e) {console.debug(rs)}
});
});
#The HTML
<input type='text' id='username' />
<input type='password' id='password'/>
<input type='submit' id='send' class='btn' href='#'>Send</a>
I'm trying to build out a front end in Backbone for a Django 1.5 app.
I understand the Tastypie stuff and have been able to get that working, but I'm not sure how the page knows if the user is logged in or not when the page is first visited. I am using Session storage - is this incompatible with a JavaScript front end? Do I need to manually store the CSRF token and delete it after the user logs in, am I forced to use Django's (non-Ajax) for login/logout and then redirect to a protected, django served page with the JavaScript app code?
I am serving the JavaScript from django now, to get the CSRF token.. So I think I'm on the right path.

Grails - Fails to submit base64 img

I need to make ajax submit to submit some data include a base64 string of the image, which is render from canvas.
When submit I look in the network panel of Chrome inspector and everything look fine, in "form data" it list all the data that I want to submit.
But in Grails I cannot get the data, there is nothing in the params, just the controller name and action name. Thus everything I get with simple params.dataName is null.
I guess there is something with the size of the post request, but I'm not so sure as I have done this before without ajax.
This is my code for upload with jquery ajax:
var imgBase64String = canvas.toDataURL("image/png");
imgBase64String = imgBase64String .replace('data:image/png;base64,', '');
var submitData = $(form).serializeArray();
submitData.push({name: "webImage", value: imgBase64String })
$.ajax({
type: 'POST',
url: '${createLink(action: 'myAction')}',
data: submitData,
dataType: "html",
success: function(data){//Success code},
});
UPDATE
My code on the server side, it fails at the simple step to retrieve params data:
def myAction= {
def paramData = params
log.info "paramData: " + paramData
def url = params.url
def email = params.email
def webImage = params.webImage
log.info "param: url = " + url
log.info "param: email = " + email
log.info "param: webImage = " + webImage
//Other implement code
}
And the output:
2012-10-08 16:31:28,988 [http-bio-8080-exec-5] INFO myController - paramData: [action:myAction, controller:myController]
2012-10-08 16:31:28,989 [http-bio-8080-exec-5] INFO myController - param: url = null
2012-10-08 16:31:28,989 [http-bio-8080-exec-5] INFO myController - param: email = null
2012-10-08 16:31:28,989 [http-bio-8080-exec-5] INFO myController - param: webImage = null
The size of the base64 image I'm trying to submit is 1998720, don't know if this matter.
Many thanks.
I believe you can simply pass canvas.toDataURL("image/png") into the data field in the $.ajax() method. Also use $.post() instead of $.ajax(). So your code should look like this in the js file:
$.post('/image/getCanvasImage', //this is your url
{
img : canvas.toDataURL('image/jpeg'),
email : email
}, function(data){
//whatever you wanna do with the returned data
}
);
Then in your action, import import sun.misc.BASE64Decoder package and you can write this code to save the canvas image:
def file = params.img.toString().substring((params.img.toString().indexOf(",")+1),params.img.toString().size())
byte[] decodedBytes = new BASE64Decoder().decodeBuffer(file)
def image = new File("mySavedImage.jpg")
image.setBytes(decodedBytes)
Should work!

Ajax and Django render_to_response (How to render to response inside success function)

Currently I am trying to implement a login validation system. I am using ajax so that users can get a response without being redirected to another page. My ajax function sends email and password that user has inputted, and get message in callback function, which can be in three types: email, password, or the actual HttpResponse object. But I have no idea how to render the given http response object using ajax and jquery. Is location.href an option? I am pasting the code below.
In javascript:
function loginSubmit(email, password) {
var d= "email=" + email + "&password=" + password;
$.ajax({
url: "/login",
type: "POST",
dataType: "text",
data: d,
success: function(m) {
if (m == "email") {
$("#emailMessage").html("There is no account associated with this email address.");
$("#emailError").show();
$("#emailError").fadeOut(5000, function() {});
} else if (m == "password") {
$("#emailMessage").html("There is no account associated with this email address.");
$("#emailError").show();
$("#emailError").fadeOut(5000, function() {});
} else {
}
}
});
}
in view function:
def login(request):
json = request.POST
e = json['email']
p = json['password']
u = User.objects.filter(email=e)
if (len(u)):
up = User.objects.filter(email=e, password=p)
if (len(up)):
return render_to_response('profile.html', context_instance=RequestContext(request))
else:
data = "password"
c = RequestContext(request, {'result':data})
t = Template("{{result}}")
datatype=u"application/javascript"
return HttpResponse(t.render(c), datatype)
else:
data = "email"
c = RequestContext(request, {'result':data})
t = Template("{{result}}")
datatype=u"application/javascript"
return HttpResponse(t.render(c), datatype)
p.s. Currently I am using a dummy template and HttpResponse to send data to the ajax success callback function. Is there a more efficient way to accomplish this (send back json data)? I will wait for your replies guys!
from django.contrib.auth import authenticate, login as auth_login
def login(request):
# Use authentication framework to check user's credentials
# http://djangosnippets.org/snippets/1001/ for auth backend
user = authenticate(
email = request.POST['email'],
password = request.POST['password'], )
if user is not None:
# Use Auth framework to login user
auth_login(request, user)
return render_to_response('profile.html',
context_instance=RequestContext(request))
else:
# Return Access Denied
# Never return bad email/bad password. This is information leakage
# and helps hackers determine who uses your platform and their emails.
return HttpResponse("Failed: Bad username or password", status=403)
function loginSubmit(email, password) {
$.ajax({
url: "/login",
type: "POST",
data: {email:email, password:password},
success: function(data) {
var returned_html = $(data);
$("#target_profile_area").clear().append(returned_html);
},
error: function(jqXHR) {
if (jqXHR.statusCode == 403) {
$("#loginMessage").text("Your login details are incorrect");
} else {
$("#loginMessage").text("Error Contacting Server");
}
$("#loginError").show();
$("#loginError").fadeOut(5000, function() {});
}
});
}

Resources