views:

12441

answers:

8

I have a Flex file upload script that uses URLRequest to upload files to a server. I want to add support for http authentication (password protected directories on the server), but I don't know how to implement this - I assume I need to extend the class somehow, but on how to I'm a little lost.

I tried to modify the following (replacing HTTPService with URLRequest), but that didn't work.

private function authAndSend(service:HTTPService):void{        
   var encoder:Base64Encoder = new Base64Encoder();        
   encoder.encode("someusername:somepassword");        
   service.headers = {Authorization:"Basic " + encoder.toString()};
   service.send();
}

I should point out that I'm not knowledgeable when it comes to ActionScript / Flex, although I have managed to successfully modify the upload script somewhat.

[Edit] - here is an update of my progress, based on the answer below, although I still cannot get this to work:

Thank you for your assistance. I've tried to implement your code but I've not had any luck.

The general behaviour I'm experiencing when dealing with HTTP authenticated locations is that with IE7 all is well but in Firefox when I attempt to upload a file to the server it displays an HTTP authentication prompt - which even if given the correct details, simply stalls the upload process.

I believe the reason IE7 is ok is down to the the session / authentication information being shared by the browser and the Flash component - however, in Firefox this is not the case and I experience the above behaviour.

Here is my updated upload function, incorporating your changes:

private function pergress():void 
{
if (fileCollection.length == 0) 
  {
  var urlString:String = "upload_process.php?folder="+folderId+"&type="+uploadType+"&feid="+formElementId+"&filetotal="+fileTotal;
  if (ExternalInterface.available) 
    {
    ExternalInterface.call("uploadComplete", urlString);
    }
  }
if (fileCollection.length > 0) 
  {
  fileTotal++;
  var urlRequest:URLRequest = new URLRequest("upload_file.php?folder="+folderId+"&type="+uploadType+"&feid="+formElementId+"&obfuscate="+obfuscateHash+"&sessidpass="+sessionPass);
  urlRequest.method = URLRequestMethod.POST;
  urlRequest.data = new URLVariables("name=Bryn+Jones");
  var encoder:Base64Encoder = new Base64Encoder();
  encoder.encode("testuser:testpass");
  var credsHeader:URLRequestHeader = new URLRequestHeader("Authorization", "Basic " + encoder.toString());
  urlRequest.requestHeaders.push(credsHeader);

  file = FileReference(fileCollection.getItemAt(0));
  file.addEventListener(Event.COMPLETE, completeHandler);
  file.addEventListener(HTTPStatusEvent.HTTP_STATUS, onHTTPStatus);
  file.addEventListener(ProgressEvent.PROGRESS, onUploadProgress);
  file.upload(urlRequest);
  }
}

As stated above, I seem to be experiencing the same results with or without the amendments to my function.

Can I ask also where the crossdomain.xml should be located - as I do not currently have one and am unsure where to place it.

+7  A: 

The syntax is a little different for URLRequest, but the idea's the same:

private function doWork():void
{
    var req:URLRequest = new URLRequest("http://yoursite.com/yourservice.ext");
    req.method = URLRequestMethod.POST;
    req.data = new URLVariables("name=John+Doe");

    var encoder:Base64Encoder = new Base64Encoder();        
    encoder.encode("yourusername:yourpassword");

    var credsHeader:URLRequestHeader = new URLRequestHeader("Authorization", "Basic " + encoder.toString());
    req.requestHeaders.push(credsHeader);

    var loader:URLLoader = new URLLoader();
    loader.load(req);
}

A couple of things to keep in mind:

  • Best I can tell, for some reason, this only works where request method is POST; the headers don't get set with GET requests.

  • Interestingly, it also fails unless at least one URLVariables name-value pair gets packaged with the request, as indicated above. That's why many of the examples you see out there (including mine) attach "name=John+Doe" -- it's just a placeholder for some data that URLRequest seems to require when setting any custom HTTP headers. Without it, even a properly authenticated POST request will also fail.

  • You'll almost surely have to modify your crossdomain.xml file to accommodate the header(s) you're going to be sending. In my case, I'm using this, which is a rather wide-open policy file in that it accepts from any domain, so in your case, you might want to limit things a bit more, depending on how security-conscious you are:

    <?xml version="1.0"?>
    <cross-domain-policy>
        <allow-access-from domain="*" />
        <allow-http-request-headers-from domain="*" headers="Authorization" />
    </cross-domain-policy>

... and that seems to work; more information on this one is available from Adobe here).

  • Apparently, Flash player version 9.0.115.0 completely blocks all Authorization headers (more information on this one here), so you'll probably want to keep that in mind, too.

The code above was tested with Flash player 10 (with debug & release SWFs), so it should work for you, but I wanted to update my original post to include all this extra info in case you run into any issues, as the chances seem (sadly) likely that you will.

Hope it helps! Good luck. I'll keep an eye out for comments.

Christian Nunciato
+1  A: 

I'm not sure about this but have you tried adding username:password@ to the beginning of your url?

"http://username:[email protected]/yourservice.ext"

Leo Jweda
Using this seems to be the only way to send authorization information using a GET request
Arpit
A: 

Seemingly similar problem was solved here. I urge you to also check the Flexcoders post linked to in the first post.

The problem was that FireFox uses a separate browser window instance to send the file upload request. The solution is to manually attach the session id to the request url. The session id is not attached as a regular GET variable, but with a semicolon (the reason for this syntax is unknown to me).

Niko Nyman
I did encounter this session issue as well, and came up with a similar solution, but this is not the problem faced here - which is specifically HTTP authentication (not session related as such).
BrynJ
A: 

Flash is very limited in terms of what sort of headers you can pass with an http request (and it changes between browsers and OSes). If you get this to work on one browser/OS, make sure you test it on the others.

The best thing to do is not mess with HTTP headers.

We have the same issue (uploading to Picasa Web Albums from flash) and post through a proxy on our server. We pass the extra headers through as post parameters and our proxy does the right thing.

Brian
A: 

var service : HTTPService = new HTTPService (); var encoder:Base64Encoder = new Base64Encoder(); encoder.insertNewLines = false; encoder.encode("user:password");

service.headers = {Authorization:"Basic " + encoder.toString()}; service.method = HTTPRequestMessage.POST_METHOD; service.request = new URLVariables("name=John+Doe"); service.addEventListener(FaultEvent.FAULT,error_handler ); service.addEventListener(ResultEvent.RESULT,result_handler); service.url = 'http://blah.blah.xml?'+UIDUtil.createUID(); service.send();

hey guys, thanks for yours ideas, i manage to come up with hack for using HTTPService instead of URLRequest. All I did was adding variable via a service.request, and set service.method = 'POST'Hope that help!!! :)
A: 

The FileReference.upload() and FileReference.download() methods do not support the URLRequest.requestHeaders parameter.

http://livedocs.adobe.com/flex/2/langref/flash/net/URLRequest.html

Steven Mo
A: 

"http://username:[email protected]/yourservice.ext"

This doesn't work in IE (http://www.theregister.co.uk/2004/01/30/ms_drop_authentication_technique/) and doesn't seem to work in Chrome either.

probably not usable in Flash

Anentropic
A: 

Here is a work-around when using ASP.Net based in part on the work here.

I built a component that dynamically writes Flex objects to the page so they can be used in UpdatePanels. Message me if you want they code. To solve the above problem in pages where authentication cookies will need to be sent by URLRequest, I add the values in as flashVars.

This code only works in my object, but you get the idea

Dictionary<string, string> flashVars = new Dictionary<string, string>();     
flashVars.Add("auth", Request.Cookies["LOOKINGGLASSFORMSAUTH"].Value);
flashVars.Add("sess", Request.Cookies["ASP.NET_SessionId"].Value);
myFlexObject.SetFlashVars(flashVars);

Then in the Flex Object, check for the params

if (Application.application.parameters.sess != null)
    sendVars.sess= Application.application.parameters.sess;
if (Application.application.parameters.auth != null)
    sendVars.au= Application.application.parameters.auth;

request.data = sendVars;
request.url = url;
request.method = URLRequestMethod.POST;

Finally stuff the cookies in on global.asax BeginRequest

if (Request.RequestType=="POST" && Request.Path.EndsWith("upload.aspx"))
{
    try
    {
        string session_param_name = "sess";
        string session_cookie_name = "ASP.NET_SESSIONID";
        string session_value = Request.Form[session_param_name]; // ?? Request.QueryString[session_param_name];
        if (session_value != null) { UpdateCookie(session_cookie_name, session_value); }
    }
    catch (Exception) { }

    try
    {
        string auth_param_name = "au";
        string auth_cookie_name = FormsAuthentication.FormsCookieName;
        string auth_value = Request.Form[auth_param_name];// ?? Request.QueryString[auth_param_name];

        if (auth_value != null) { UpdateCookie(auth_cookie_name, auth_value); }
    }
    catch (Exception) { }   

}

Hope this help someone avoid the 6 hours I just spent addressing this. Adobe has closed the issue as unresolvable, so this was my last resort.

Laramie