Silverlight, WCF, Web Services

Get a relative URI to your Web/WCF Service in Silverlight

How do you get a relative URI to your web service / WCF service?

It’s pretty easy:

  • Create a binding
  • Create an EngpointAddress
  • Instantiate the client proxy

<Sample Code>

Get a relative URI to your Web/WCF Service in Silverlight Sample

</Sample Code>

Note: The downloadable sample is in C# whereas the samples in the article are in VB.NET 🙂

Dim binding As New BasicHttpBinding(BasicHttpSecurityMode.None)
binding.MaxReceivedMessageSize = 10000000

Dim endpoint As New EndpointAddress(someUri)

service = New SomeWebServiceClientThatHasBeenAddedAsReference(binding, endpoint)

Nice and simple, but there is an issue with portability. What if you want to move the Silverlight app around and need the service URI to be relative. Silverlight has some cool relative URI stuff, but for some reason the EndpointAddress class doesn’t support relative URIs which makes this a little more difficult than it should be. Attempting to pass in a relative URI to it’s constructor will result in an Exception: System.ArgumentException: The given URI must be absolute.

Getting around this is pretty easy too however. Try this:

Dim uri As New Uri(App.Current.Host.Source, "../SomePath/SomeService.asmx")
Dim endpoint As New EndpointAddress(uri)

Note the App.Current.Host.Source URI is where the XAP file lives – which is a great starting point. Pass in a relative URL string to this point and you can find your service (or anything else for that matter) from wherever you app lives!

ASP.NET, Silverlight, Visual Studio, WCF, Windows Live

Silverlight and Windows Live ID Video

Complementing my earlier post here is a video cast of the same content.

It’s available in browser and in two download formats (WMV and iPod).

Check it out here.

NOTE: In the video I refer to Windows Live ID and services like it as delegated authentication providers… this isn’t 100% correct. Delegated authentication is a part of Windows Live ID. For more information see here.

AJAX, ASP.NET, C#, MVC, Silverlight, WCF, Windows Live

Silverlight, WCF, Membership, Forms Authentication and Windows Live ID

Watch the companion screen cast here

Nobody wants to be in the business of password management. It’s too risky and creation of username/password storage and authentication mechanisms adds a lot of overhead to any project. There are a number of ways to get around this and still provide secure access to your site.

One such way is by using an authentication provider such as Windows Live ID (Windows Live Login). Other providers include Yahoo! ID and Open ID.

This article will show you how to set up a Windows Live ID based login and then consume it from Silverlight using a combination of ASP.NET Membership, ASP.NET Application Services (the Authentication application service) and ASP.NET Forms Authentication.

The beauty of using this combination of technologies is that it negates the need for you to store and manage passwords, but you still get to manage profiles (name, email etc) using your own custom code and Membership provider. It also integrates with the existing ASP.NET Forms Authentication controls, like the login status control etc. Finally we can hook up to the Membership application service and consume login information from the Silverlight application.

Configuring Windows Live ID

Start by downloading the Windows Live ID Web Authentication SDK here. An overview of the SDK and general HOWTO can be found here.

Continue reading once you have the SDK and test site configured (files are installed to C:\Program Files\Windows Live ID\WebAuth, Create an IIS virtual called WebAuth and point to this location).

To use Windows Live ID (WLID from here on) in your web app you first need to register for a new application key on the Windows Live site. This process sets up your site to authenticate against the Windows Live ID service and provides the security keys to ensure that other parties cannot spoof your site’s identity.

Filling out this form is relatively straight forward, just follow the prompts. Leave Application verifier set to 0 for web applications.

You’ll note the code here. This secret code will be used to encrypt all information between your site and Windows Live servers. This key will be needed at a later stage so ensure you jot it down.

Part of this process is providing a return URL for your site which can make debugging a little difficult – it will try to return to your production site, and will not work with “localhost” etc. Luckily the SDK includes a sample account for testing.

For brevity we will be using the sample account which is present in the SDK which works with localhost. To see this example account in action navigate to http://localhost/webauth/sample/default.aspx and click the Sign in button.

Create a Demonstration Project

<Sample Code>

http://www.webjak.net/files/FileDetail.aspx?fileId=d3c01c17-4056-4d3d-b6a3-1c7d159866c2

</Sample Code>

All said and done, all you get out of a Windows Live ID login is a key that is unique to both your site and the current user. This key will never be the same for any user on any other site in the world.

To demonstrate we will create a simple TextBlock which will show the login status of the user. Examples will focus only on the authentication side of things, so there is some assumed knowledge (you can see how the rest is done in the code download).

Start by creating a new Silverlight application in Visual Studio called “Silverlight Comments”. Ensure you select “add a new Web to the Solution”. Like all true developers should select “Web Application Project” in the Project Type drop down. NOTE: This stuff needs Visual Studio 08.

Handling the Login Response

In the Web Applicaton project add a new class library called WindowsLiveLogin.cs and copy in the code from C:\Program Files\Windows Live ID\WebAuth\App_Code\WindowsLiveLogin.cs. This class allows you to decode the tokens that are returned from the Windows Live system.

Copy and paste in the default app settings from the example project:


 
 
 

When you are ready to go live with a real site you will change these settings to reflect the real values from the Live ID registration site.

Next we will create the page that Windows Live Login returns to (this is the page that you would normally configure in the Return URL in the Live ID configuration site). This page takes the response and performs an action (logout, clearcookie and default) depending on the data. It also creates the login cookie that we will use to store the login.

Because this page needs to work with the sample WLID account, create the new page in Sample/webauth-handler.aspx. In production you can create this page where ever you like, as long as you set the correct return URL in the WLID configuration page (dev.live.com).

Open C:\Program Files\Windows Live ID\WebAuth\Sample\webauth-handler.aspx.cs and copy the code from between the two class { } delimiters in to the code behind of the new page you just created.

e.g.


public partial class HandlerPage : System.Web.UI.Page
{

    //Copy all this 
    const string LoginPage = "default.aspx";
    const string LogoutPage = LoginPage;
    const string LoginCookie = "webauthtoken";
    static DateTime ExpireCookie = DateTime.Now.AddYears(-10);
    static DateTime PersistCookie = DateTime.Now.AddYears(10);

    //... 

}

Move the const and static fields in to the WindowsLiveLogin class as we will need to access these from other areas of code. Ensure you make them public.

e.g.


public class WindowsLiveLogin
{
        public const string LoginPage = "default.aspx";
        public const string LogoutPage = LoginPage;
        public const string LoginCookie = "webauthtoken";
        public static DateTime ExpireCookie = DateTime.Now.AddYears(-10);
        public static DateTime PersistCookie = DateTime.Now.AddYears(10);
        /// <summary>
        /// Stub implementation for logging debug output. You can run
        /// a tool such as 'dbmon' to see the output.
        /// </summary>

Update any broken references that this causes.

Remove all content from webauth-handler.aspx except for the page directive

e.g.




Create the Login Page

The login page is very easy to create. The way that WLID works is you simply insert an IFRAME in to your page which then provides the Sign in link for you. You have control over the basic formatting. See here for more information.

Start by creating a new ASPX page to host the sign in IFRAME. In this example I created a page called Login.aspx in the WebAuth sub folder. In a production application you may want to put your “return” page in this location also.

Place this IFRAME in between the body tags in Login.aspx.


<iframe 
       id="WebAuthControl" 
       name="WebAuthControl"
       src="http://login.live.com/controls/WebAuth.htm?appid=&context=myContext&style=font-size%3A+10pt%3B+font-family%3A+verdana%3B+background%3A+white%3B"
       width="80px"
       height="20px"
       marginwidth="0"
       marginheight="0"
       align="middle"
       frameborder="0"
       scrolling="no"
       style="border-style: hidden; border-width: 0">
   

Note the appid parameter here has been changed from the example to load from the application settings you pasted in earlier.

First Test Run

Before the log in can work correctly the site needs to be running in a virtual called WebAuth (like the sample).

– Start by opening IIS and removing the sample site (WebAuth) you added earlier.
– Next in Visual Studio, right click on the Web Application project and select properties.
– Select the Web tab.
– Select “Use Local IIS Web server” option.
– In the Project URL field, enter http://localhost/WebAuth.
– Click “Create Virtual Directory”
– Close the properties window.

Ensure that Login.aspx is set as the start-up page, and press F5. You should see a Sign in link. Click it, sign in and it will log you in! You will see a 404 error after you log in, fear not as this is expected. This is because in webauth-handler.aspx there is a line which redirects on successful login:


	res.Redirect(WindowsLiveLogin.LoginPage);

Change the LoginPage constant in WindowsLiveLogin.cs to the following to redirect back to the homepage.

public const string LoginPage = "~/default.aspx";

Keeping Them Logged In

Simply creating the WLID cookies doesn’t mean the user is logged in to your site, and certainly doesn’t mean that the system is working with Forms Authentication. Also there hasn’t been any mention of custom Membership yet. The only way to tell the user is logged in at this stage is to check the WLID cookie. This may be okay in some instances, but we want the full implementation with all the bells and whistles!

In this scenario by the time the user has logged in to WLID and WLID has returned to our “return page” the user is authenticated. So calling off to Membership’s ValidateUser() serves only to create the new user in your system. The typical idea here is when they first log in to your site you ask them to enter some information like nickname and email address etc.

Create a new folder off the web root called Membership and add anew class called WLIDMembership.cs. We will implement a very simple membership provider.

For the full code review the sample code available in this article.

Note: The actual implementation of this membership provider is outside the scope of this article. This includes the back-end database look-ups and schema etc. In this example I return hard-coded values.


public class WLIDMembership : MembershipProvider
{
	//Lots of methods that look like this:
	public override bool ChangePassword(string username, string oldPassword, string newPassword)
	{
		throw new NotImplementedException();
	}

	public override MembershipUser GetUser(string username, bool userIsOnline)
	{

		//Normally you would perform a lookup in your own database for this user.

		//This example is hardcoded for brevity.

		//MyUser currentUser = UserManager.GetItem(username);
		//if (currentUser != null)
		//{
		//    return convertUser(currentUser);
		//}
		//throw new ApplicationException("User not found");            

		MembershipUser u = new MembershipUser("WLIDMembership", "SomeNickname", "WLID_From_Database", "", "", "", true, false, DateTime.Now, DateTime.Now, DateTime.Now, DateTime.Now, DateTime.Now);
		return u;
	}

	public override MembershipUser GetUser(object providerUserKey, bool userIsOnline)
	{
		//Lookup user by WLID key

		MembershipUser u = new MembershipUser("WLIDMembership", "SomeNickname", "WLID_From_Database", "", "", "", true, false, DateTime.Now, DateTime.Now, DateTime.Now, DateTime.Now, DateTime.Now);
		return u;
	}

	public override string GetUserNameByEmail(string email)
	{
		//Lookup username and convert to email.
		return "SomeNickname";            
	}

	public override bool ValidateUser(string id, string providerName)
	{
		try
		{
			//Sample of how this may look in a real system

			//MyUser user = UserManager.GetItem(id);
			//if (user == null)
			//{
			//    user = new MyUser();
			//    user.Id = id;
			//    user.CreatedDate = DateTime.Now;                
			//}

			//user.LastLoginDate = DateTime.Now;

			//UserManager.Save(user);                
		}
		catch (Exception ex)
		{
			throw ex;
		}
		return true;
	}  
}

Next set up the Membership provider in the web.config (find the <authentication mode=”Windows”/< tag and place it above that.



  
	
	
  


This sets the membership provider to the be default provider for the site. Now all components that understand Membership can use the new class.

In webauth-handler.cs add the following code to call the Membership Validate method and create the account if it doesn’t already exist:

if (user.UsePersistentCookie)
{
	loginCookie.Expires = WindowsLiveLogin.PersistCookie;
}

//Add this code.
System.Web.Security.Membership.ValidateUser(user.Id, "Windows Live ID");         

Forms Authentication

This is all well and good but the user is _still_ not logged in (as far as Forms Authentication goes)!

Simply calling Membership.Validate() doesn’t actually log the user in, it merely tells the system if the user has provided correct authentication and leaves it at that. There are a few more steps required to hook this up with Forms Authentication.

First there is another change required in the web.config. Overwrite <authentication mode=”Windows”/< with the following:



  


This is a pretty basic Forms Authentication configuration – of interest are the loginUrl and name attributes. loginUrl tells Forms Authentication controls where to redirect the user to log in. The name attribute tells Forms Authentication which cookie to use to verify login status, and also how to log a user out (by removing the cookie, or setting its expiry to the past).

Now all that is left is to make the WLID handler register the user as having logged in with Forms Authentication. This is done by creating a Forms Authentication token in code. Normally you don’t have to perform this step with Forms Authentication as the login controls do it for you, but we are using a more customised login process.

Back in webauth-handler.aspx.cs add the following method:

private static bool createTicket(string userName, bool stayLoggedIn, string type, DateTime cookieTime)
{
	FormsAuthentication.Initialize();

	FormsAuthenticationTicket ticket = new FormsAuthenticationTicket(1, userName, DateTime.Now, cookieTime, stayLoggedIn, type, FormsAuthentication.FormsCookiePath);

	string hash = FormsAuthentication.Encrypt(ticket);

	HttpCookie cookie = new HttpCookie(FormsAuthentication.FormsCookieName, hash);

	if (ticket.IsPersistent) cookie.Expires = ticket.Expiration;

	HttpContext.Current.Response.Cookies.Add(cookie);

	return true;
}

Thanks to Andreas Kraus for the FormsAuthenticationTicket example 🙂

This code creates a new authentication ticket based on the name attribute that is on the forms element in the web.config.

Add a call to this new method right below where you added the call to Membership.ValidateUser():

createTicket(user.Id, user.UsePersistentCookie, "", WindowsLiveLogin.PersistCookie);

Note here the UsePersistentCookie value from WLID is passed through to Forms Authentication. This means that the system will obey their selection to not keep them logged in between browser sessions (“remember me” option).

If you where you add an asp:LoginName control to the homepage now you would see something like 85bf44d80fbed3f5900adfca1e269199 after you Sign in. This is they WLID token unique to the site and the user. You will see what ever you pass in to createTicket… so if you set up your site to request the select a unique nickname on first Sign in and pass that in to createTicket the users will see that nickname in the login control.

Once more small thing is needed to support Forms Authentication Sign out when the user clicks the Sign Out link (which will be automatically shown in the Sign in IFRAME after they have authenticated ie. go back to login page once signed in).

– In webauth-handler.aspx.cs, add FormsAuthentication.SignOut(); to both the action==”logout” section and the action==”clearcookie” section.

Expose a WCF Service

Before Silverlight can read the sign in status we need to configure a WCF service.

– Add a new folder called WebServices
– Add a new Text File called Authentication.svc. We don’t add a normal Silverlight WCF here as we want to have manual control over its configuration etc.
– Past the following code in to the new file:


This tells the service to expose the System.Web.ApplicationServices.AuthenticationService.

Note the Factory attribute here. This is required if you are running the service on a shared server (like an ISP or external web hoster). This prevents WCF services from getting all sorts of strange errors like 404 and “already bound to this address” errors. If you need to implement this then follow these steps. If not remove the Factory attribute and continue.

– Add a reference to System.ServiceModel.
– Add a new class to the same folder called Factory.cs.
– Copy in the following code:

public class AuthenticationServiceHostFactory : ServiceHostFactory
{
    public override System.ServiceModel.ServiceHostBase CreateServiceHost(string constructorString, System.Uri[] baseAddresses)
    {

        return base.CreateServiceHost(constructorString, baseAddresses);

    }
    protected override System.ServiceModel.ServiceHost CreateServiceHost(Type serviceType, System.Uri[] baseAddresses)
    {
        Uri webServiceAddress = new Uri("http://localhost/WebAuth/WebServices/Authentication.svc");
        ServiceHost webServiceHost = new AuthenticationServiceHost(serviceType, webServiceAddress);
        return webServiceHost;
    }
}

public class AuthenticationServiceHost : ServiceHost
{

    public AuthenticationServiceHost(Type serviceType, Uri baseAddresses)
    {

        UriSchemeKeyedCollection BaseAddressScheme = new UriSchemeKeyedCollection();

        BaseAddressScheme.Add(baseAddresses);
        base.InitializeDescription(serviceType, BaseAddressScheme);

    }
    protected override void ApplyConfiguration()
    {

        base.ApplyConfiguration();

    }
    protected override void InitializeRuntime()
    {

        base.InitializeRuntime();

    }
}

You can override the service address by altering the URI above (change it to the real URL of the service in production). It’s probably best to have this as an app setting so you can have prod and dev versions.

Finally you need to expose the service in the web.config. Add the following configuration right above the </configuration> element:


    
      
        
          
        
      
    
    
              
        
          
          
        
      
    
    
          
      
        
        
      
    
  

  
    
      
        
      
    
  

Finally Some Silverlight!

Now we can import the WCF service in to the Silverlight and consume it!

– In your Silverlight project, right click references and select “Add Service Reference”.
– Click Discover. You will see the authentication service.
– In the Namespace field enter “AuthenticationService”
– Click OK.

We will keep the Silverlight application very basic. It will simply be a text block which tells the user if they are logged in or not.

Add a TextBlock to Page.xaml:


In Page.xaml.cs add the following code:

private void performLoginCheck()
{
	AuthenticationService.AuthenticationServiceClient authClient = new Silverlight_Comments.AuthenticationService.AuthenticationServiceClient();            
	authClient.IsLoggedInCompleted += new EventHandler(authClient_IsLoggedInCompleted);
	authClient.IsLoggedInAsync();            
}       

void authClient_IsLoggedInCompleted(object sender, Silverlight_Comments.AuthenticationService.IsLoggedInCompletedEventArgs e)
{
	bool loginState = e.Result;
	LoginStatus.Text = string.Format("The user is{0} logged in!", loginState ? "" : " not");
}

Place a call to performLoginCheck(); right after InitializeComponent(); in the constructor.

You are just about ready to test out your shiny new system! Before you can test it, ensure you Silverlight app is placed in your Default.aspx file – examples on how to do this where added when you created the application called something like “SilverlightCommentsTestPage.aspx”.

Press F5 and see how you go!

NOTE: If you are getting a 404 error, see here.

With any luck your Silverlight application is detecting if the user is authenticated by using Windows Live ID!

NOTE: When you download and run the sample application, ensure you set the Web application as the start-up project or you will have problems.

Silverlight, WCF

Silverlight 2: Call to local WCF service gets 404!

You create a new Silverlight WCF service and a snazzy new Silverlight application to access it to do all this cool stuff. You go in and add a Service Reference, click discover and it automatically finds your WCF for you. You wire up the events and code to make a call to the service…

You run up your Silverlight application and BAM – 404! But shouldn’t it just work?

Well if you get this problem it’s probably cross domain policy raining down on your parade. It kicks in when the domain you are requesting from is not the same as the domain you are requesting to. And yes, this may have just happened even though you aren’t exactly running a root DNS server in your bedroom at mum’s house.

What may be happening is that you are pressing F5 in Visual Studio, which then fires up your app on http://localhost/MyAwesomeApp – but in your Silverlight ServiceReferences.ClientConfig the endpoint address is set to http://devboxoftotalawesome/getrichquick/WebServices/Comments.svc.

Change this to the same URL that Visual Studio fires up your web site (and service) on and voila, your awesome WCF messages spill down the wire.

You may also want to consider editing your cross domain policy file – see the lovely Karen’s article here on this: http://scorbs.com/2008/04/15/silverlight-http-networking-stack-part-2-cross-domain-communication-overview/. Although this will fix the issue, remember that in production you probably do not want to go down this route unless you really do intend outside apps to access your services.

Also keep in mind that the cross domain policy file only works for (web)clients that adhere to it – such as Silverlight and Flash (Flash policy file). It’s important that note that adding a cross domain policy file does not protect your service from nefarious users… it merely prevents Silverlight and Flash apps from accessing your services. The only way to protect your sevices and other data sources from misuse is to apply security – like user authentication etc.