views:

13230

answers:

20

I'm trying to request a HTTP resource that requires basic authorization headers from within an Adobe AIR application. I've tried manually adding the headers to the request, as well as using the setRemoteCredentials() method to set them, to no avail.

Here's the code:

<mx:Script>
    <![CDATA[
     import mx.rpc.events.ResultEvent;
     import mx.rpc.events.FaultEvent;

     private function authAndSend(service:HTTPService):void
     {
      service.setRemoteCredentials('someusername', 'somepassword');
      service.send();
     }

     private function resultHandler(event:ResultEvent):void
     {
      apiResult.text = event.result.toString();
     }

     private function resultFailed(event:FaultEvent):void
     {
      apiResult.text = event.fault.toString();
     }
    ]]>
</mx:Script>

<mx:HTTPService id="apiService"
    url="https://mywebservice.com/someFileThatRequiresBasicAuth.xml"
    resultFormat="text"
    result="resultHandler(event)"
    fault="resultFailed(event)" />

<mx:Button id="apiButton"
    label="Test API Command"
    click="authAndSend(apiService)" />

<mx:TextArea id="apiResult" />

However, a standard basic auth dialog box still pops up prompting the user for their username and password. I have a feeling I'm not doing this the right way, but all the info I could find (Flex docs, blogs, Google, etc.) either hasn't worked or was too vague to help.

Any black magic, oh Flex gurus? Thanks.


EDIT: Changing setRemoteCredentials() to setCredentials() yields the following ActionScript error:

[MessagingError message='Authentication not supported on DirectHTTPChannel (no proxy).']


EDIT: Problem solved, after some attention from Adobe. See the posts below for a full explanation. This code will work for HTTP Authentication headers of arbitrary length.

import mx.utils.Base64Encoder;
private function authAndSend(service:HTTPService):void
{
        var encoder:Base64Encoder = new Base64Encoder();
        encoder.insertNewLines = false; // see below for why you need to do this
        encoder.encode("someusername:somepassword");

        service.headers = {Authorization:"Basic " + encoder.toString()};                                                
        service.send();
}
A: 

Try using setCredentials rather than setRemoteCredentials and failing that, using Fiddler/Charles to find out what headers are being sent with the request.

Richard Szalay
Still no dice. See updated question.
Bob Somers
What was the output of fiddler/charles?
Richard Szalay
+1  A: 

The setCredentials() & setRemoteCredentials() methods are intended for use with Flex/LiveCycle Data Services, so they probably don't apply in your case.

This ought to work for you. I was able to reproduce this behavior on my server, and this fix seems to have done the trick; it still seems a bit odd this isn't more API-user-friendly, considering how common a use case you'd think it were, but nonetheless, I've tested and verified this works, given a valid SSL cert:

private function authAndSend(service:HTTPService):void
{
     var encoder:Base64Encoder = new Base64Encoder();
        encoder.encode("someusername:somepassword");

     service.headers = {Authorization:"Basic " + encoder.toString()};             
        service.send();
}

Hope it helps! And thanks for posting -- I'm sure I would've run into this one sooner or later myself. ;)

Christian Nunciato
Ah, thanks, that makes more sense. I still have issues though, as this code gives me a "Error #2096: The HTTP request header Basic <base64here> cannot be set via ActionScript." Would this have anything to do with Adobe blacklisting the Authorization header? That shouldn't affect an AIR app, right?
Bob Somers
In other words, do I need to prod the web service provider to create a crossdomain.xml file?
Bob Somers
Not that I know of, no -- you're using AIR 1.5, right? I tested the above with AIR 1.5 + Flex 3.2, and your identical source, just with my own uid/pwd, path to XML and SSL cert. Didn't have any problem. What do you mean "Adobe blacklinting the Auth header"?
Christian Nunciato
Yep, AIR 1.5/Flex 3.2. I setup my own test environment like you did and it worked fine. I switch to the live API and it breaks. It looks like I need to talk to the web service provider and find out what's going on. Your code works great, though. Thanks!
Bob Somers
A: 

Just wanted to post an update on this. It looks like there there may be a bug in the way Flex handles long HTTP headers. The API key I'm trying to use (which is used for HTTP Basic Authentication) is 64 characters long, and that's longer than the cutoff. I could get it to send up to 54 characters (before base 64 encoding, including the colon), but 55 or more and it complains that ActionScript can't set the Authentication header.

I've opened up a bug report on Adobe's site to have them take a look at it, you can follow it there if you like: https://bugs.adobe.com/jira/browse/SDK-19024

Bob Somers
+1  A: 

Finally received some attention from Adobe and got an answer on this. The problem with long HTTP Authentication headers is that, by default, the Base64Encoder class will inject newline characters every 72 characters. Obviously that causes a chunk of the base-64 encoded string to be interpreted as a new header attribute, which causes the error.

You can fix this by setting (in the above example) encoder.insertNewLines = false; The default setting is true.

I've fixed the above code to work for arbitrarily long Authentication strings.

Bob Somers
A: 

Also, just so other people don't spend 10 minutes working out why the correct example doesn't quite work asis, you need to import the mx.utils.Base64Encoder package eg:

        import mx.utils.Base64Encoder;

At the beginning or somewhere within the CDATA area. I'm new to flex so this wasn't quite obvious at first.

Good catch. I've updated the code in the question to reflect the missing import, thanks!
Bob Somers
A: 

How does this work for a WebService? I'm really struggling with this at the moment!

Thanks,

Dave

A: 

I know the example URL above isn't real, so did someone actually try this over HTTPS and not HTTP? I followed the revised instructions above, but I continue to get the basic auth dialog box pop-up. Using fiddler to intercept the request, I notice that the Authorization header isn't passed. However, using the Flex debugger, I verified that this header is set correctly.

The HTTPS URL I'm using works just fine once I enter the credentials into the basic auth pop-up, so the URL itself isn't the issue. I don't know what else to try...

Does anyone know what I might be doing wrong?

I did not test it over HTTPS, only standard HTTP, so there may be other issues when using an SSL connection.
Bob Somers
+1  A: 

This is not working for me. I have a flex app running in browser with flash 9.0.124.0. Here is my code:

...
var encoder:Base64Encoder = new Base64Encoder();
encoder.encode(uname+":"+pwd);
var value:String = encoder.toString();
service.headers.Authorization = "Basic " + value;
...

When viewed by HttpAnalyzer the GET request shows no authorization header. Also, as further proof that this does not work, I get the standard challenge pop-up requesting credentials. After entering them, all is ok.

Anyone know how to do this from a Flex browser app (running from flash)?

thanks.

A: 

Doesn't work for me either, seems like the credentials won't work on GET requests only on post just like it is when working with URLRequest class.

Anyone has an idea how to get this working with GET as well?

guytom
+4  A: 

Ah. The pain, the suffering. The sheer misery.

While you've figured out how to add a header before making your call, the nasty truth is that somewhere deep down in the Flash/browser integration space your headers are being removed again.

From my blogpost last year at verveguy.blogspot.com

So I have unraveled the Truth. (I think) It's more tortured than one would imagine

1/ All HTTP GET requests are stripped of headers. It's not in the Flex stack so it's probably the underlying Flash player runtime

2/ All HTTP GET requests that have content type other than application/x-www-form-urlencoded are turned into POST requests

3/ All HTTP POST requests that have no actual posted data are turned into GET requests. See 1/ and 2/

4/ All HTTP PUT and HTTP DELETE requests are turned into POST requests. This appears to be a browser limitation that the Flash player is stuck with. (?)

What this boils down to in practical terms is that if you want to pass headers in all requests, you should always use POST and you should find another way to communicate the semantics of the operation you "really wanted". The Rails community have settled on passing ?_method=PUT/DELETE as a work around for the browser problems underlying 4/

Since Flash adds the wonderful header stripping pain on GET, I'm also using ?_method=GET as a workaround for that. However, since this trips up on 3/, I am passing a dummy object as the encoded POST data. Which means my service needs to ignore dummy posted data on a ?_method=GET request.

Crucial at this point to know about 2/. That wasted a bunch of my time.

I've built all of this handling into a new RESTService class with MXML markup support so it's possible to pretend this doesn't exist on the client side.

Hope this helps someone.

verveguy
A: 

I get a stream error 2032, it happens often, howcome when i make a POST request, it comes out as a get request?

This happens a lot, and I have no idea why? But other users are using the app with no problem.

Patrick

ReduxDJ
+1  A: 

This really has helped me! Thanks! I use Flex Builder 3

One note: WebService's property headers is read only. So I tried to use httpHeaders. It works!

    var encoder:Base64Encoder = new Base64Encoder();
    encoder.insertNewLines = false;
    encoder.encode("test:test");

    sfWS.httpHeaders = {Authorization:"Basic " + encoder.toString()};
Antonio
A: 

I'm also struggling with the flex - web service communicaton using basic http authentication. My web service is located on the JBOSS server, crossdomain policy is tuned up as well. But I always have authorization popup and never have this basic authorization header in the GET request (firebug don't show this header at least, and I inclined to trust him ^)). Can somebody post solution for the this issue if have it? Google didn't help this at all.

A: 

How to Add “NTLM Authentication” Header to HTTPService?

amony
A: 

I have a solution only if you run a Tomcat, JBoss runs Tomcat embedded so it will work. I do not know any solution that will work from the client side, so it is a server side fix.

http://code.google.com/p/web-actions/wiki/FlexBasicAuthenticationValve

Ivan Latysh
A: 

Hi,

Thank you for your example. It works well with IE/FF/Chrome, but it doesn't work with Opera. Anybody knows an solution for Opera ?

Thank you.

Gildas
A: 

And it gets worse now that I'm trying to make AIR do proper PUT requests using Flex 4 on AIR 1.5. Guess what?

These lines in DirectHTTPChannel.as in the createURLRequest() method completely override my attempt to PUT when the data type is XML.

    if (httpMsg.method == HTTPRequestMessage.POST_METHOD || contentTypeIsXML)
    {
        result.method = "POST";

Any suggestions on how to hack around this gross ass-umption on Adobe's part appreciated. grumble, grumble, grumble

[UPDATE]

So, I filed a bug with Adobe at https://bugs.adobe.com/jira/browse/SDK-26482

I've also found a hack around that involves subclassing a whole swag of Adobe classes and patching at just the right point to effect a reversal of this assumption.

verveguy
A: 

god i hate adobe.

i have a web service that requires auth.

the wsdl itself also requires auth.

flash tries to get wsdl using GET method, and of course it epicly fails, since it strips out the authorization, and gives no change of changing the method.

second day trying to develop a workaround that wouldn't require me to change adobe files, or copy all of them to my solution, just to HARDCODE the post value. jeez

flunik
A: 

Experiencing the same issue with Opera...is works just perfectly fine on FF/IE/CH, but on Opera no Authentication Header is send...

does anybody have a solution?

Martijn Mol
A: 

HI if login fails then what the control doesnt go in the fault it opens Same PopuP for authentication

virat