Can Ajax make a Cross-Origin Login? - ajax

I'm trying to login from one of my servers to another in order to send cross-origin requests that requires being logged. is it possible?
I have two web servers, A and B. Lets say www.a.com and www.b.com.
B has an API that can be used only if the client is logged in. I need to use that API from A clients.
So, I send from A client an ajax (post) login request to B. B responses with CORS headers, the session cookie and a successful redirection to B's home/index.
But when I make a second ajax request (jsonp request) from A client to B server, this request doesn't send the previous session cookie received, therefore the login request failed.
If I login to www.b.com manually (in a second browser tab), all requests from A to B are successful detected as a logged user, so, the B API works from A.
I think that the session cookie received from my login requests is not being saved to the browser.
This is my login request:
$.post("www.b.com/login", { 'j_username': 'username', 'j_password': 'password' } );
Using:
jqXHR.withCredentials = true;
settings.crossDomain = true;
Response headers:
Access-Control-Allow-Headers:x-requested-with
Access-Control-Allow-Methods:POST, GET, OPTIONS
Access-Control-Allow-Origin:*
...
Location:http://www.b.com/home
...
Set-Cookie:JSESSIONID=tY++VWlMSxTTUkjvyaRelZ0o; Path=/
The cookie received is being saved to www.a.com or to www.b.com? How can I set this cookie to www.b.com from an A client ajax request? I think that is the problem.

As apsillers said, we can't use the wildcard Access-Control-Allow-Origin:*.
But this doesn't solved the problem.
I was setting jqXHR.withCredentials = true; inside a beforeSend handler function.
$.post({
...
beforeSend: function(xhr) {
xhr.withCredentials = true;
},
...
});
And for some reason, this doesn't work. I had to set the use of credentials directly:
$.post({
...
xhrFields: {
withCredentials: true
},
...
});
This code works perfectly !
Thanks you.

Related

Axios : put on hold requests

I have the following axios interceptor.
It checks the validity of the stored token. If not valid, a request is fired to retrieve and store the refreshed token.
axios.interceptors.request.use(async config =>
if(checkValidity(localStorage.getItem('token')) === false) {
const response = await axios.get('http://foobar.com/auth/refresh');
localStorage.setItem('token', response.headers['token']);
config = getConfigFromResponse(response);
}
return config;
});
It works great. The problem is that if I have many requests with invalid token then many requests to http://foobar.com/auth/refresh are done to refresh it.
Is it possible to put all the requests in an array and fire them after when the refresh is done ?
The idea is to avoid catching 401 errors and replaying the request : this is why I want to "save" the requests while the token is being retrieved and then fire them when the token is ready.

CSRF Token Name on Django Documentation is not Matching the Actual Name of the Variable in AJAX Header

I was struggling to send and recieve CSRF token, and I found, in the end, that Django was not able to get the token value because its name was different from the recommended one in its documentation. Why?
(I am doing AJAX on a HTTPS address and requests are cross-site.)
Django documentation recommends that I add token to AJAX header in following way:
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);
}
}
});
Here, the name is X-CSRFToken, which somehow becomes HTTP_X_CSRFTOKEN.
On the other hand, Django is looking up the cookie under CSRF_COOKIE.
Line 278 in csrf.py of CsrfViewMiddleware:
csrf_token = request.META.get('CSRF_COOKIE')
if csrf_token is None:
# No CSRF cookie. For POST requests, we insist on a CSRF cookie,
# and in this way we can avoid all CSRF attacks, including login
# CSRF.
return self._reject(request, REASON_NO_CSRF_COOKIE)
I cannot change the variable name because I get this error:
Request header field CSRF_COOKIE is not allowed by Access-Control-Allow-Headers in preflight response.
So, I ended up changing the variable name in the source code from CSRF_COOKIE to HTTP_X_CSRFTOKEN. Are there any way to make this work?
(I do not do #csrf_exempt, so please do not recommend.)
The problem is not from Django, if you read closely here: https://docs.djangoproject.com/en/2.0/ref/csrf/#how-it-works you will understand how it works and what kind of logic they follow.
The problem is that you are not allowing the headers:
Request header field CSRF_COOKIE is not allowed by Access-Control-Allow-Headers in preflight response.
If you search for this ACAH you will find that you must edit your server config file to allow this kind of posts.
The other case is that you may not be sending properly the header and that's why it's looking for the cookie. In that case you can try adding this to your header:
xhr.setRequestHeader('X-CSRFToken': $('meta[name="token"]').attr('content') });

cross-(sub)domain jQuery.ajax query to flask app receives cookies but can't send them

I have two domains:
https://localhost.com # SPA served via Nginx
https://api.localhost.com # Flask API proxied by nginx (uwsgi_pass)
and added both to my hosts file via 127.0.0.1 localhost.com api.localhost.com.
I've added server-side oauth2 via GitHub's oauth service and I'm able to authenticate (I receive an access token and can query the GitHub API). This is the "flow":
user calls: https://localhost.com
ajax: https://api.localhost.com/v1/login # gets the app's Github OAuth url
# sets a session cookie for
# localhost.com
user calls: https://github.com/... # gets redirected to log in ect.
redirect: https://api.localhost.com/callback
# github redirects to the callback
# can access some session cookie
# (I assume) from localhost.com
redirect: https://localhost.com # now has two session cookies:
# localhost.com
# api.localhost.com
ajax: https://api.localhost.com/v1/username
# this is where it breaks
I set both, the server-side CORS headers for api.localhost.com (snippet from nginx.conf)
add_header 'Access-Control-Allow-Origin' "https://localhost.com";
add_header 'Access-Control-Allow-Credentials' 'true';
add_header 'Access-Control-Allow-Methods' 'GET, POST, OPTIONS';
add_header 'Access-Control-Allow-Headers' 'Content-Type, *';
and the withCredentials parameter in the queries:
$.getJSON("https://api.localhost.com/v1/login",
{
xhrFields: { withCredentials: true },
},
(response) => {
this.login_url = response;
});
$.getJSON("https://api.localhost.com/v1/username",
{
xhrFields: { withCredentials: true },
},
(response) => {
console.log(response)
this.username = response;
});
The first cross-domain ajax: https://api.localhost.com/v1/login works and sets a cookie, so I think it's configured correctly. (I might be wrong.)
However, the second ajax: https://api.localhost.com/v1/username doesn't seem to send the session cookie, as I am left with an empty session in flask:
from flask import session
# [ ... ]
def username_get():
return str(list(session.keys())) # returns "[]"
I can manually decrypt both session cookies (localhost.com and api.localhost.com). They both have the same content (username, access token and oauth_state [required during GitHub OAuth]) and indicate that authentication was successful.
What I don't understand is:
Why do I get two cookies, in particular why is the session cookie from ajax: https://api.localhost.com/v1/login stored in localhost.com and not api.localhost.com?
Github OAuth is working, meaning redirect: https://api.localhost.com/callback receives the session cookie set by https://api.localhost.com/v1/login and can use the data (oauth_state). However the subsequent ajax: https://api.localhost.com/v1/Username does not send the session cookie. Why?
I hope I've included all the necessary bits. This is a very puzzling problem and none of the existing SO questions helped me past this point. ANY help or pointers are highly appreciated.
(2) I was using $.getJSON wrong. It's so simple that it hurts.
As per the documentation:
jQuery.getJSON( url [, data ] [, success ] )
and data will be appended to the URL as a query parameter. Thus replacing the call with
$.ajax({
dataType: "json",
url: "https://api.localhost.com/v1/username",
xhrFields: { withCredentials: true },
success: (response) =>
{
this.username = response;
}
});
(not using the shorthand) sets the appropriate xhrField and now correctly sends the cookie.
(1) Probably caused by caching or some temporary files. I brought down the entire server, cleared all temp files and did the same with my browser (Chrome). After restarting, I only get a single session cookie for api.localhost.com which is the expected behavior.

CORs error when accessing Square V2 API

I'm making a client-side request out to V2 of the Square API using Vue and Axios. My Vue component is as follows:
import axios from 'axios';
export default {
mounted() {
var instance = axios.create({
baseURL: 'https://connect.squareup.com/v2/',
timeout: 1000,
headers: {
'Authorization': 'Bearer xxxxxxxxxxxxxxxxx',
'Accepts': 'application/json',
'Content-Type': 'application/json'
}
});
instance.get('catalog/list')
.then(function (response) {
console.log(response);
}) ;
}
}
However, when I make that call, I receive the following error:
Failed to load https://connect.squareup.com/v2/catalog/list: Response to preflight request doesn't pass access control check: No 'Access-Control-Allow-Origin' header is present on the requested resource. Origin 'https://local-env.dev' is therefore not allowed access. The response had HTTP status code 403.
That error suggests that there is some configuration that has to happen on the Square side, but I saw no opportunity to whitelist domains, etc.
Has anyone come across this error before, regardless of service, and if so, how did you resolve?
I don't think the Square API supports being called from a browser. I used Postman to do an OPTIONS request on https://connect.squareup.com/v2/catalog/list and the response was a NOT_FOUND. The OPTIONS request is needed for proper CORS support.
Plus, if you did this, I would think your auth token would need to be sent to the client -- thus exposing it to everyone. It looks like the Square API is only designed to be called from a server. But that is just based on me skimming the docs a bit. I have no experience using their API.
When doing OAuth authorization request you are not supposed to do it from your application. Create and URL with the parameters and open it in a new browser window or tab, Something like:
const grants='MERCHANT_PROFILE_READ CUSTOMERS_READ CUSTOMERS_WRITE PAYMENTS_READ PAYMENTS_WRITE PAYMENTS_WRITE_ADDITIONAL_RECIPIENTS PAYMENTS_WRITE_IN_PERSON';
const params = new HttpParams()
.set('scope', grants)
.set('client_id', <YourSquareApplicationId>)
.set('state', '1878789');
const requestUrl = `${<squareUrl>}/oauth2/authorize?${params.toString()}`;
window.open(requestUrl, "_blank");
That new window is supposed to ask the end user to login to his account and accept or deny the request.

ajax json request , always returning error

Hello i've got a problem with ajax json request. Im always getting an error, even if the requests are succeeded. At the moment i have this code:
function sumbitLoginForm(user, pass) {
if (user.trim() == '' || pass.trim() == '') {
alert("You must enter username and password!");
} else {
$.ajax({
type : 'POST',
url : 'https://url.php',
dataType : 'json',
data : {
userlogin : user,
userpass : pass
},
contentType: "application/json;",
success : function(data) {
$("#images").html("uspeshno");
},
error : function(data) {
$("#images").html("greshka");
}
});
}
return false;
}
$(document).ready(function() {
clearPageInputs();
$("#submitButton").click(function() {
sumbitLoginForm($("#username").val(), $("#password").val());
});
});
Im always getting an error , no matter what username and password i type . But the status of request is changing , if i type correct user and pass i get status 302 Moved temporarly , but when i type wrong user or pass i get status 200 OK . What am i doing wrong ?
PRG Pattern and Ajax
It looks like your server returns a HTTP 200 status code when the userid and password will not validate. This is proper behavior, as HTTP error codes not meant for application errors, but for HTTP protocol errors.
When the userid and password are matched succesfully, you are redirected to another page. This is also normal behavior, e.g. to prevent other people to re-use your login credentials using the back key.
This is called the Post/Redirect/Get pattern.
See: http://en.wikipedia.org/wiki/Post/Redirect/Get
The problem is that the PRG pattern does not play nice with Ajax applications. The redirect should be handled by the browser. It is therefore transparent for the jQuery code. The Ajax html response will be the page that is mentioned in the Location header of the 302. Your Ajax application will not be able to see that it is being redirected. So your are stuck.
In one of my projects I solved this on the server side. If I detected an Ajax call, I would not send a redirect but a normal 200 response. This only works if you have access to the server code.
If you cannot change the redirect for your Ajax calls, then you can parse the response headers or the html to see if you were being redirected and act accordingly. Probably the login will set a cookie, so you might try and look for the presence of that cookie.

Resources