I have a client Angular 2 application, and a Spring boot (1.4.1.RELEASE) server application. I wrote the client code to send a connection request to the server using the angular library ng2-stomp-service, and the server code to manage the connection request.
All works fine when using http, but when using https I see a connection error (HTTP/1.1 400) in the javascript console:
GET https://tomcatunisvid.lunagest.com/unisvid/workflow/046/mmesis4u/websocket [HTTP/1.1 400 358ms]
Firefox non può stabilire una connessione con il server wss://tomcatunisvid.lunagest.com/unisvid/workflow/046/mmesis4u/websocket.
In Firefox I found the request and the response headers:
-- REQUEST HEADERS
Accept text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8
Accept-Encodinggzip, deflate, br
Accept-Language it-IT,it;q=0.8,en-US;q=0.5,en;q=0.3
Cache-Control no-cache
Connection keep-alive, Upgrade
Host tomcatunisvid.lunagest.com
Origin https://tomcatunisvid.lunagest.com
Pragma no-cache
Sec-WebSocket-Extensions permessage-deflate
Sec-WebSocket-Key J2Aqm3n2r6vcE7F8SrPr3w==
Sec-WebSocket-Version 13
Upgrade websocket
User-AgentMozilla/5.0 (Macintosh; Intel Mac OS X 10.12; rv:56.0) Gecko/20100101 Firefox/56.0
-- RESPONSE HEADERS
Access-Control-Allow-Credentials true
Access-Control-Allow-Originh ttps://tomcatunisvid.lunagest.com
Cache-Control no-cache, no-store, max-age=0, must-revalidate
Connection close
Date Mon, 06 Nov 2017 11:50:42 GMT
Expires 0
Pragma no-cache
Server Apache/2.4.18 (Ubuntu)Transfer-Encoding chunked
Vary Origin
X-Content-Type-Options nosniff
X-Frame-Options DENY
X-XSS-Protection1; mode=block
and the response is empty.
Here is my relevant client code:
// In the constructor, where stomp is a StompService:
stomp.configure({
host: this.url,
debug: true,
queue: { 'init': false, '/user/queue/user/reply': false },
// headers: { 'upgrade': 'WebSocket' }
});
// in another method:
this.stomp.startConnect().
then(() => this.openSocket_OK_Callback(this)).
catch((error) => this.openSocket_KO_Callback(this, error));
Here is my relevant server code:
#Configuration
#EnableWebSocketMessageBroker
public class WebSocketConfig extends
AbstractWebSocketMessageBrokerConfigurer {
#Override
public void configureMessageBroker(MessageBrokerRegistry config) {
config.setApplicationDestinationPrefixes("/wfm");
config.enableSimpleBroker("/topic", "/queue");
}
#Override
public void registerStompEndpoints(StompEndpointRegistry registry) {
SessionDisconnectionListener sdl = new SessionDisconnectionListener();
String endpoint = "/workflow";
registry.addEndpoint(endpoint).
setAllowedOrigins("*").withSockJS();
registry.addEndpoint(endpoint).setHandshakeHandler(new DefaultHandshakeHandler(new TomcatRequestUpgradeStrategy())).
setAllowedOrigins("*").withSockJS();
}
}
Any hints how to solve this issue?
I put here the answer I found after some debugging, in case it is useful to others. In the release environment Tomcat was proxied by Apache Http Server. The solution in this case was to remove Apache Http Server and configure Https directly on Tomcat.
I'm currently working on site that uses various Ajax-requests to save, load and autocomplete data. It is build using C#, MVC and JQuery. All actions on the MVC controllers require the users to be authorized, and we use IdentityServer3 for authentication. It was installed using NuGet, and the current version is 2.3.0.
When I open the page and push buttons, everything is working just fine. The problem seem to occur when a certain session expires. If I stay idle for a while, and try to use an Ajax-function, it generates the following error:
XMLHttpRequest cannot load https://identityserver.domain.com/connect/authorize?client_id=Bar&redirect_uri=http%3a%2f%2flocalhost%3a12345&response_mode=form_post&response_type=id_token+token&scope=openid+profile+email+phone+roles [...]. No 'Access-Control-Allow-Origin' header is present on the requested resource. Origin 'http://localhost:12345' is therefore not allowed access.
From what I know about Ajax, the problem itself is pretty simple. The MVC site has lost track of the current session, and it is asking the client to authenticate again. The response I get from the Ajax-request is a "302 Found", with a Location-header that points to our IdentityServer. The IdentityServer happens to be on another domain, and while this works fine when you are performing regular HTTP-requests, it does not work particularly well for Ajax-requests. The "Same Origin Policy" is straight up blocking the Ajax-function from authenticating. If I refresh the page, I will be redirected to the IdentityServer and authenticate normally. Things will then go back to normal for a few minutes.
The solution is probably to add an extra header in the response message from the IdentityServer, that explicitly states that cross-origin requests are allowed for this service.
I am currently not getting this header from the IdentityServer (checked in Fiddler).
According to the docs, it should be enabled by default. I have checked that we have indeed enabled CORS this way:
factory.CorsPolicyService = new Registration<ICorsPolicyService>(new DefaultCorsPolicyService { AllowAll = true });
This is one of my clients:
new Client
{
Enabled = true,
ClientName = "Foo",
ClientId = "Bar",
ClientSecrets = new List<Secret>
{
new Secret("Cosmic")
},
Flow = Flows.Implicit,
RequireConsent = false,
AllowRememberConsent = true,
AccessTokenType = AccessTokenType.Jwt,
PostLogoutRedirectUris = new List<string>
{
"http://localhost:12345/",
"https://my.domain.com"
},
RedirectUris = new List<string>
{
"http://localhost:12345/",
"https://my.domain.com"
},
AllowAccessToAllScopes = true
}
These settings do not work. I am noticing that I have an extra forward slash in the URIs here, but if I remove them, I get the default IdentityServer-error that states that the client is not authorized (wrong URI). If I deploy the site (instead of running a localhost debug), I use the domain name without a trailing slash, and I get the exact same behaviour as I do in debug. I do notice that there is no trailing slash in the error message above, and I figured this could be the problem until I saw the same thing in the deployed version of the site.
I also made my own policy provider, like this:
public class MyCorsPolicyService : ICorsPolicyService
{
public Task<bool> IsOriginAllowedAsync(string origin)
{
return Task.FromResult(true);
}
}
... and I plugged it into the IdentityServerServiceFactory like this:
factory.CorsPolicyService = new Registration<ICorsPolicyService>(new MyCorsPolicyService());
The idea is for it to return true regardless of origin. This did not work either; exactly the same results as before.
I've read about a dozen other threads on this particular subject, but I'm getting nowhere. To my knowledge, we are not doing anything unusual when it comes to the setup of the different sites. It's all pretty much out-of-the-box. Any advice?
----- UPDATE -----
The problem persists. I have now tried some fresh tactics. I read somewhere that cookie authentication was bad for Ajax-requests, and that I should be using bearer tokens instead. I set this up in Ajax like this:
$(function () {
$(document).ajaxSend(function (event, request, settings) {
console.log("Setting bearer token.");
request.setRequestHeader("Authorization", "Bearer " + $bearerToken);
});
});
Both the console in Chrome and Fiddler confirms that the token is indeed present and sent by JQuery. The token I use comes from the access_token-property on claims principal object from HttpContext.GetOwinContext().Authentication.User.
This didn't do much. I still get a 302-response from the server, and Fiddler reveals that the token is not sent on the following Ajax-request (which is a GET-request) to the IdentityServer.
From there, I read this thread:
Handling CORS Preflight requests to ASP.NET MVC actions
I tried to put this code in to the startup.cs of the IdentityServer, but there does not appear to be a "preflight" request going in. All I see in Fiddler is this (from the beginning):
1 - The initial Ajax-request from the client to the MVC controller:
POST http://localhost:12345/my/url HTTP/1.1
Host: localhost:12345
Connection: keep-alive
Content-Length: pretty long
Authorization: Bearer <insert long token here>
Origin: http://localhost:12345
User-Agent: Mozilla/5.0 (Windows NT 10.0; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/47.0.2526.106 Safari/537.36
Content-Type: application/x-www-form-urlencoded; charset=UTF-8
Accept: application/json, text/javascript, */*; q=0.01
X-Requested-With: XMLHttpRequest
Referer: http://localhost:12345/my/url
Accept-Encoding: gzip, deflate
Accept-Language: nb-NO,nb;q=0.8,no;q=0.6,nn;q=0.4,en-US;q=0.2,en;q=0.2
Cookie: OpenIdConnect.nonce.<insert 30 000 lbs of hashed text here>
param=fish&morestuff=salmon&crossDomain=true
2 - The redirect response from the MVC controller:
HTTP/1.1 302 Found
Cache-Control: private
Location: https://identityserver.domain.com/connect/authorize?client_id=Bar&redirect_uri=http%3a%2f%2flocalhost%3a12345%2f&response_mode=form_post&response_type=id_token+token&scope=openid+profile+email [...]
Server: Microsoft-IIS/10.0
X-AspNetMvc-Version: 5.2
X-AspNet-Version: 4.0.30319
Set-Cookie: OpenIdConnect.nonce.<lots of hashed text>
X-SourceFiles: <more hashed text>
X-Powered-By: ASP.NET
Date: Fri, 15 Jan 2016 12:23:08 GMT
Content-Length: 0
3 - The Ajax-request to the IdentityServer:
GET https://identityserver.domain.com/connect/authorize?client_id=Bar&redirect_uri=http%3a%2f%2flocalhost%3a12345%2f&response_mode=form_post&response_type=id_token+token&scope=openid+profile+email [...]
Host: identityserver.domain.com
Connection: keep-alive
Accept: application/json, text/javascript, */*; q=0.01
Origin: http://localhost:12345
User-Agent: Mozilla/5.0 (Windows NT 10.0; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/47.0.2526.106 Safari/537.36
Content-Type: application/x-www-form-urlencoded; charset=UTF-8
Referer: http://localhost:12345/my/url
Accept-Encoding: gzip, deflate, sdch
Accept-Language: nb-NO,nb;q=0.8,no;q=0.6,nn;q=0.4,en-US;q=0.2,en;q=0.2
4 - The response from IdentityServer3
HTTP/1.1 302 Found
Content-Length: 0
Location: https://identityserver.domain.com/login?signin=<some hexadecimal id>
Server: Microsoft-IIS/8.5
Set-Cookie: SignInMessage.<many, many, many hashed bytes>; path=/; secure; HttpOnly
X-Powered-By: ASP.NET
Date: Fri, 15 Jan 2016 12:23:11 GMT
5 - The meltdown of Chrome
XMLHttpRequest cannot load https://identityserver.domain.com/connect/authorize?client_id=Bar&blahblahblah. No 'Access-Control-Allow-Origin' header is present on the requested resource. Origin 'http://localhost:12345' is therefore not allowed access.
I was having a similar issue using OWIN Middleware for OpenIDConnect with a different identity provider. However, the behavior occurred after 1 hour instead of 5 minutes. The solution was to check if the request was an AJAX request, and if so, force it to return 401 instead of 302. Here is the code that performed this:
app.UseOpenIdConnectAuthentication(new OpenIdConnectAuthenticationOptions
{
ClientId = oktaOAuthClientId,
Authority = oidcAuthority,
RedirectUri = oidcRedirectUri,
ResponseType = oidcResponseType,
Scope = oauthScopes,
SignInAsAuthenticationType = "Cookies",
UseTokenLifetime = true,
Notifications = new OpenIdConnectAuthenticationNotifications
{
AuthorizationCodeReceived = async n =>
{
//...
},
RedirectToIdentityProvider = n => //token expired!
{
if (IsAjaxRequest(n.Request))
{
n.Response.StatusCode = 401;//for web api only!
n.Response.Headers.Remove("Set-Cookie");
n.State = NotificationResultState.HandledResponse;
}
return Task.CompletedTask;
},
}
});
Then, I used an Angular interceptor to detect a statusCode of 401, and redirected to the authentication page.
I came across this problem as well and UseTokenLifetime = false was not solving the problem since you loose the token validity on STS.
When I tried to reach the authorized api method, I still got 401 even if I was valid on Owin.
The solution I found is keeping UseTokenLifetime = true as default but to write a global ajax error handler (or angular http interceptor) something like this:
$.ajaxSetup({
global: true,
error: function(xhr, status, err) {
if (xhr.status == -1) {
alert("You were idle too long, redirecting to STS") //or something like that
window.location.reload();
}
}});
to trigger the authentication workflow.
I had this issue recently, it was caused by the header X-Requested-With being sent with the AJAX request. Removing this header or intercepting it and handling it with a 401 will put you on the right track.
If you don't have this header, the issue is most likely being caused by a different header triggering the Access-Control-Allow-Origin response.
As you found, nothing you do in Identity Server regarding CORS will solve this.
As it turns out, the problem was in the client configuration in MVC. I was missing the UseTokenLifetime property, which should have been set to false.
app.UseOpenIdConnectAuthentication(
new OpenIdConnectAuthenticationOptions
{
ClientId = "Bar",
Scope = "openid profile email phone roles",
UseTokenLifetime = false,
SignInAsAuthenticationType = "Cookies"
[...]
For some reason, IdentityServer sets all these cookies to expire within 5 minutes of them being distributed. This particular setting will override IdentityServer's tiny expiration time, and instead use aprox. 10 hours, or whatever the default is in your client application.
One could say that this is good enough for solving the problem. It will however inevitably return if the user decides to spend 10 hours idling on the site, clicking nothing but Ajax-buttons.
https://github.com/IdentityServer/IdentityServer3/issues/2424
Assumptions:
.NET Framework 4.8 WebForms
OWIN-based auth lib i.e. Microsoft.Owin.Security.OpenIdConnect v4.2.2.0
UseOpenIdConnectAuthentication() with Azure AD endpoint
UseTokenLifetime=true
In Layout.Master:
$.ajaxSetup({
global: true,
error: function (xhr, status, err) {
if (xhr.status == 401) {
window.location.reload();
}
}
});
In startup.cs:
app.UseOpenIdConnectAuthentication(new OpenIdConnectAuthenticationOptions
{
...
Notifications = new OpenIdConnectAuthenticationNotifications()
{
...
RedirectToIdentityProvider = RedirectToIdentityProvider
}
});
...
public Task RedirectToIdentityProvider(RedirectToIdentityProviderNotification<OpenIdConnectMessage, OpenIdConnectAuthenticationOptions> context)
{
if (IsAjaxRequest(context.Request))
{
context.Response.StatusCode = 401;
context.Response.Headers.Remove("Set-Cookie");
context.State = NotificationResultState.HandledResponse;
}
}
public bool IsAjaxRequest(this IOwinRequest request)
{
if (request == null)
{
throw new ArgumentNullException("Woopsie!");
}
var context = HttpContext.Current;
var isCallbackRequest = false;
if (context != null && context.CurrentHandler != null && context.CurrentHandler is System.Web.UI.Page page)
{
isCallbackRequest = page.IsCallback;
}
return isCallbackRequest || (request.Cookies["X-Requested-With"] == "XMLHttpRequest") || (request.Headers["X-Requested-With"] == "XMLHttpRequest");
}
I would like to keep the websocket connection alive.
I am using RFC 6455 protocol as websocket protocol.
I tested this via Firefox 13 and Chrome 20.
===WEB SOCKET CLIENT===
GET / HTTP/1.1
Host: #.#.#.#:#
User-Agent: Mozilla/5.0 (Windows NT 5.1; rv:13.0) Gecko/20100101 Firefox/13.0.1
Accept: text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8
Accept-Language: ko-kr,ko;q=0.8,en-us;q=0.5,en;q=0.3
Accept-Encoding: gzip, deflate
Connection: keep-alive, Upgrade
Sec-WebSocket-Version: 13
Origin: http://its.secret.com
Sec-WebSocket-Key: TbJqTlFtGocvRvk/9stMhg==
Pragma: no-cache
Cache-Control: no-cache
Upgrade: websocket
===WEB SOCKET SERVER===
HTTP/1.1 101 WebSocket Protocol Handshake
Upgrade: websocket
Connection: Upgrade
Sec-WebSocket-Accept: outIACgIETcILj0NjzA0MhwP7uc=
the newline in this websocket protocol is CR/LF.
the next is the part of websocket client source.
var host = "ws://#.#.#.#:#";
try {
Rsocket = new WebSocket(host);
Rsocket.onopen = function(msg) {
alert("Welcome - status " + this.readyState);
};
Rsocket.onmessage = function(msg) {...
};
Rsocket.onclose = function(msg) {
alert("Disconnected - status " + this.readyState);
};
}
catch (ex) {
log(ex);
}
First, the success alert appears in the web browser.
Just after pressing OK, the disconnect alert appears.
Although pressing OK when the success alert appears, I would like the disconnect alert not to appear.
I have a simple method (running on Tomcat 6.0.35) that looks like so:
#RequestMapping(value = "/bla/d", method = RequestMethod.DELETE)
#ResponseStatus(HttpStatus.NO_CONTENT)
public void d(#RequestParam String d){
//logic here
}
When I send a DELETE request with post like parameters (d=gggg in the body) I get a 400 Bad Request.
But if I change it to
#RequestMapping(value = "/bla/d", method = RequestMethod.POST)
#ResponseStatus(HttpStatus.NO_CONTENT)
public void d(#RequestParam String d){
//logic here
}
It works perfectly.
I was using a Firefox Add-on to test it (and python and Spring's RestTemplate with same result) here's how the request look with POST(a is a cope pasted method named a with parameter a):
POST /bla/a HTTP/1.1
Host: ~~~~:8080
User-Agent: Mozilla/5.0 (Windows NT 6.1; WOW64; rv:13.0) Gecko/20100101 Firefox/13.0
Accept: text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8
Accept-Encoding: gzip, deflate
Connection: keep-alive
Content-Type: application/x-www-form-urlencoded; charset=UTF-8
Content-Length: 7
Pragma: no-cache
Cache-Control: no-cache
a=asdas
HTTP/1.1 204 No Content
Server: Apache-Coyote/1.1
Date: Tue, 12 Jun 2012 09:29:46 GMT
And delete looks like:
DELETE /bla/d HTTP/1.1
Host: ~~~~~:8080
User-Agent: Mozilla/5.0 (Windows NT 6.1; WOW64; rv:13.0) Gecko/20100101 Firefox/13.0
Accept: text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8
Accept-Encoding: gzip, deflate
Connection: keep-alive
Content-Type: application/x-www-form-urlencoded; charset=UTF-8
Content-Length: 7
d=asdas
HTTP/1.1 400 Bad Request
Server: Apache-Coyote/1.1
Content-Type: text/html;charset=utf-8
Content-Length: 971
Date: Tue, 12 Jun 2012 09:30:04 GMT
Connection: close
Please help me, I might be missing something stupid but I just can't see it.
My original problem was sending an array via post-like body with DELETE request but it seems that something more basic is wrong.
Well after doing some research and debugging I've found out that Spring's ServletWebREquest calls getParameterValues of org.apache.catalina.connector.RequestFacade.getParameterValues which calls getParameterValues in which I've found the following line (Request.java 2599-2600):
if (!getMethod().equalsIgnoreCase("POST"))
return;
Which kills any attempt to send POST-like parameters with DELETE which means Tomcat is actively restricts this use-case even though the RFC does not restrict such usage(although it does say that some existing implementations may reject such requests, Tomcat just throws it's parameters away).
What brings one that's using Spring and Tomcat and trying to send a DELETE requests with parameters to ugly solutions like getting all the request body with #RequestBody and extracting it manually which makes your supposedly innocent method that just wants to delete something aware of some a Map that contains the request body.
#fmucar
I was having a similar issue and the resolution that I found was to add the fields in the query string. I would still like to know the reasons why a form body would be excluded in this way, but for now this is a work-around.
So for your example it would mean adding
?a=asdas
to the Host: ~~~~~:8080 URL.
I am using spring-webmvc:3.2.4.RELEASE so I'm not sure if this will work in your version or not.
This is a pretty old post, but in case anyone else is looking for how to enable #RequestParam on DELETE methods, here's what I did on tomcat 8.5.4.
#Value("${server.parseBodyMethods}")
private String parseBodyMethods;
#Bean
public TomcatEmbeddedServletContainerFactory containerFactory() {
return new TomcatEmbeddedServletContainerFactory() {
protected void customizeConnector(Connector connector) {
super.customizeConnector(connector);
connector.setParseBodyMethods(parseBodyMethods);
}
};
}
Plug in 'POST,DELETE' to that customizer, and your delete request parameters should start working.
I found parseBodyMethods in org.apache.catalina.connector.Connector, and here is Tomcat's documentation on it:
This is useful in RESTful applications that want to support POST-style semantics for PUT requests. Note that any setting other than POST causes Tomcat to behave in a way that goes against the intent of the servlet specification. The HTTP method TRACE is specifically forbidden here in accordance with the HTTP specification. The default is POST (Source)
My goal is to upload file with ajax-way.
I use this javascript library http://valums.com/wp-content/uploads/ajax-upload/demo-jquery.htm
There is a link on my page like "Upload" button on example page.
When I click it, "Open file" dialog is open.
I choose file and form is automatically submitted.
This is my javascript code.
var upload_btn = $('#upload-opml');
new AjaxUpload(upload_btn.attr('id'), {
action: upload_btn.attr('href'),
name: 'opml',
onComplete: function (file, response) {
//
}
});
This is server code in Ruby on Rails.
def upload_opml
render :text => 'hello'
end
Headers, taken from Firebug.
>> Response headers
Server nginx/0.7.62
Date Wed, 09 Jun 2010 19:03:28 GMT
Content-Type text/html; charset=utf-8
Connection keep-alive
Etag "5d41402abc4b2a76b9719d911017c592"
X-Runtime 18
Content-Length 5
Cache-Control private, max-age=0, must-revalidate
Set-Cookie _RssWebApp_session=BAh7CDoPc2Vzc2lvbl9pZCIlMzJhMTQ0ZWZhOGM3YmIwODFhZmFmNjkwYTI1YWQ2ZjQ6EF9jc3JmX3Rva2VuIjEvZHVzdm1NOVlMTUF6bEw3cGRFT2I3RzZvcVJZUU42bCtMNS9PVVYrNHdBPToMdXNlcl9pZGkG--13f1950a9530591881404fbfab7b1246f98f0d81; path=/; HttpOnly
>> Request headers
Host readbox.cz
User-Agent Mozilla/5.0 (X11; U; Linux i686; ru; rv:1.9.2) Gecko/20100115 Firefox/3.6
Accept text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8
Accept-Language ru,en-us;q=0.7,en;q=0.3
Accept-Encoding gzip,deflate
Accept-Charset windows-1251,utf-8;q=0.7,*;q=0.7
Keep-Alive 115
Connection keep-alive
Referer http://readbox.cz/view
Cookie _RssWebApp_session=BAh7CDoPc2Vzc2lvbl9pZCIlMzJhMTQ0ZWZhOGM3YmIwODFhZmFmNjkwYTI1YWQ2ZjQ6EF9jc3JmX3Rva2VuIjEvZHVzdm1NOVlMTUF6bEw3cGRFT2I3RzZvcVJZUU42bCtMNS9PVVYrNHdBPToMdXNlcl9pZGkG--13f1950a9530591881404fbfab7b1246f98f0d81; login=1; APE_Cookie=%7B%22frequency%22%3A11%7D; show-tsl=0
But in Firefox I get an error
!:#8?BC http://readbox.cz (document.domain=http://readbox.cz) >B:070=> 2 #07#5H5=88 =0 ?>;CG5=85 A2>9AB20 HTMLDocument.readyState 87 http://readbox.cz (document.domain =5 1K; CAB0=>2;5=).
[Break on this error] if (doc.readyState && doc.readyState != 'complete') {
In Google Chrome
Unsafe JavaScript attempt to access frame with URL http://readbox.cz/subscriptions/upload_opml from frame with URL http://readbox.cz/view#/posts/all. Domains, protocols and ports must match.
/javascripts/ajaxupload.js?1276107673:574
Uncaught TypeError: Cannot read property 'readyState' of undefined
Domain readbox.info points to 127.0.0.1. It's for development.
I had the same problem and I fix it editing the ajaxupload library, with this commit:
https://github.com/felipelalli/ajax-upload/commit/9307f5eb6ded1ec63eac828a7ef4b8187acb9617
I already sent a pull request to the author.
I had this problem when I was using the sandbox developer environment (opensocial for Orkut). I just check now if "doc" is undefined. The upload works fine, but the callback now has no answer (the answer is undefined).
I don't know exactly what is the cause, but I think it is some kind of limitation of the dev environment.
If you want to download the fix, please check it out: https://github.com/felipelalli/ajax-upload/commits/3.9.1