Django DRF - How to do CSRF validation with token authentication - python

I am working on REST API using Django and Django Rest Framework.
In the front-end I have AngularJs app.
I have used Token authentication for APIs. This disables the CSRF checks.
I want keep CSRF validation with REST API. How to achieve this ?
How should I get CSRF Token value so that it can be set into the header of every POST request, using interceptor of my angular application.

I have this configuration in my app.js. Should do the trick!
app.config(function($httpProvider) {
$httpProvider.defaults.headers.common['X-Requested-With'] = 'XMLHttpRequest';
$httpProvider.defaults.xsrfCookieName = 'csrftoken';
$httpProvider.defaults.xsrfHeaderName = 'X-CSRFToken';
});

I've got the same problem when i started to use Angular 1.x with Django and DRF, and then i found this code snippet in a book i think, and it works fine for me. Include this file in your base.html file or your main html file before any javascript import, and everything will work smoothly and you can start talking to your backend.
// Place at /static/js/csrf.js
// CSRF helper functions taken directly from Django docs
function getCookie(name) {
var cookieValue = null;
if (document.cookie && document.cookie != '') {
var cookies = document.cookie.split(';');
for (var i = 0; i < cookies.length; i++) {
var cookie = jQuery.trim(cookies[i]);
// Does this cookie string begin with the name we want?
if (cookie.substring(0, name.length + 1) == (name + '=')) {
cookieValue = decodeURIComponent(cookie.substring(name.length + 1));
break;
}
}
}
return cookieValue;
}
var csrftoken = getCookie('csrftoken');
function csrfSafeMethod(method) {
// these HTTP methods do not require CSRF protection
return (/ ˆ (GET|HEAD|OPTIONS|TRACE) $ /.test(method));
}
$.ajaxSetup({
beforeSend: function (xhr, settings) {
if (!csrfSafeMethod(settings.type) && !this.crossDomain) {
xhr.setRequestHeader("X-CSRFToken", csrftoken);
}
}
});

Related

How to delete a user's facebook data from firebase with firebase admin?

I'm using firebase UI to authenticate users in a given frontend. Facebook authentication is enabled. I need to implement a facebook data deletion callback so I need to make my backend do two things:
delete/disable the facebook sign in method from the user that issued the data deletion request from facebook
delete every trace of facebook data from my firebase user (the provider user info) but without deleting the user
However, I can't find anything in firebase admin's documentation to delete facebook data. So, how can I delete the data?
PD: Keep in mind that I want to delete the user's provider user info, but not the whole user (because I need the user to stay there for data consistency)
If someone is looking for a NodeJS implementation, this is how you can do it:
module.exports = functions.https.onRequest(async (req, res) => {
try {
const signedRequest = req.body.signed_request;
const userObj = parseSignedRequest(signedRequest, FB_SECRET_KEY);
const userRecord = await admin
.auth()
.getUserByProviderUid("facebook.com", userObj.user_id);
await admin.auth().deleteUser(userRecord.uid);
res.json({
url: "<status_url>",
confirmation_code: "<code>",
});
} catch (e) {
// console.log(e);
res.status(400).json({
message: "Bad Request",
});
}
});
function base64decode(data: string) {
while (data.length % 4 !== 0) {
data += "=";
}
data = data.replace(/-/g, "+").replace(/_/g, "/");
return Buffer.from(data, "base64").toString("utf-8");
}
function parseSignedRequest(signedRequest: string, secret: string) {
var encoded_data = signedRequest.split(".", 2);
// decode the data
var sig = encoded_data[0];
var json = base64decode(encoded_data[1]);
var data = JSON.parse(json);
if (!data.algorithm || data.algorithm.toUpperCase() != "HMAC-SHA256") {
throw Error(
"Unknown algorithm: " + data.algorithm + ". Expected HMAC-SHA256"
);
}
var expected_sig = crypto
.createHmac("sha256", secret)
.update(encoded_data[1])
.digest("base64")
.replace(/\+/g, "-")
.replace(/\//g, "_")
.replace("=", "");
if (sig !== expected_sig) {
throw Error("Invalid signature: " + sig + ". Expected " + expected_sig);
}
return data;
}
PS. There is no way to just unlink providers via Firebase Admin SDK for now so best we can do is delete the user data.

Cannot retrieve data from endpoint

I have installed Chatterbot for Django integration. I followed the easy tutorial with every step and made it so that the endpoint was: http://127.0.0.1:8000/chatterbot/ What I did next was try to communicate with that endpoint to see if I would get back any results. So I made an Ajax request as follows:
var query = {"text": "My input statement"};
$.ajax({
type: 'POST',
url: "http://127.0.0.1:8000/chatterbot/",
data: JSON.stringify(query),
contentType: 'application/json',
success: function (data) {
console.log(data);
}
});
However, what returns in console is: POST http://127.0.0.1:8000/chatterbot/ 403 (Forbidden) and what returns in the cmd prompt when I run my server is:
csrf: WARNING - Forbidden (CSRF token missing or incorrect.):
/chatterbot/ [29/Mar/2018 02:16:43] "POST /chatterbot/ HTTP/1.1" 403
2502
Why am I getting this error? How can I fix it so I receive the call back from the endpoint?
View for this page:
def IndexView(request):
latest_questions = Questions.objects.all().order_by("-date_published")[:5]
popular_questions = Questions.objects.all().order_by("-num_replies")[:5]
return render(request, 'core/index.html',
{'latest_questions': latest_questions, 'popular_questions': popular_questions
})
Try this code
// using jQuery
function getCookie(name) {
var cookieValue = null;
if (document.cookie && document.cookie !== '') {
var cookies = document.cookie.split(';');
for (var i = 0; i < cookies.length; i++) {
var cookie = jQuery.trim(cookies[i]);
// Does this cookie string begin with the name we want?
if (cookie.substring(0, name.length + 1) === (name + '=')) {
cookieValue = decodeURIComponent(cookie.substring(name.length + 1));
break;
}
}
}
return cookieValue;
}
var csrftoken = getCookie('csrftoken');
var query = {
"text": "My input statement",
"csrfmiddlewaretoken": csrftoken
};
$.ajax({
type: 'POST',
url: "http://127.0.0.1:8000/chatterbot/",
data: query,
contentType: 'application/json',
success: function (data) {
console.log(data);
}
});
one way is to send the csrfmiddlewaretoken like below
var query = {
"text": "My input statement",
'csrfmiddlewaretoken': "{{csrf_token }}"
};
other way is to use #csrf_exempt decorator
from django.views.decorators.csrf import csrf_exempt
#csrf_exempt
def IndexView(request):
# .... code.....
other is to add a script
function csrfSafeMethod(method) {
// these HTTP methods do not require CSRF protection
return (/^(GET|HEAD|OPTIONS|TRACE)$/.test(method));
}
$.ajaxSetup({
beforeSend: function(xhr, settings) {
if (!csrfSafeMethod(settings.type) && !this.crossDomain) {
xhr.setRequestHeader("X-CSRFToken", csrftoken);
}
}
});
Reference: https://docs.djangoproject.com/en/2.0/ref/csrf/
If you dont want to use CSRF tokens just add this above your code.
from django.views.decorators.csrf import csrf_exempt
#csrf_exempt
def IndexView(request):
# your code

RESTful authentication in a hybrid web app

I'm writing an hybrid web app using flask. By hybrid I mean that there is the conventional web server application built with template engine and there is RESTful API for client side application as well. So here is my confusion:
In my current application, user logs in through the web server so that an HTTP session is created, the user then can do stuff. However, in one of the pages, there is a action that is done via AJAX call to the RESTful part of the same application. Normally in this API, the user will have to authenticate itself again. But here the client side code has no way of knowing the user name and password. What's the correct pattern here?
You can authenticate the user client side in ajax call:
For example:
$.ajax({
url: 'http://example.com/api.ashx/v2/users.xml',
beforeSend: addHeaders,
dataType: "text",
processData: false,
success: function(data, status) {
// do stuff here
},
error: function(xhr, status, error) {
// do stuff here
}
});
var addHeaders = function(xhr) {
var restAuthHeader = readCookie("AuthorizationCookie");
if (restAuthHeader != null) {
xhr.setRequestHeader("Rest-Authorization-Code", restAuthHeader);
}
};
var readCookie = function(input) {
var nameEQ = input + "=";
var ca = document.cookie.split(';');
for (var i = 0; i < ca.length; i++) {
var c = ca[ i ];
while (c.charAt(0) == ' ') c = c.substring(1, c.length);
if (c.indexOf(nameEQ) == 0)
return c.substring(nameEQ.length, c.length);
}
return null;
};
Let's say you have a form with username and password to authenticate.
<form id="login-form">
<input data-key="username" type="text" placeholder="username" />
<input data-key="password" type="password" placeholder="password" />
<button type="submit">Login</button>
</form>
Your endpoint should return a token and a userid.
var $form = $('#login-form');
// post to your login endpoint with username and password
$.post('/login', {
username: $form.find('input[data-key="username"]').val(),
password: $form.find('input[data-key="password"]').val();
}).done(function (response) {
// put the token and userid in the sessionStorage or localStorage
window.sessionStorage.setItem('token', response.data.token);
window.sessionStorage.setItem('userId', response.data.userId);
}).fail(function (e) {
// handle incorrect credentials here.
alert('authentication failed');
});
You should append these to your headers to request data.
function requestEndpoint(endpoint) {
$.ajax({
// other stuff here you probably know
headers: {
'X-Auth-Token': window.sessionStorage.getItem('token'),
'X-User-Id': window.sessionStorage.getItem('userId'),
'Content-Type': 'application/json'
}
});
}
Just scan for these headers at the endpoints in flask

AJAX Python Call Definition [closed]

Closed. This question needs details or clarity. It is not currently accepting answers.
Want to improve this question? Add details and clarify the problem by editing this post.
Closed 8 years ago.
Improve this question
I want to call a definition that is located in Python with a parameter, using AJAX. The web framework I'm using is Flask.
In my test.py file:
def example(param):
In my JS file
$.ajax({
type: 'POST',
url: "test.py"
//pass param here?
});
$.ajax({
type: 'POST',
url: "test.py"
data: {
param: param
}
});
make sure to include your CSRF token. You can learn more about it here http://flask.pocoo.org/snippets/3/
you can add this to your js code.
function getCookie(name) {
var cookieValue = null;
if (document.cookie && document.cookie != '') {
var cookies = document.cookie.split(';');
for (var i = 0; i < cookies.length; i++) {
var cookie = jQuery.trim(cookies[i]);
// Does this cookie string begin with the name we want?
if (cookie.substring(0, name.length + 1) == (name + '=')) {
cookieValue = decodeURIComponent(cookie.substring(name.length + 1));
break;
}
}
}
return cookieValue;
}
var csrftoken = getCookie('csrftoken');
function csrfSafeMethod(method) {
// these HTTP methods do not require CSRF protection
return (/^(GET|HEAD|OPTIONS|TRACE)$/.test(method));
}
$.ajaxSetup({
beforeSend: function(xhr, settings) {
if (!csrfSafeMethod(settings.type) && !this.crossDomain) {
xhr.setRequestHeader("X-CSRFToken", csrftoken);
}
}
});

CSRF With Ajax Polling

I have some AJAX that polls the server every 5 seconds:
var date = $('article').first().find('time').text();
console.log(date);
setInterval(function() {
$.post('pollNewEntries', {'date':date}, newEntrySuccess)
}, 5000);
Unfortunately, I'm getting a 403 error every time the AJAX tries to poll the server, stating that I have made an invalid CSRF request. I've used AJAX with forms before and included the CSRF token within the forms, but I"m not sure how I would do it with a formless AJAX request like above.
The solution to this problem is described in the Django documentation: https://docs.djangoproject.com/en/dev/ref/contrib/csrf/#ajax
Add this code to the top of your js:
$.ajaxSetup({
beforeSend: function(xhr, settings) {
function getCookie(name) {
var cookieValue = null;
if (document.cookie && document.cookie != '') {
var cookies = document.cookie.split(';');
for (var i = 0; i < cookies.length; i++) {
var cookie = jQuery.trim(cookies[i]);
// Does this cookie string begin with the name we want?
if (cookie.substring(0, name.length + 1) == (name + '=')) {
cookieValue = decodeURIComponent(cookie.substring(name.length + 1));
break;
}
}
}
return cookieValue;
}
if (!(/^http:.*/.test(settings.url) || /^https:.*/.test(settings.url))) {
// Only send the token to relative URLs i.e. locally.
xhr.setRequestHeader("X-CSRFToken", getCookie('csrftoken'));
}
}
});
You need to pass csrf token along with your post data:
var date = $('article').first().find('time').text();
console.log(date);
setInterval(function() {
$.post('pollNewEntries', {'date':date, 'csrfmiddlewaretoken': '{{csrf_token}}'}, newEntrySuccess)
}, 5000);
Simply add these lines in your script. Here is an example in coffeescript :
### CSRF methods ###
csrfSafeMethod = (method) ->
# these HTTP methods do not require CSRF protection
return (/^(GET|HEAD|OPTIONS|TRACE)$/.test(method))
$.ajaxSetup(
crossDomain: false
beforeSend: (xhr, settings) ->
if !csrfSafeMethod(settings.type)
xhr.setRequestHeader("X-CSRFToken", $.cookie('csrftoken'))
)
Read the documentation : CSRF
In other hand, as user1427661 suggests to you, it will be better to use HTTP GET method instead of POST, because you only need to read data and don't write anything. See the W3 docs.

Categories

Resources