I'm trying to build client/server system as follows: server is django REST with some database, client is static HTML/CSS/JS built as separate django project.
I've got stuck with a basic user authentication scheme processed via simple login form on client side. My idea was to go for token authentication, token to be stored in client's localStorage - so that's why I've taken approach to handle the form submit and POST for token to server by JS.
The issue is I cannot get the token from server to client due to Forbidden (CSRF cookie not set.): /getToken/ error on the server side. Spent tons of time on it and I cannot find answer why CSRF cookie is not set as I enforce at least 4 mechanisms forcing the cookie set: 1) {% csrf_token %}, 2) manual setting of cookie header, 3) #ensure_csrf_cookie, 4) removing 'django.middleware.csrf.CsrfViewMiddleware', from MIDDLEWARE_CLASSES in server settings.py ...
Here are the details:
The client part looks as follows:
login.html form:
<form name="login" method="POST">
{% csrf_token %}
Username<input type="text" name="uname"/>
Password<input type="password" name="psswd"/>
<input type="button" onclick="do_login(this.form)" value="Login"/>
<input type="reset" value="Reset"/>
</form>
where do_login() is in the script below. Pls mind the {% csrf_token %} is included ...
login_script.js :
function do_login(form) {
var csrftoken = getCookie('csrftoken');
uname = form.uname.value;
psswd = form.psswd.value;
var req = new XMLHttpRequest();
var url = "http://localhost:8000/getToken/";
var params = JSON.stringify({ username: uname, password: psswd });
req.open("POST", url, true);
req.setRequestHeader("X-CSRFToken", csrftoken);
req.setRequestHeader("Content-type", "application/x-www-form-urlencoded");
req.onreadystatechange = function() {
if(req.readyState == XMLHttpRequest.DONE && req.status == 200) {
alert(http.responseText);
}
}
req.send(params);}
where getCookie is a copy&paste function from django tutorial (it seems to return some cookie, so I assume it's correct)
views.py:
#ensure_csrf_cookie
def user_login(request):
return render(request, 'login.html', {})enter code here
I put #ensure_csrf_cookie to force the cookie input but with no success ...
On the server side I have a view
#require_POST
def getToken(request):
serializer = AuthTokenSerializer(data=request.data)
serializer.is_valid(raise_exception=True)
user = serializer.validated_data['user']
token, created = Token.objects.get_or_create(user=user)
response = HttpResponse({'token': token.key}, content_type="application/json")
response["Access-Control-Allow-Origin"] = "*"
return response
which is basically the slightly redefined django's built-in obtain_auth_token (as I couldn't overcome CORS issue in any other way round)
Related
I am completely new to using JWT authentication in Django and don't know how to implement tokens within each template of my application. I am using JWT (JSON Web Tokens) instead of sessions.
Scenario:
I have a login form that uses AJAX where after clicking the submit button, it points to my API and takes the credentials of a registered user. If authenticated, I intend to use JWT authentication and access the dashboard page, and include those tokens within the page (instead of using sessions). However, I am not redirecting to the specified URL after logging in. Furthermore, I have used Simple-JWT in order to generate tokens but don't know how to include them in my template (AJAX call) in order to access an API. I have tried the following:
views.py:
I have created a login form that doesnt access the values.
Instead, I have used AJAX in my template in order to point it towards the LoginAPI below:
def login(request):
if request.method == 'POST':
login_form = LoginForm(request.POST)
if login_form.is_valid():
pass #not doing anything here since all I need is a form for logging in.
else:
login_form = LoginForm()
return render(request, "users/login.html", {"login_form" : login_form})
#Login API that gets called after clicking the form submit button.
class LoginAPI(APIView):
permission_classes = [AllowAny]
def post(self, request, format = None):
username = request.data['username']
password = request.data['password']
new_user = authenticate(username = username, password = password)
if new_user is not None:
url = 'http://localhost:8000/api/token/' #need to pass in the credentials in order to access the JWT tokens.
values = {
'username' : username,
'password' : password
}
r = requests.post(url, data = values)
token_data = r.json()
return Response(token_data) #should return the JSON tokens to AJAX in success function.
else:
return Response({"status" : "Denied."}, status=status.HTTP_400_BAD_REQUEST)
forms.py:
class LoginForm(forms.Form):
username = forms.CharField()
password = forms.CharField(widget=forms.PasswordInput)
urls.py:
from users import views as users_views
from django.conf import settings
from django.conf.urls.static import static
from users.views import LoginAPI
from rest_framework_simplejwt.views import TokenObtainPairView, TokenRefreshView
urlpatterns = [
path('admin/', admin.site.urls),
path('login_api/', LoginAPI.as_view(), name = 'login_api'),
path('api-auth/', include('rest_framework.urls', namespace='rest_framework')),
path('api/token/', TokenObtainPairView.as_view(), name='token_obtain_pair'),
path('api/token/refresh/', TokenRefreshView.as_view(), name='token_refresh'),
path('login/', user_views.login, name = 'login'),
]
login.html:
{% extends 'users/base.html' %}
{% load static %}
{% block javascript %}
<script src='{% static "users/js/main.js" %}'></script>
{% endblock %}
{% block content %}
<div class="content-section">
<form method="POST">
{% csrf_token %}
{{ login_form|crispy }}
<div class="form-group">
<button class="btn btn-outline-info" type="submit" id='login'> Login </button>
</div>
</form>
</div>
{% endblock content %}
main.js (the file that contains the AJAX function):
$('#login').click(function () {
var username = $('#username').val();
var password = $('#password').val();
$.ajax({
cache: false,
method: 'POST',
url: "/login_api/", #After click, the URL should point towards the LoginAPI in views.py
data: {username:username, password: password},
datatype: 'json',
success: function(data) {
console.log(data.access);
localStorage.setItem('access', data.access);
window.location.href = 'http://127.0.0.1:8000/punch_funcs'; #the template I wish to go to after successful logging in.
},
When testing the login form, I found out that I am being redirected to my template 'punch_funcs'. But the URL is the same as my login page which means that whenever I refresh the page, I am taken back to my login template:
'http://localhost:8000/login' this should be 'http://localhost:8000/punch_funcs' instead.
Also, the tokens have been generated successfully, but how can I place this within my 'punch_funcs' template in order to access several APIs (not listed in the code above)?
Any help is highly appreciated. Thank you very much.
I am getting a 403 forbidden error and WARNING csrf.py _reject: Forbidden (CSRF token missing or incorrect.) is django logs.
Here is my html, jquery-
function req() {
var server_id = $( "#server option:selected" ).val();
$.post("/sp/add_req", JSON.stringify({ cir: {{ cir }}, server_id: server_id, csrfmiddlewaretoken: {{ csrf_token }}}), function (data) {
console.log(data)
});
}
and views.py-
def add_request(request):
....
return JsonResponse({'success': True})
I have the 'django.middleware.csrf.CsrfViewMiddleware' in settings. What is wrong and how to solve this?
{% csrf_token %} will render as <input type="hidden" name="csrfmiddlewaretoken" value="xxxxxx">. Therefore, you could render it separately and then create the JSON object with javascript.
var csrfToken = $('[name="csrfmiddlewaretoken"]').val();
var data = {'csrfmiddlewaretoken': csrfToken);
and then send the data along on your post. I prefer to use the Fetch API to post.
When using AJAX, you need to pass the CSRF token along with your requests. See more at the official guide: https://docs.djangoproject.com/en/2.1/ref/csrf/#ajax
Hi I would really appreciate if somebody could please paste there code here for there Facebook login created for their Django project, whether it is a separate app or not, with a couple of explanations. Pulling User Name, Email and profile pic. Thank you
It took me a week but I have implemented Facebook login the hard way. If you don't want a 3rd party app on your site (more secure and trustworthy for users) here are the steps:
Get the FB login button here: (https://developers.facebook.com/docs/facebook-login/web/login-button). You can change the settings of the button before you copy the code.
Get the javascript plugin here (https://developers.facebook.com/docs/facebook-login/web). I suggest copying the example and modifying the following:
Javascript:
if (response.status === 'connected') {
// Logged into your app and Facebook.
FB.api('/me', {fields: 'name, email'}, function(response) {
console.log('Successful login for: ' + response.name);
document.getElementById("your_name2").value = response.name;
document.getElementById("your_email").value = response.email;
document.getElementById("myForm").submit();
document.getElementById('status').innerHTML =
'Thanks for logging in, ' + response.name + response.email + '!';});
Once logged in and 'connected' you need to change the info you call. Add the {fields...} you require above. Keep the log to see if it's working.
Submit the info pulled in 2 into a hidden form in order to send it to a view and model. Here is the form (hellls just default value):
Form template:
<form action="{% url 'facebooklogin:register' %}" method="post" style="display: none;" id="myForm">
{% csrf_token %}
<label for="your_name">Your name: </label>
<input id="your_name2" type="text" name="your_name" value="helllllls">
<input id="your_email" type="text" name="your_email" value="helllllls">
<input type="submit" value="OK">
</form>
Set up your form, model and view to handle the information as you want. Get profile pic but simply adding an ImageField to form.
URL:
url(r'^registerfb/$', views.get_name, name='register')
VIEW:
def get_name(request):
# if this is a POST request we need to process the form data
if request.method == 'POST':
# create a form instance and populate it with data from the request:
form = NameForm(request.POST)
# check whether it's valid:
if form.is_valid():
# process the data in form.cleaned_data as required
logger.error('Form is valid and running')
logger.error(request.POST.get('your_name'))
logger.error(request.POST.get('your_email'))
# redirect to a new URL:
return HttpResponseRedirect('/thanks/')
# if a GET (or any other method) we'll create a blank form
else:
form = NameForm()
return render(request, 'facebooklogin/name.html', {'form': form})
FORM:
class NameForm(ModelForm):
class Meta:
model = FBUser
fields = ['your_name', 'your_email',]
This is my URLS.py:
from django.conf.urls import include, url
from django.contrib import admin
urlpatterns = [
url(r'^CMS/', include('CMSApp.urls')),
url(r'^admin/', include(admin.site.urls)),
]
and this is CMSApp/urls.py:
from django.conf.urls import url
from django.conf.urls import include
from CMSApp import views
urlpatterns = [
url(r'^$', views.HomePageView.as_view()),
url(r'^users$', views.user_list.as_view()),
url(r'^users/(?P<pk>[0-9]+)$', views.user_detail.as_view()),
url(r'^api-auth/', include('rest_framework.urls',
namespace='rest_framework')),
]
My HomePageView serves this home.html page:
<h3>Register</h3>
<form ng-submit="ctrl.add()" name="myForm">
<label>Username</label>
<input type="text" name="uname" ng-model="ctrl.user.username" required>
<label>Password</label>
<input type="password" name="pwd" ng-model="ctrl.user.password" required>
<label>Email</label>
<input type="email" name="mail" ng-model="ctrl.user.email" required>
<input type="submit" value="Register">
</form>
<h3>Login</h3>
<form ng-submit="ctrl.loginUser()" name="myLoginForm">
<label>Username</label>
<input type="text" name="uname" ng-model="ctrl.user.username" required>
<label>Password</label>
<input type="password" name="pwd" ng-model="ctrl.user.password" required>
<input type="submit" value="Login">
</form>
<script>
angular.module("notesApp", [])
.config(['$httpProvider', function($httpProvider) {
$httpProvider.defaults.xsrfCookieName = 'csrftoken';
$httpProvider.defaults.xsrfHeaderName = 'X-CSRFToken';
}])
.controller("MainCtrl", ["$http", function($http) {
var self = this;
self.users = {};
var fetchUsers = function() {
return $http.get("/CMS/users").then(function(response) { // get list of existing users
self.users = response.data;
}, function(errResponse) {
console.error("Error while fetching users.");
});
};
self.loginUser = function() {
$http.post("/CMS/api-auth/login/", self.user)
.error(function(data, status, headers, config) {
console.log(data);
})
.then(fetchUsers);
console.log("Success Login with ", self.user);
};
}]);
</script>
When I register a user which already exists, then Django returns a 400 Bad Request error, which is good because I use AngularJS on the frontend to handle the 400 status code.
The issue is, when I try to log in with an incorrect username and password (a username which does not exist or a username and password which do not match), Django returns a 200 OK status code so I can't track the issue using AngularJS on the frontend. Any idea why Django returns a 200 OK when I try to log in with an incorrect username and password? How do I get Django to return a 400 when an incorrect username / password is submitted?
Edit: This was my original post: Django Rest Framework + AngularJS not logging user in or throwing errors and charlietfl commented on it saying "as for the ajax error handling. If login fails .... using true REST methodology would send back a 401 status which would then fire your ajax error handler", which is why I do not want a 200 OK when login fails.
The first thing to understand is that Django does not by default enforce authentication.. you can use the auth module to enroll users and to authenticate them, but you have to protect your views explicitly. The authentication app only provides API's for you to use, it doesn't actually protect anything unless you use those API's.
Any view that isn't explicitly checked for authentication will be open to anyone.
Sure, admin requires you to log in, but that's because the authors of the admin app included checks in their code...
The Django REST Framework has it's own checks for this, so very little coding needed, you just have to configure each view (see docs):
http://www.django-rest-framework.org/api-guide/authentication/
For any other view you might want to protect, you need to add some checks. The #login_required decorator on your view is one way to do that, for regular function type views. Since you are dealing with Class-Based-Views, look at the Django docs here:
https://docs.djangoproject.com/en/1.8/topics/class-based-views/intro/#mixins-that-wrap-as-view
Another option for checking login status is to use a middleware class. That's what I'm doing in my current system since almost everything on our site requires that the user be logged in. So, in the middleware class, we check to see if request.user.is_anonymous. If they are, then there's just a small subset of pages they can access, and if they aren't accessing those pages, we redirect them to login. The middleware runs before any view so that covers the whole site.
Ok, so now that I understand you want to actually log the user in via an ajax request, and not just check their access.. and you want control over what response comes back, then what I suggest is implementing your own view for the login service. Something like:
class LoginView(View):
def get(self, request, *args, **kwargs):
user = auth.authenticate(
username=request.GET.get('username'),
password=request.GET.get('password'))
# return whatever you want on failure
if not user or not user.is_active:
return HttpResponse(status=500)
auth.login(request, user)
return HttpResponse('OK')
I did not test this code but it should be pretty close.
I don't understand why but if I try to upload file via ajax it does not work but with regular request it does.
Printed the request.FILES.
#For ajax request
<MultiValueDict: {}>
#For regular request
<MultiValueDict: {u'file': [<TemporaryUploadedFile: IMG_3056.JPG (image/jpeg)>]}>
#Here's my front-end and back-end code
<form action="" method="post" enctype="multipart/form-data">{% csrf_token %}
...
</form>
function submitForm(target, form){
$.ajax({
url:form.attr('action'),
type:'post',
data:form.serialize(),
dataType:"html",
error:function(data, status, xhr){
error();
},
beforeSend:function(){
loading();
},
success:function(data, status, xhr){
$(target).html(data);
},
complete:function(){
loadingDone();
}
});
}
#views.py
def file_upload(request):
doc_form = DocumentForm(user = request.user)
if request.method == 'POST':
doc_form = DocumentForm(request.POST, request.FILES, user = request.user)
print request.FILES
if doc_form.is_valid():
document = doc_form.save()
return render_to_response("create_doc.html", { 'doc_form':doc_form,
}, context_instance = template.RequestContext(request))
Uploading files with regular jQuery's AJAX does not work. See some of these links for help:
http://www.nickdesteffen.com/blog/file-uploading-over-ajax-using-html5
How can I upload files asynchronously?
Sending multipart/formdata with jQuery.ajax
Basically there is a new API in HTML 5 for dealing with files or the old school way is using an iFrame/Flash. Personally I used Uploadify on a project a couple of months ago.
Please try using this jquery form submit plugin, I think it will manage to send the FILE to the server like you need.
(It works for me for a PHP server side, no reason why it wont work for you too)