Unfortunately the version 1.0 of the Windows Phone 7 SDK is missing a few essentials. One of which is that the Phone’s WCF implementation doesn’t support basic authentication for non-WCF services. So if you need to supply a user name and password to access a remote service, you’re on your own. Setting the Security mode in your binding config as you normally would doesn’t work.
Fortunately basic HTTP authentication is easy enough to implement yourself.
As the name suggests Basic Authentication is basic. It is simply an “Authorization” header which is added to the HTTP call which contains the base64 encoded user name and password.
Authorization: Basic dXNlcm5hbWU6cGFzc3dvcmQ=
The good news is that implementing basic authentication means simply adding this header to your going calls. Here’s how.
Firstly we need a method to encode the credentials:
private string EncodeBasicAuthenticationCredentials(string username, string password) { //first concatenate the user name and password, separated with : string credentials = username + ":" + password; //Http uses ascii character encoding, WP7 doesn’t include // support for ascii encoding but it is easy enough to convert // since the first 128 characters of unicode are equivalent to ascii. // Any characters over 128 can’t be expressed in ascii so are replaced // by ? var asciiCredentials = (from c in credentials select c <= 0x7f ? (byte)c : (byte)'?').ToArray(); //finally Base64 encode the result return Convert.ToBase64String(asciiCredentials); }
Now that we have a means of encoding our credentials we just need to add them to the headers of our WCF request. We can easily do this by wrapping the call in an OperationContextScope:
var credentials = EncodeBasicAuthenticationCredentials("username", "password"); using (OperationContextScope scope = new OperationContextScope(service.InnerChannel)) { HttpRequestMessageProperty request = new HttpRequestMessageProperty(); request.Headers[System.Net.HttpRequestHeader.Authorization] = "Basic " + credentials; OperationContext.Current.OutgoingMessageProperties.Add( HttpRequestMessageProperty.Name, request); service.DoSomethingAsync(); }
This easily adds basic HTTP authentication for your WCF call on the fly. Unfortunately using the OperationContext like this means you have to remember to add the header each time you make a call. If you are going to regularly be needing to do this you might want to consider creating a custom Channel, which automatically adds the header to all outgoing requests. The technique would essentially be the same, it’s just that it would code it once for the channel rather than for each individual call. [Update 08/12/2010: There is no centralised way to do this in Silverlight 3.0]
Also it is always worth mentioning that basic authentication is not secure. Encoding the username and password does not encrypt them. They are transmitted over the internet in the clear for anyone to read.
Wouldn’t SSL + Basic Authentication be enough? SSL would handle the encryption and basic authentication would give you some about of user authentication.
Yes, certainly using Basic Authentication over SSL is much more secure. With SSL (or TLS to be more accurate) your entire session is encrypted at a lower level. So it doesn’t matter if the user name and password are plain text, because they are travelling over a secure channel.
I’m still trying to figure out how to get a secure WCF connection with authentication. It’s being very frustrating.
I removed all my header manipulation code because the .Add method is missing from the Headers collection, but you’re just using the indexer [].
This could have far-reaching implications…
Does this add or just change what’s already at the index?
Thanks, Luke
Hi Luke
No, using the indexer doesn’t change what is already in the headers collection. If you index into the collection with a key that doesn’t already exist in the collection, it will be added for you with the value you supply. If that key does already exist in the collection then the existing value will be over written with the value you supply. Any existing headers in the collection will remain unchanged.
You can also change the User Agent header with the same technique, so
request.Headers[System.Net.HttpRequestHeader.Authorization] = "Basic " + credentials;
request.Headers[System.Net.HttpRequestHeader.UserAgent] = "MyApp 1.0";
would yield the headers:
Authorization: Basic dXNlcm5hbWU6cGFzc3dvcmQ=
User-Agent: MyApp 1.0
if you added second User Agent header it would overwrite the first. So:
request.Headers[System.Net.HttpRequestHeader.Authorization] = "Basic " + credentials;
request.Headers[System.Net.HttpRequestHeader.UserAgent] = "MyApp 1.0";
request.Headers[System.Net.HttpRequestHeader.UserAgent] = "MyApp 2.0";
would yield the headers:
Authorization: Basic dXNlcm5hbWU6cGFzc3dvcmQ=
User-Agent: MyApp 2.0
A
Bobby – I’m also having a nightmare. The phone isn’t reattaching the cookie it gets from the AuthenticationService to subsequent calls to services at the same domain.
My blood is boiling. Microsoft continually leave out some tiny thing that wastes so much of my time and flies in the face of their guidance on how to architect a full system on their technology stack.
As I type I’m on the phone to Microsoft about this problem. I copied and pasted my code over from the full framework and as soon as I saw the Add method had been removed, I thought that was a deliberate act to prevent coding changes to the headers.
Thanks very much for this.
Luke
Excellent post! You are my hero. Simple but very elegant solution. I’ve been very frustrated by this. Hopefully they will fix this is the January patch.
Hi, I’m also frustrated because we cannot get it to work to use WCF with SSL and Basic Authentication. I’ve to implement my own wrappers for all Webservices with the HttpWebRequest object, add the authorization and parse the response with the ugly XDocument. Things which worked since .NET CF 2.0.
Hey, it just works also over HTTPS. Yehaa thank you. But there seems no way to avoid the using (OperationContextScope).
BTW, this will not work with a website that uses Forms authentication. I can’t enable basic and forms authentication at the same time. Maybe I’m missing something?
Are trying to call a WCF web service? if that’s the case, you need to implement your own ServiceAuthoriationManager that will validate the user and create the prinicpal (just like the early days of asp.net)
Here’s an example http://bit.ly/MyTakeOnWP71
Pingback: Windows Phone 7 Developer Roundup #5 for 12/08/2010 « "A" Developer`s Life
Any idea when the next version of the Windows Phone 7 SDK will be released?
No, but I would guess we should probably expect one next month (or February at the latest). I must say Microsoft is seriously in danger of losing momentum if they don’t start moving quicker than they are.
That’s extactly what I want to do. Could you give me some details about the Client- and ServerConfiguration File, please.
I would like to use HTTPS (transport) and BasicAuthentication. Only HTTPS works like a charm but both combinated doesn’t work for me.
My current ServerConfigFile:
And my Client Config:
Thank you.
Jason. I would post your question on StackOverflow.com
Thanks for your fast reply Adam.
I’ve posted my question on StackOverflow.com:
http://stackoverflow.com/questions/6790549/wp7-wcf-iis-https-transport-basic-authentication
Thank you for this one Adam.. have the same problem like Jason.. Could you please help me?
Seems my web.config (WCF) to be right?
Thank you in advance. Greg
Pingback: Call SOAP Web Service With Basic Authorization | Road to Data Professional