Adding a custom message header to the WCF service consumed by a Windows Mobile 6 client

I have a WCF service application whose server is used for different types of clients. When calling service methods, I want to send some information in the service header.

With a newer version of the .NET Framework, I can handle the situation with MessageHeader. Since the consumer can see the service as a WCF service, no problem.

[DataContract] public class AuthToken { [DataMember] public string Username { get; set; } [DataMember] public string Password { get; set; } } 

on the client side:

 AuthWCFSvc.Service1Client client = new AuthWCFSvc.Service1Client(); using (OperationContextScope scope = new OperationContextScope(client.InnerChannel)) { SvcAuthClient.AuthWCFSvc.AuthToken token = new AuthWCFSvc.AuthToken(); token.Username = "wcfuser"; token.Password = "wcfpass"; MessageHeader<SvcAuthClient.AuthWCFSvc.AuthToken> header = new MessageHeader<SvcAuthClient.AuthWCFSvc.AuthToken>(token); var untyped = header.GetUntypedHeader("Identity", "http://www.my-website.com"); OperationContext.Current.OutgoingMessageHeaders.Add(untyped); client.TestHeader(); } 

Server side:

 MessageHeaders headers = OperationContext.Current.IncomingMessageHeaders; AuthToken token = headers.GetHeader<AuthToken>("Identity", "http://www.my-website.com"); 

But there are Windows Mobile 6 devices with the .NET Framework 3.5 Compact Edition using these services. Due to technological limitations, they can only handle WCF services as web services.

If the client uses the WCF service as a web service, how can I add spesific header information and resolve the header information using the service method?

+2
source share
1 answer

As you know, on .NET CF 3.5 you can use WCF only as a standard web service along the SOAP path. Therefore, you cannot use any WCF security resources.

I figured out how to use Basic Http Authentication , setting up the client and server sides, and I can explain this as follows:


Client side

Client-side (on your device with .Net CF 3.5) is easy. Just provide your credentials by setting up your clientServiceProxy using:

  var service = new YourServiceNamespace.YourService(); service.Credentials = new NetworkCredential("login", "12345"); service.PreAuthenticate = true; 

This will force your client to process the "WWW-Authenticate" header from the server response and automatically pass your credentials through the "Authorization: basic" response header.


Server side

In the WCF configuration on your web.config, you should configure security only for transport and use HTTPS (this is enough to protect your message from sniffers).

  <basicHttpBinding> <binding> <security mode="Transport"> <transport clientCredentialType="None" /> </security> </binding> </basicHttpBinding> 

Now, since WCF does not have native support for Basic Http Authentication, we must use a custom HTTP module to handle it.

 public class BasicHttpAuthentication : IHttpModule { public delegate bool AuthenticateDelegate( string username, string password ); public static AuthenticateDelegate AuthenticateMethod; public void Dispose() { } public void Init( HttpApplication application ) { application.AuthenticateRequest += this.OnAuthenticateRequest; application.EndRequest += this.OnEndRequest; } private void DenyAccess( HttpApplication app ) { app.Response.StatusCode = 401; app.Response.StatusDescription = "Access Denied"; // Write to response stream as well, to give user visual // indication of error during development app.Response.Write( "401 Access Denied" ); app.CompleteRequest(); } private void OnAuthenticateRequest( object source, EventArgs eventArgs ) { if ( AuthenticateMethod == null ) return; var app = ( HttpApplication )source; //the Authorization header is checked if present string authHeader = app.Request.Headers["Authorization"]; if ( !string.IsNullOrEmpty( authHeader ) ) { string authStr = app.Request.Headers["Authorization"]; if ( string.IsNullOrEmpty( authStr ) ) return; // No credentials; anonymous request authStr = authStr.Trim(); if ( authStr.IndexOf( "Basic", 0 ) != 0 ) // header is not correct...we'll pass it along and // assume someone else will handle it return; authStr = authStr.Trim(); string encodedCredentials = authStr.Substring( 6 ); byte[] decodedBytes = Convert.FromBase64String( encodedCredentials ); string s = new ASCIIEncoding().GetString( decodedBytes ); string[] userPass = s.Split( new[] { ':' } ); string username = userPass[0]; string password = userPass[1]; if ( !AuthenticateMethod( username, password ) ) this.DenyAccess( app ); } else { app.Response.StatusCode = 401; app.Response.End(); } } private void OnEndRequest( object source, EventArgs eventArgs ) { //the authorization header is not present //the status of response is set to 401 and it ended //the end request will check if it is 401 and add //the authentication header so the client knows //it needs to send credentials to authenticate if ( HttpContext.Current.Response.StatusCode == 401 ) { HttpContext context = HttpContext.Current; context.Response.StatusCode = 401; context.Response.AddHeader( "WWW-Authenticate", "Basic Realm=\"Please inform your credentials\"" ); } } } 

To enable the HTTP module, add the following to the web.config file in the system.webServer section:

 <system.webServer> <modules> <add name="BasicHttpAuthentication" type="BasicHttpAuthentication, YourAssemblyName"/> </modules> 

Now you need to tell the module the function that will be used to verify the credentials from the client. You can see that there is a static delegate inside the module called "AuthenticateMethod", so you can report your Application_Start function to your global.asax:

 BasicHttpAuthentication.AuthenticateMethod = ( username, password ) => username == "login" && password == "12345"; 
0
source

All Articles