One of the most powerful technologies available on the .NET platform is the ClickOnce which makes it possible to implement selected components of a web application as desktop applications.
It is a common architectural pattern to build a web service for the clickonce desktop component so that no matter which user interface technology runs on the client side (web or desktop), the application logic is always executed on the server side. It is also a common pattern to secure the web application using Forms Authentication.
This artice focuses on following issue: sharing the forms authentication between web and desktop components of the application.
The Ultimate Goal
My goal is to build a typical web application secured with Forms Authentication. The application will expose selected parts of its functionality as a ClickOnce application. What I'd like to have is a way to run the ClickOnce application from within the web application and share the identity a user is logged as. In other words - when a user authenticates via the login page of the web application, he/she should not be forced to reauthenticate when the ClickOnce application starts but rather the ClickOnce application should automatically share the user credentials with the web application.
The solution should be also secured in a sense that users who are not authenticated in a web application should not be able to execute any methods in the ClickOnce application that require the web service call.
In yet other words - both web application and the ClickOnce application should share the same Forms Authentication.
The solution is based solely on the Forms Authentication semantics, specifically - the FormsAuthenticationModule.
You see, on succesfull login, a cookie is appended to the server's response and the cookie is passed between a web browser and the application server. However, from the application server's perspective there's no difference between a web browser call to a web page and a ClickOnce application call to a web service. In both cases the FormsAuthenticationModule checks if the authentication cookie is properly passed by the HTTP protocol and if this is so, the application server treats the incoming request as authenticated.
If we would be able to somehow pass the authentication cookie from the web page to the ClickOnce application (so that the authentication cookie would be appended to any web service calls) the issue would be solved. Fortunately, this is not difficult at all!
First, build your web application as usual. Implement your MembershipProvider and the login page. Then, on a selected web page put a link to invoke the ClickOnce application.
The hyperlink will be dynamically evaluated so that the authentication cookie would be passed to the ClickOnce application:
Second, create a web service for the ClickOnce application and secure each method with declarative check against not authenticated calls:
Third, in your ClickOnce application invoked from the web page read the form authentication cookie parameter:
Fourth, pass the authentication cookie (available now to the ClickOnce application) to each Web Service call:
And this is it - the identity is shared between the web application and its ClickOnce component. Even if the ClickOnce component is invoked by an unauthenticated user, any web service call from within the ClickOnce component will fail since the application service is protected by the PrincipalPermission attribute.
Download the source code here. Description inside.