Authenticating mobile Facebook users

Up until now, I have been using Facebook C# SDK to handle authentication of users for Am I Interesting. However, when Facebook released separate handling of mobile users Рwhich is welcome Рthe SDK did not support that. The Facebook error message shown to the users was:

The mobile version of the app “Am I Interesting” is unavailable because it is misconfigured. It appears to be caught in a redirect loop.

The underlying issue is that all users, no matter how they access the application, get redirected to the same URL when using the SDK, in my case to apps.facebook.com/amiinteresting, and that does not work for mobile users. For mobile users, we have to redirect them outside of Facebook’s IFrame.

My authentication goal:
Get a Facebook Access Token and Facebook User Id for all users who login

What I need as far as URLs go:

  • “Regular” users should be redirected to apps.facebook.com/amiinteresting (IFrame within Facebook)
  • Mobile users should be redirected to www.amiinteresting.net (outside Facebook’s frame)

For the solution described below, I followed Facebook’s developer documentation about authentication at:
http://developers.facebook.com/docs/authentication/

For the first login step, I use the following code:

        public ActionResult Login()
        {
            var redirectUrl = string.Format("https://www.facebook.com/dialog/oauth?client_id={0}&scope={1}&redirect_uri={2}",
            _configurationHelper.FacebookAppId,
            string.Join(",", FacebookPermissions.RequiredPermissions),
            _configurationHelper.FacebookRedirectUrl);
            var html = string.Format("<html><body><script type=\"text/javascript\">window.top.location='{0}';</script></body></html>", redirectUrl);
            return Content(html, "text/html");
        }

The Facebook Redirect Url configuration setting above is always http://www.amiinteresting.net. Facebook takes care of instead redirecting to apps.facebook.com/amiinteresting if it’s not a mobile user.

After fixing the redirect problem, I noticed that:

  • When a “regular” user arrives to the application at apps.facebook.com within an IFrame, Facebook posts what’s called a signed_request that can be decoded and contains an access token (which I need), the User ID, and some other nice info (described here).
  • When a mobile user arrives to the application outside Facebook’s frame, I don’t get a signed_request, but rather a “code” in the querystring that can be used to query Facebook together with the application secret to get the access_token and other info as needed.

So – I needed two separate ways to handle this, and implemented them in a method that I call CheckNewFacebookLogin (supporting methods further down):

        public bool CheckNewFacebookLogin()
        {
            var codeInUrl = _currentHttpContext.QueryString["code"];
            var signedRequest = _currentHttpContext.Form["signed_request"];

            if (codeInUrl == null && signedRequest == null)
            {
                return false;
            }

            if (signedRequest != null)
            {

                // Signed Request
                var parametersInSignedRequest = ExtractDataFromRequest(signedRequest);
                var accessToken = parametersInSignedRequest["oauth_token"].ToString();
                var userId = Convert.ToInt64(parametersInSignedRequest["user_id"]);
                if (_sessionVariables.AccessToken != null && _sessionVariables.AccessToken == accessToken)
                {
                    return false; // User already authenticated and no new user logged in
                }
                _sessionVariables.InvalidateSession(); // New app login or new user accessed application
                _sessionVariables.AccessToken = accessToken;
                _sessionVariables.UserInfo = new PersonEntity
                                                 {
                                                     UserId = userId
                                                 };
            }
            else
            {

                // Code in URL
                if (_sessionVariables.FacebookCode != null && _sessionVariables.FacebookCode == codeInUrl)
                {
                    return false; // User already authenticated and no new user logged in
                }
                _sessionVariables.InvalidateSession(); // New app login or new user accessed application
                _sessionVariables.FacebookCode = codeInUrl;
            }

            return true;
        }

If the method returns true = a new user logged in, I display a landing/waiting page and in parallell call this CompleteLogin method (simplified):

        public void CompleteLogin()
        {
            // Get token & userId
            if (_sessionVariables.FacebookCode != null)
            {
                var accessToken = GetFacebookAccessToken(_sessionVariables.FacebookCode);
                _sessionVariables.AccessToken = accessToken;

                var userId = _facebookService.GetUserIdFromFacebookToken(_sessionVariables.AccessToken);
                _sessionVariables.UserInfo = new PersonEntity
                {
                    UserId = userId
                };
            }
        }

        private string GetFacebookAccessToken(string code)
        {
            var requestUrl = string.Format("https://graph.facebook.com/oauth/access_token?client_id={0}&client_secret={1}&code={2}&redirect_uri={3}",
                _configurationHelper.FacebookAppId,
                _configurationHelper.FacebookAppSecret,
                code,
                _configurationHelper.FacebookRedirectUrl);
            var response = _facebookRequest.GetTextResponse(new Uri(requestUrl));
            var parameters = response.Split('&');
            foreach (var parameter in parameters)
            {
                var key = parameter.Split('=')[0];
                var value = parameter.Split('=')[1];
                if (key == "access_token")
                {
                    return value;
                }
            }
            return null;
        }

Supporting methods:

        private static Dictionary<string, object> ExtractDataFromRequest(string signedRequest)
        {
            var jsonEncodedObject = signedRequest.Split('.')[1];
            var json = Base64_Url_Decode(jsonEncodedObject);
            var parameters = SerializeHelpers.DeserializeFromJson(json);
            var parameterDictionary = new Dictionary<string, object>();
            foreach (var parameter in parameters)
            {
                parameterDictionary.Add(parameter.Key, parameter.Value);
            }
            return parameterDictionary;
        }

        public static string Base64_Url_Decode(string input)
        {
            // From http://www.hazardbrick.com/post/2011/04/05/Facebook-Login-Registration-signed_request-Base64-problems-in-ASPNet-C.aspx
            string fixedString;
            var fixedDashString = input.Replace('-', '+');
            var fixedUnderscoreString = fixedDashString.Replace('_', '/');
            if (fixedUnderscoreString.Length % 4 != 0)
            {
                fixedString = String.Format("{0}", fixedUnderscoreString);
                int paddingCount = fixedString.Length % 4;
                while (paddingCount % 4 != 0)
                {
                    fixedString += '='; paddingCount++;
                }
            }
            else
            {
                fixedString = fixedUnderscoreString;
            }
            var inputBytes = Convert.FromBase64String(fixedString);
            return Encoding.UTF8.GetString(inputBytes);
        }

Let me know if you have the same problem and need me to further expand on the solution.

Good luck! ūüôā

Advertisements

Solving “The Remote Desktop configuration was not generated by Windows Azure Tools”

For some time I’ve had issues with enabling Remote Desktop connections through the Publish dialog from within Visual Studio 2010, where I’ve gotten the following error message:

This time I really wanted to start using Web Deploy to iterate changes quickly, so I needed to fix this, and it turned out to be relatively simple. Just follow the following two steps:

Step 1 – Remove the RemoteAccess and RemoteForwarder modules from ServiceDefinition.csdef

<Imports>
  <Import moduleName="RemoteAccess" />
  <Import moduleName="RemoteForwarder" />
</Imports>

Step 2 – Remove all RemoteAccess/RemoteForwarder settings and the RemoteAccess certificate from ServiceConfiguration.*.cscfg

<ConfigurationSettings>
<Setting name="Microsoft.WindowsAzure.Plugins.RemoteAccess.Enabled" value="false" />
<Setting name="Microsoft.WindowsAzure.Plugins.RemoteAccess.AccountUsername" value="xxx" />
<Setting name="Microsoft.WindowsAzure.Plugins.RemoteAccess.AccountEncryptedPassword" value="xxx" />
<Setting name="Microsoft.WindowsAzure.Plugins.RemoteAccess.AccountExpiration" value="2011-12-31T23:59:59.0000000+01:00" />
<Setting name="Microsoft.WindowsAzure.Plugins.RemoteForwarder.Enabled" value="true" />
<Certificates>
<Certificate name="Microsoft.WindowsAzure.Plugins.RemoteAccess.PasswordEncryption" thumbprint="xxx" thumbprintAlgorithm="sha1" />
</Certificates>
</ConfigurationSettings>

Now it should work to enable Remote Desktop (and Web Deploy) through the Publish dialog.

Publically exposing a service on my laptop using the AppFabric Service Bus

To learn a bit more about the AppFabric Service Bus in Azure, I am creating a relatively simple solution and documenting it as I go in this blog post.

The idea: Create a WCF (REST/JSON) service that runs on my laptop and exposes a public endpoint on the Service Bus that I can connect to from any browser to find out where my laptop is physically located. The service must work as long as there is an active Internet connection, even if the computer is behind various firewalls and with NAT.

In other words, very similar to various¬†“Find my device” services that exist, but this one will be even more rough and inaccurate in its positioning ;).

Round 1 РCreate Service Bus namespace

First I needed to create a service bus namespace where my service will be exposed. This is done via the Windows Azure Management Portal. First, navigate to the Service Bus tab and click to create a new service namespace:

Then fill in the unique namespace name that you want and select a region, subscription and connection pack size. The connection pack size has an effect on cost, and I will choose “0 connections” which basically means pay-as-you-go (but at a higher per connection cost than if I would have selected a fixed package size). More information about pricing for the Service Bus can be found here.

That is all that’s needed to create the namespace. Note that there is a button to retrieve a key that we will need later on to publish the service to this namespace

Round 2 – Creating an application that exposes a service on the Service Bus

The next phase is to develop the actual application that will host the service and expose the endpoint on the AppFabric Service Bus. I will do this in a simple Console Application, even though it would be very easy for someone who steals my laptop to shut down the application and stop me from finding it :).

First, we need to reference Microsoft.ServiceBus.dl which comes with the Windows Azure AppFabric SDK and is located in the following folder after installation:
C:\Program Files\Windows Azure AppFabric SDK\V1.5\Assemblies\NET4.0

The target framework for the project has to be changed to the full¬†“.NET Framework 4” to expose a WCF service:

Exposing the service is very similar to creating a “normal” WCF service. One difference is a specific helper method for creating the URI for your service. In the example below the first parameter is the protocol prefix, the second parameter is the service namespace, and the last parameter is the relative path that the service will be exposed on:

ServiceBusEnvironment.CreateServiceUri("https", "findmylaptopdemo", "findmylaptop");

The resulting URL for the service methods will be:
https://findmylaptopdemo.servicebus.windows.net/findmylaptop/METHODNAME

We then need to specify a relay binding, in my case a WebHttpRelayBinding which is recommended for WCF REST implementations. I specify to not require any authentication from the clients:

var binding = new WebHttpRelayBinding();
binding.Security.RelayClientAuthenticationType = RelayClientAuthenticationType.None;

The next thing to create a behavior with the issuer name and secret. This is required to add the service to the AppFabric service namespace. You get the secret and issuer name (default=owner) from the Management Portal as described earlier.

var behavior = new TransportClientEndpointBehavior();
behavior.TokenProvider = TokenProvider.CreateSharedSecretTokenProvider(IssuerName, IssuerSecret);

The last step involves defining the interface and writing the code for the service, just like with any service. Part of the  service interface:

[ServiceContract]
public interface IFindMyLaptopService
{
[OperationContract]
[WebInvoke(
Method = "GET",
UriTemplate = "/ip")]
Stream GetIp();
}

The code for the derived method:

public Stream GetIp()
{
Console.WriteLine("Incoming request for GetIp(), getting and returning external IP:");

var ip = WhatIsMyIpService.GetExternalIpAddress();
Console.WriteLine(ip);
Console.WriteLine("");

var html = string.Format("<html><body>Laptop's IP address: {0}</body></html>", ip);

return new MemoryStream(System.Text.Encoding.UTF8.GetBytes(html));
}

The helper method to retrieve the IP from WhatIsMyIp:

public static IPAddress GetExternalIpAddress()
{
const string whatIsMyIpUrl = "<a href="http://automation.whatismyip.com/n09230945.asp">http://automation.whatismyip.com/n09230945.asp</a>";
var webClient = new WebClient();
var utf8 = new UTF8Encoding();
var html = utf8.GetString(webClient.DownloadData(whatIsMyIpUrl));
var externalIp = IPAddress.Parse(html);
return externalIp;
}

The full solution can be downloaded from:
https://p2piter.blob.core.windows.net/theazureexperience/AzureFindMyLaptop.zip

You will need to enter your own information in the constants at the top of Program.cs:

private const string ServiceNamespace = "findmylaptopdemo";
private const string IssuerName = "owner";
private const string IssuerSecret = "XXXYYYZZZ";

Good luck!

Parallelizing Facebook FQL queries to increase performance

This post really doesn’t have anything to do with Azure, but as my blog has a taste of Facebook, I’ll throw it out there anyhow.

Am I Interesting is an application that reads quite a bit of data from Facebook and needs to execute five different queries against Facebook for each user that is rated to have enough data to calculate the final rating. In the original solution, I performed these queries in a synchronous sequence like this:

I now attempted to instead run the queries as much as possible in parallel:

The code I for testing the parallelism, using the .NET 4.0 Task Parallel Library:

I created a simple web page that displays the results and compares the sequential to the parallel processing:

As you can see, this modification cut the total query time in about half!

For now, the performance after the change to parallel query execution is good enough. The next thing to try when/if we require even faster performance is sending off all queries to Facebook in one call using fql.multiquery.

Testing/QA environment in Azure for Facebook application

With ongoing quite extensive modifications of core business rules for Am I Interesting, I need a testing environment where I can deploy my application so that other selected people (my partners in crime) can help test out that everything works the way it should before we release into production.

My initial idea was to do this by deploying to the staging slot of my existing Hosting Service is Azure, but this was not a good solution for me because:

  • I need a predictable URL for this environment.¬†When deploying to the staging slot in Azure, a GUID-looking URL is automatically generated that cannot be changed, and this does not work for me since I have to put this URL in the Facebook application settings.
  • I am using¬†Facebook C# SDK¬†for authentication from my ASP.NET¬†MVC application that reads all Facebook settings, including the application URL,¬†from web.config, which cannot be modified after deployment. And I don’t know the URL before¬†the application is deployed.
  • The staging slot for Azure is really intended for a final smoke test before going into production and should therefore have a production-like physical setup, but for my testing environment I only want/need one Extra Small instance to save cost.

The solution therefore was to create a new Hosted Service and use its production slot, which has a known URL, for my testing environment:

An arguable disadvantage of using a new Hosting Service with a predictable and fixed public URL is that it’s more likely to be hit by users who shouldn’t have access to the testing environment. However, this is not a big issue in my case since the Facebook sandbox mode prevents users, other than those that I have explicitly configured, from getting access to the application.

As any other testing environment, I need storage that’s separated from production, so I have created a new SQL Azure database also setup a new storage account¬†for tables and queues¬†(the “p2piter” account below is the one I use in production):

I have then setup a new Facebook application (in sandbox mode) and pointed that application to the new URL:

To ease deployment, I have added a new Service Configuration with settings specific for the testing environment, like connection strings to Azure Storage and SQL Azure:

Also, I’ve added a new build configuration with a corresponding¬†web.config transformation that contains, among other things, the Facebook application settings that the Facebook C# SDK reads:

When it’s time to deploy, I can now simply choose my new service and build configuration to create the complete deployment package:

Done! ūüôā

Using ACS for Facebook Authentication from WP7 (Part 1/2)

On top of the ASP.NET MVC web app for Am I Interesting (AII), I’m also adding a Windows Phone 7 client application. This application needs to first authenticate against Facebook and retrieve a Facebook access token before passing¬†that token when authenticating against the API for AII.

This is a two-part blog post:
Part 1 – Configuring ACS for Facebook Authentication
Part 2 – Implementing ACS in your WP7 app (next post)

For this authentication, I will use Azure AppFabric Access Control Service (ACS) from the WP7 app. ACS¬†offers a convenient way to¬†authenticate against identity providers such as Windows Live, Facebook, Google and Yahoo, and it’s priced at only $1.99 per 100,000 transactions. If you, like me, have an MSDN subscription, you also have 100,000-500,000 transactions per month included free of charge.

To set up ACS, go to the Windows Azure Management Portal, navigate to “Service Bus, Access Control & Caching” and click to create a new service namespace:

Check the Access Control check box and enter a unique namespace, “amiinteresting” in my case.

The next step is to add Facebook as an identity provider to your created service namespace. Click to manage Access Control Service:

Click on Identity providers and then Add:

Enter a display name, your Facebook Application Id and¬†Facebook Application Secret¬†(from the Facebook developer portal), and a comma-separated list of Application permissions that you need. Offline_access is needed if you don’t want your WP7 users to have to enter their login credentials every time they start the app:

I have added two Identity providers for the two different Facebook applications I use for Development (local debugging) and Production with different Application Ids and Application Secrets.

The next step is to tell ACS about the client application (relying party) that will connect to ACS to authenticate users. To do this, navigate to Relying party applications and click on Add:

For a thick client application, that will not use browser redirection, it’s enough to enter a display Name, a URI¬†Realm that the token should be issued for (a bit tricky subject, described here), which in my case is the application URI https://www.amiinteresting.net, and a Token format. I have chosen a Simple Web Token (SWT) which is suitable for tokens that are be passed in the HTTP header or request, which is normally the case when communicating with REST (or REST-alike) API:s:

Further down on the page, choose the Identity provider that was added in a previous step and click to Generate a token signing key for this relying party application:

The last thing you need to do is configure the Rule group that was automatically created when you added the relying party. A rule group is used to define how claims from the identity provider (Facebook) are passed to the relying party (WP7 app). Navigate to Rule groups and click on the automatically added rule group to configure:

On the screen that appears, no rules will have been added yet. The rules can be added individually/manually, or you can click on Generate to automatically generate rules for you:

On the next screen, I have chosen to generate rules for both my Facebook applications (development and production):

I then modified the generated rules by removing the emailaddress claim (I don’t need and therefore didn’t configure the Identity provider to request the email permission from Facebook) and changing the description of the nameidentifier claim to “Facebook User Id” (which it is):

Done! ACS is now configured for Facebook authentication and ready to be used by WP7!

Stay tuned for the next post that covers how to connect the WP7 app to ACS and ultimately get the Facebook access token, which is required to authenticate against the API for Am I Interesting….

Planned blog entries

Below are some of my planned blog posts. Help me prioritize by letting me know if you find anything in partciular extra interesting, or if the list is missing something of interest.

  • Facebook applications – the perfect match for Windows Azure (Completed)
  • Application architecture for Am I Interesting
  • Session state in Azure AppFabric Cache
  • SQL Azure and/or Azure Table Storage?
  • Generic class to work with Azure Table Storage
  • Using own property names instead of PartitionKey and RowKey
  • Adding a certificate for SSL/HTTPS
  • Using a Web Role background thread to host a worker process
  • Using Azure Queues for async processing
  • Exponential back-off polling of queues
  • Dirty way of realizing transactions over SQL Azure and Azure Table Storage
  • Retrying SQL Azure connections
  • Repository interface for unit testing
  • Using a Startup Task to avoid IIS Idle Timeout
  • Activating diagnostics (event logs, trace logs and performance counters)
  • Using log4net for trace logging
  • Operating cost as a new parameter for product owners to prioritize development