The first part of this blog post demonstrates how to implement a WCF web service with username/password authentication and role based authorization. The web service can be used in Windows Phone applications and Windows 8 applications and it is even possible to use it with other libraries or frameworks. The second part explains how to set up the development environment to test the web service and the client application locally.
The sample project can be downloaded here.
Web service code
First we have to create a new web service project and create a web service (.svc and its corresponding .svc.cs file). The following code shows the needed code for a web service. By default, Visual Studio generates an interface and a web service code file. It is also possible to merge these two files into one class, which you can see below (this way the contract attribute in the “web.config” contains the class name not the interface name):
namespace WcfSampleApplication
{
[ServiceContract]
[AspNetCompatibilityRequirements(RequirementsMode = AspNetCompatibilityRequirementsMode.Required)] // see note 1)
public class DataService
{
[OperationContract]
[PrincipalPermission(SecurityAction.Demand, Role = "Administrator")] // see note 2)
public string GetData(int value)
{
...
Notes:
- This attribute is needed if we use a RoleProvider in UseAspNetRoles mode
- With this attribute, the operation will only be executed if the current user is in the role “Administrator”. Otherwise a security exception will be thrown.
Membership provider and role provider
After creating the web service, we have to develop a membership and role provider. The membership provider is used to authenticate the user; the role provider is used to determine the roles associated with a user. As you can see in the web service code, these roles can be used to authorize the web service operation calls.
namespace WcfSampleApplication.Security
{
public class MembershipProvider : System.Web.Security.MembershipProvider
{
public override bool ValidateUser(string username, string password)
{
if (string.IsNullOrEmpty(username))
throw new ArgumentNullException("username");
if (username.ToLower() == "anonymous")
return true;
return username.ToLower() == "test" && password == "test";
}
public override MembershipUser GetUser(string username, bool userIsOnline)
{
if (string.IsNullOrEmpty(username))
throw new ArgumentNullException("username");
var currentDate = DateTime.Now;
return new MembershipUser(Name, username, null, string.Empty, string.Empty, string.Empty,
true, false, currentDate, currentDate, currentDate, currentDate, currentDate);
}
...
namespace WcfSampleApplication.Security
{
public class RoleProvider : System.Web.Security.RoleProvider
{
public override string[] GetRolesForUser(string username)
{
if (username.ToLower() == "Anonymous")
return new[] { "User" };
return new[] { "User", "Administrator" };
}
public override bool IsUserInRole(string username, string roleName)
{
return GetRolesForUser(username).Contains(roleName);
}
...
web.config
The “web.config” ties all pieces together and tells the IIS web server how to publish the web service. First we have to define the membership provider and role provider:
<system.web>
<membership>
<providers>
<add name="membershipProvider" type="WcfSampleApplication.Security.MembershipProvider" />
</providers>
</membership>
<roleManager enabled="true" defaultProvider="roleProvider">
<providers>
<add name="roleProvider" type="WcfSampleApplication.Security.RoleProvider" />
</providers>
</roleManager>
</system.web>
Now we define the web services. If you want to use the web service in Windows Phone clients and Windows 8 clients you need to use a basicHttpBinding. Windows Phone does not support wsHttpBindings. The behavior specifies the membership provider and the role provider which is declared above (in the “system.web” tag).
<system.serviceModel>
<serviceHostingEnvironment aspNetCompatibilityEnabled="true" multipleSiteBindingsEnabled="true" />
<services>
<service name="WcfSampleApplication.Service1" behaviorConfiguration="behavior">
<endpoint address="" binding="basicHttpBinding" bindingConfiguration="binding" contract="WcfSampleApplication.Service1" />
</service>
</services>
<behaviors>
<serviceBehaviors>
<behavior name="behavior">
<serviceMetadata httpGetEnabled="false" httpsGetEnabled="true" />
<serviceDebug includeExceptionDetailInFaults="true" />
<serviceAuthorization principalPermissionMode="UseAspNetRoles" roleProviderName="roleProvider" />
<serviceCredentials>
<userNameAuthentication userNamePasswordValidationMode="MembershipProvider" membershipProviderName="membershipProvider" />
</serviceCredentials>
</behavior>
</serviceBehaviors>
</behaviors>
<bindings>
<basicHttpBinding>
<binding name="binding">
<security mode="TransportWithMessageCredential">
<message clientCredentialType="UserName" />
</security>
</binding>
</basicHttpBinding>
</bindings>
</system.serviceModel>
Setup development environment for SSL communication
The following steps demonstrate how to setup the web project to accept HTTPS calls and use the web service on development clients.
- Install IIS Express
- In Visual Studio go to web project properties
- Go to tab “Web” and select “Use Local IIS Web server” and select “Use IIS Express”
- Click on “Create Virtual Directory”
- Select “Don’t open a page. Wait for a request from an external application.” if you want to suppress opening the browser each time you start the server application.
- Now select the web project in the solution explorer. In the “Properties” pane change “SSL Enabled” to “True”. Now the SSL url should appear: Start the server and browse to the web service: e.g. https://localhost:44300/Service1.svc
- If you see a certificate error in your browser, you have to install the development certificate in your trusted certificate store. The next steps shows how to install the certificate on the client machine to access the web service. Because you will install the localhost certificate, it is only possible to access the web service if it is hosted on the same machine. If you want to test your Windows 8 Metro apps you have to install the server on the Windows 8 machine or use another certificate.
- Run “mmc” (Microsoft Management Console)
- Select “File/Add/Remove Snap-In…” and add “Certificates”, then select “Computer account”
- Open “Certificates” snap-in and open “Personal/Certificates”. There you should see a certificate issued to “localhost” with frindly name “IIS Express Development Certificate”. If you don’t see this certificate you have to reinstall IIS Express as this will recreate the missing certificate.
- Right click on this certificate and select “All Tasks/Export…” and export the certificate without private key in DER format.
- Double click the exported certificate in explorer and install it into the “Trusted Root Certification Authorities” store by selecting “Place all certificates in the following store”.
Use the web service in the Windows Phone emulator
To use the web service in a Windows Phone application you have to install the certificate in the emulator. There are two ways to install a certificate in Windows Phone: Send it by email or browse to it with the Internet Explorer. First you have to export the development certificate (see step 7 from the previous chapter). If you want to configure your IIS Express to serve your certificate do the following steps:
-
Open “My Documents/IISExpress/config/applicationhost.config” and remove or comment this line:
<add name="SecurityCertificate" path="*.cer" verb="GET,HEAD,POST" modules="IsapiModule" scriptProcessor="%IIS_BIN%\asp.dll" resourceType="File" />
-
Add a new mime type mapping:
<mimeMap fileExtension=".cer" mimeType="application/octet-stream" />
-
Now copy your certificate .cer file to the root of the web project.
-
After starting the web instance, you can access the certificate using the HTTP address: e.g.
http://localhost:23205/certificate.cer
The code to call the web service looks like this:
var svc = new ServiceReference1.Service1Client();
svc.ClientCredentials.UserName.UserName = "Anonymous";
svc.ClientCredentials.UserName.Password = "";
svc.GetDataCompleted += (s, a) =>
{
MessageBox.Show(a.Result);
svc.CloseAsync();
};
svc.GetDataAsync(42);
Rico Suter
SOFTWARE ENGINEERING
EDIT
WCF Windows Phone WinRT