Django-HttpRedirect fails in Internet explorer - django

I have a Facebook application using Django. In one of my views I use following piece of code to make user logged-in.
In IE, return HttpResponseRedirect line fails with error message "This content cannot be displayed in a frame...", although other browsers are working fine.
Do you have an idea, why IE fails for HttpResponseRedirect?
(This is problem is produced on IE9 on Windows 7, server is using django-1.3)
def auto_login(request):
username = request.GET['username']
password = request.GET['password']
user = authenticate(username=username, password=password)
if user is not None:
if user.is_active:
login(request, user)
theURL='http://apps.facebook.com/myapp/'
return HttpResponseRedirect(theURL)
else:
return HttpResponse("disabled account")
else:
return HttpResponse("Invalid login")

This can be two things, both related to the browser security model.
Option 1 is the redirect to another domain.
Clients may decide to follow the redirect, or to refuse. In particular a HTTP 307 redirect (which allows forwarding of POST data) is not always accepted by clients.
Option 2 is related to the redirect of a resource with HTTP method POST url to another resource with method GET.
If the HTTP method of the current view and the redirect are different (i.e. HTTP POST against the /login url vs. HTTP GET of the facebook/myapp), at least IE8 will refuse to redirect. I'm not sure of this has been changed in IE9.
There's a few things you could try.
You could try another HTTP response code. Assuming there is no need to forward the HTTP parameters from the original request to the redirected request, a response code 303 would be better than a 307.
If your situation involves a redirect of an HTTP POST resource to the external HTTP GET resource at facebook, another attempt is put an extra redirect in the middle:
POST resource on yoursite.com --> redirect to GET resource on yoursite.com --> external redirect to facebook domain.
The "extra redirect" option could fix one browser but break another (browsers have limits on redirects, which may vary per browser type and version). If you would get into this situation you may need to detect the user-agent and switch between IE and other browsers.
A few good links:
Django/IE8 Admin Interface Weirdness
http://en.wikipedia.org/wiki/List_of_HTTP_status_codes

Related

Django all auth facebook login with Use Strict Mode for Redirect URIs

Hi I am trying to implement Facebook login for my website using Django Allauth.
As we can no longer disable Use Strict Mode for Redirect URIs I am getting an error when I try to login via facebook.
The callback URL formed at the time of Facebook login is of this format -
https://example.com/accounts/facebook/login/callback/?code=AQB7W48oY-1XxZv2xU9iahxS80ZPs4oBNLlXWTY7Y93dclyIElEPG-jWKB5ELV7Pv11ckcRYg3L67Wfcz6xqC8yhNLBaFaOQjd4F2AEp8nfScltnY3LoY79g9NjtslCSbQnSlc_hDdBm_rxQtScz-rLChNvAJaky3KYMG_USSTkm9qdyvw5lIMdcIHQjz3CTF8KdgmuFG1T8_WvVqdGDEpfhC_PD7w5tnkcChBEowHnWR656DYa1wrMR1fbP2rqxBocNn6fKPCy_GM_DZynPp8mx0F0YP55vzw2Kv8KchB2nxCaHwQ4dRvJq785w5CfCgDVc6REhbc3CNG2KqZxdxjuG&state=eukVyjHYk04X#_=_
This URL contains the query params code and state because of which it is not an exact match and I checked it via Redirect URI to Check which reported it as invalid.
So on the authentication_error.html I get the following error.
{'provider': 'facebook', 'code': 'unknown', 'exception':
OAuth2Error('Error retrieving access token:
b'{"error":{"message":"Can\'t load URL: The domain of this URL
isn\'t included in the app\'s domains. To be able to load this
URL, add all domains and sub-domains of your app to the App Domains
field in your app
settings.","type":"OAuthException","code":191,"fbtrace_id":"AxoTkIBeoUSKsxuWvMx-Wg4"}}'',)}
My Valid OAuth Redirect URIs has the following URL's
https://example.com/accounts/facebook/login/callback/
https://www.example.com/accounts/facebook/login/callback/
Please help me with this issue, I have looked into all the existing issue but haven't found a solution.
For anyone facing a similar issue, it could be because you missed to add this line to your settings.py file.
ACCOUNT_DEFAULT_HTTP_PROTOCOL = 'https'

ExtJS 5 application + Django rest framework CORS error when changing URL of store

I am developing a ExtJS application that uses a Django-rest-framework service. I am using CORS headers to allow fetching the data from the service (https://github.com/OttoYiu/django-cors-headers).
What happens is that at a point in time I want to change the URL from the store. And when I do that I get the following error:
XMLHttpRequest cannot load http://10.98.0.241:8000/reacsearch/as?_dc=1418831884352&page=1&start=0&limit=25. The request was redirected to 'http://10.98.0.241:8000/reacsearch/as/?_dc=1418831884352&page=1&start=0&limit=25', which is disallowed for cross-origin requests that require preflight.
In the settings.oy I define the following properties for the CORS
CORS_ALLOW_METHODS = (
'GET',
'OPTIONS'
)
CORS_ORIGIN_ALLOW_ALL = True
This works fine when I use URLs to list all the elements in my database, however when I change the store for another URL I get the error above. Also the link works fine in the browser.
The store url change is made this way:
var store = Ext.getStore(storeName);
store.getProxy().setUrl(newURL);
store.load();
The difference between the views, is that the two that work on the application are viewsets, while the other is just a generic list
class Example1viewset(viewsets.ModelViewSet):
"""
API endpoing that allows metabolites to be viewed.
"""
queryset = examples1.objects.all()
serializer_class = Example1Serializer
class Example1SearchList(generics.ListAPIView):
serializer_class = Example1Serializer
def get_queryset(self):
queryset = Example.objects.all()
if 'attr' in self.kwargs:
queryset = queryset.filter(Q(attribute1__contains=self.kwargs['attr']) | Q(attribute2__contains=self.kwargs['abbr']))
return queryset
Like I mentioned both examples work fine in the browser (even accessing through other computers in the network), however in the application when changing the URL of the store I get the CORS error. Does anyone has any idea why this is happening?
Thank you.
Edit:
Just for clarification, the problem is not in changing the url of the store. As I tried to set those urls as defaults, but they are not working when accessing from the application.
My urls.py file:
router = routers.DefaultRouter()
router.register(r'example', views.Example1ViewSet)
# Wire up our API using automatic URL routing.
# Additionally, we include login URLs for the browsable API.
urlpatterns = [
url(r'^', include(router.urls)),
url(r'^reacsearch/(?P<attr>.+)/$', Example1SearchList.as_view()),
url(r'^api-auth/', include('rest_framework.urls', namespace='rest_framework'))
Can it be that the problem is related with the fact that I am not adding the search list to the router?
Edit2
Problem solved since I was trying to fetch data from a different domain. I changed the type of store to jsonp in Extjs, and I also allowed my rest service to render data as jsonp.
Just a reminder if anyone comes accross this same problem, it is necessary to add ?format=jsonp to the store url:
http://my/url/?format=jsonp
Since it looks like an alternate solution was found, I'll explain what the issue appeared to be as well as why the alternative works.
XMLHttpRequest cannot load first url. The request was redirected to 'second url', which is disallowed for cross-origin requests that require preflight.
The issue here is that you are telling Django to enforce the trailing slash, which makes it automatically redirect urls without a trailing slash to urls with a trailing slash, assuming that one exists. This is why, as stated in the error, the request was redirected to the second url, which you can tell has the missing trailing slash. This is controlled by the APPEND_SLASH Django setting which is True by default.
The problem is that when CORS is doing a preflight request, which is what allows it to determine if the request can be made, there must be a valid response at the requested URL. Because you are redirecting the request, the preflight request fails and you're stuck without your information.
You can fix this by adding the trailing slash in your code. There appear to be a few solutions for doing this with ext, but I personally can't recommend a specific one. You can also manually set the url to use the trailing slash, which sounds like what you were doing previously.
Or you can use JSONP...
You've found the alternative solution, which is to use JSONP to make the request instead of relying on CORS. This gets around the preflight issue and works in all major browsers, but there are some drawbacks to consider. You can find more information on CORS vs JSONP by looking around.
You're going to need CORS if you want to push any changes to your API, as JSONP only supports GET requests. There are other advantages, such as the ability to abort requests, that also comes with CORS.

Angular can't get CSRF cookie from Django

I did a lot of research on this topic, but it's still not working for me.
I set my csrftoken cookie in Django,and it does in the response object.
But in any browser, it says no cookies in this site
Backend:
#ensure_csrf_cookie
def home(request):
csrf_token = get_token(request)
response = HttpResponse()
response = render(request, 'index.html')
response.set_cookie(key='csrftoken', value=csrf_token)
return response
Angular:
myapp.config(function($httpProvider){
//I use this when in angular1.0.x
//$http.defaults.headers.post['X-CSRFToken'] = $cookies['csrftoken'];
//now in angular1.2.x I use code below. but none of them works
$httpProvider.defaults.xsrfCookieName = 'csrftoken';
$httpProvider.defaults.xsrfHeaderName = 'X-CSRFToken';
});
When I do a POST I get message
Failed to load resource: the server responded with a status of 403 (FORBIDDEN)
Also if I print out header info in the $http error function:
console.log(header('Set-Cookie'));
console.log(header('Access-Control-Allow-Headers'));
console.log(header('Access-Control-Allow-Methods'));
all of these three are null.
I can't figure it why! Especially, it works fine in localhost, either Firefox or Chrome, but in an Apache server, always no cookie in this site.
Is there any setting should I do? Can anyone help my with this issue?
I'm not sure this will help, but your view is terribly written. You're trying to force the csrf in about five different ways, and you also have some redundant lines that don't do anything (you do response = HttpResponse() and then override it completely, making that line completely void). so there's a good chance one of them is screwing things over.
The point is - when you use render you don't need to do anything else to enforce the csrf (you know, except for making sure it's enabled). That's the point of using it over render_to_response. Try this much simpler version and see how much it helps:
def home(request):
return render(request, 'index.html')
Please check the domain of the cookie set by Django.
Be aware of cross-domain requests.
$http docs : Angular provides a mechanism to counter XSRF, When performing XHR requests but will not be set for cross-domain requests.
Here is a small lib that might help you https://github.com/pasupulaphani/angular-csrf-cross-domain/blob/master/dist/angular-csrf-cross-domain.js
Try including the ngCookies module in your application.
myApp.run(function ($http, $cookies) {
$http.defaults.headers.common['X-CSRFToken'] = $cookies.csrftoken;
});

How to enable https in Django-auth generated pages?

Using the Django-auth application (Django version 1.3), I want to have my login page go to https://mysite.com/login/. Currently, I'm using:
# urls.py
from django.contrib.auth.views import login
urlpatterns = patterns('', url(r'^login/$', login, name='login-view'),)
# navbar.html
<li id="nav-login"><a href="{% url login-view %}" ><b>Login</b></a></li>
which works nicely, but goes to http://mysite.com/login/.
Is there some way to tell Django-auth what prefix (https) to use, when it reverses the view name? I've read the entire manual page, and haven't found anything that covers it. Or maybe some way to tell the url tag to go to https?
Or is the only option to specify the entire URL manually? I hope not :) And given how powerful Django has been so far, I can't believe it wouldn't have that ability - I must be overlooking it. :)
Set OS environmental variable HTTPS to on
You need to enable the OS environmental variable HTTPS to 'on' so django will prepend https to fully generated links (e.g., like with HttpRedirectRequests). If you are using mod_wsgi, you can add the line:
os.environ['HTTPS'] = "on"
to your wsgi script. You can see the need for this by reading django/http/__init__.py:
def build_absolute_uri(self, location=None):
"""
Builds an absolute URI from the location and the variables available in
this request. If no location is specified, the absolute URI is built on
``request.get_full_path()``.
"""
if not location:
location = self.get_full_path()
if not absolute_http_url_re.match(location):
current_uri = '%s://%s%s' % (self.is_secure() and 'https' or 'http',
self.get_host(), self.path)
location = urljoin(current_uri, location)
return iri_to_uri(location)
def is_secure(self):
return os.environ.get("HTTPS") == "on"
Secure your cookies
In settings.py put the lines
SESSION_COOKIE_SECURE = True
CSRF_COOKIE_SECURE = True
and cookies will only be sent via HTTPS connections. Additionally, you probably also want SESSION_EXPIRE_AT_BROWSER_CLOSE=True. Note if you are using older versions of django (less than 1.4), there isn't a setting for secure CSRF cookies. As a quick fix, you can just have CSRF cookie be secure when the session cookie is secure (SESSION_COOKIE_SECURE=True), by editing django/middleware/csrf.py:
class CsrfViewMiddleware(object):
...
def process_response(self, request, response):
...
response.set_cookie(settings.CSRF_COOKIE_NAME,
request.META["CSRF_COOKIE"], max_age = 60 * 60 * 24 * 7 * 52,
domain=settings.CSRF_COOKIE_DOMAIN,
secure=settings.SESSION_COOKIE_SECURE or None)
Direct HTTP requests to HTTPS in the webserver
Next you want a rewrite rule that redirects http requests to https, e.g., in nginx
server {
listen 80;
rewrite ^(.*) https://$host$1 permanent;
}
Django's reverse function and url template tags only return relative links; so if you are on an https page your links will keep you on the https site.
As seen in other StackOverflow questions, you could implement middleware that would automatically redirect the login page to a secure version.
If you are really serious about security, you should probably migrate the entire website to SSL. From the EFF's How to Deploy HTTPS Correctly:
You must serve the entire application domain over HTTPS. Redirect HTTP requests with HTTP 301 or 302 responses to the equivalent HTTPS resource.
Some site operators provide only the login page over HTTPS, on the theory that only the user’s password is sensitive. These sites’ users are vulnerable to passive and active attack.

best way to send a variable along with HttpResponseRedirect

I am reading a djangobook and get questions about HttpResponseRedirect and render_to_response.
suppose I have a contact form, which posts data to confirm view. It goes through all the validation and database stuff. Then, as a usual way, I output the html with
return render_to_response('thank_you.html',
dict(user_code = user_code),
context_instance=RequestContext(request))
However, the book suggested "You should always issue a redirect for successful POST requests." because if the user "Refresh" on a this page, the request will be repeated. I wonder what's the best way to send the user_code along through HttpResponseRedirect to the thank_you.html.
Pass the information in a query string:
thank_you/?user_code=1234
Or use a session variable.
The problem with a query string is that the user can see the data.
When you send a redirect, you are sending the user back a response (a 302 HTTP response) and they are then making an entirely new request to the provided URL. That's a completely new request/response cycle so there is no way to supply data unless you save it in a session variable, cache, cookie etc.
What you can do instead of telling the user to redirect, is to call the view you want to show them yourself from within the same request (i.e. at the point you would issue the redirect) and then you could pass whatever you liked.