Using Windows Azure AppFabric Access Control Service in an iPhone App to Integrate with Facebook

Intro

This is the third post in a series that shows how to use Windows Azure as a platform that provides key services for native mobile applications. In the first two parts of the series, I walked through the process of creating my SpeakEasy app, a fictitious iPhone app that keeps track of speakers and their events.

In this post, I will walk through how to use the Azure AppFabric Access Control Service (ACS). ACS is a service that provides federated authentication using claims. ACS provides support for different identity providers, including Facebook, Windows Live, Google, Yahoo!, and ADFS 2.0 (and other WS-Federation identity providers). In this scenario, I will be using Facebook as an identity provider. Rather than force the user to log in using their Facebook account right away, I will only ask the user to log in when they want to share the details of an event on their Facebook wall. When they attempt to share the event, they will first have to log in using their Facebook account and then, by using the token received from Facebook (which will be provided as a claim), I will show how to post a message on the user's wall.

As a reminder, this series includes:

Facebook Application Setup

To start with, we need to create a Facebook application:

  1. Go to http://www.facebook.com/developers and log in.
  2. Click the Create New App button.
  3. In the Create New App dialog, enter an appropriate name and the optional namespace. I have configured my application with the following settings:
    • App Name: SpeakEasy iPhone App from DeviantPoint
    • App Namespace: speakeasy-dp.
    • This is optional but if you want to use Facebook’s Open Graph to post different Actions and define custom Object Types, you’ll need a namespace (I presume to prevent naming collisions).

After you have agreed to the Facebook Platform Policies agreement and clicked the Continue button, you will be taken to your app’s settings page. On this page are two very important pieces of information: the App ID and App Secret. Make a copy of both values as this will be used later on in the ACS setup. The rest of the application configuration is as follows:

  • Contact Email: <my email>
  • Website Site URL: https://<ACS-Service-Namespace>.accesscontrol.windows.net.
    • This is the URL that Facebook will redirect to after the user has successfully logged in. While you’re debugging your application, it’s ok to configure this section and set the URL to your Azure ACS service namespace URL (details in following section). However, when you’ve published your app to the App Store, you’ll want to delete this section and then configure the Native iOS App section correctly to point to your app in the app store.

ACS Setup

Configuring a Service Namespace

  1. Log in to the Azure Management Portal and go to the Service Bus, Access Control & Caching section.
  2. Select Services > Access Control from the left pane and click on the New menu item from the ribbon. My Service Namespace configuration looks like this:

image

The end result of this will be an ACS URL for my namespace: https://speakeasy.accesscontrol.windows.net.

Once the Service Namespace has been created, you will be redirected to the Access Control Service management portal for your new namespace.

Configuring Facebook as an Identity Provider

The next step is configuring your Identity Providers (idP). In this example, we’ll only be using Facebook as our idP. However, you can configure multiple idPs to allow your users to log in using different idP services, like Facebook, Google, Yahoo, Windows Live, and ADFS 2.0.

  1. Click on the Identity Providers link under Trust Relationships in the left pane.
  2. Click on the Add link.
  3. Select Facebook Application from the custom identity provider section and click Next.
  4. Configure the following settings:
    • Display name: Facebook
    • Application ID: this is one of the values that you should have copied from the Facebook Application Setup. Enter that value here.
    • Application secret: this is one of the values that you should have copied from the Facebook Application Setup. Enter that value here.
    • Application permissions:email, publish_stream
      • The application permissions field is used to tell Facebook what permissions your app will need. The email is given by default. The publish_stream permission will allow our app to post on the user’s behalf on his/her newsfeed. Here is a list of all application permissions you can request.
  5. Click Save.

Configuring a Relying Party Application

The next step is to configure a relying party application. A relying party application is an application that trusts the identity provider and consumes the claims made by that identity provider about the user.

  1. Click on the Relying party applications under Trust Relationships in the left pane.
  2. Click on the Add link.
  3. The following are the configuration settings I used for my relying party application:
    • Name: speakeasy.deviantpoint.com
    • Mode: manual
    • Realm: uri:speakeasy.deviantpoint.com
    • Return URL:empty
      • Normally, this is where ACS will redirect your application to after the user has successfully logged in. Since we’re building a native iPhone app, this setting isn’t needed. However, it is sometimes still a good idea to set this to a web page that you’ve built that can take the incoming claims and do something with them. I like to set this to a page on my site where I have some code to read the incoming claims so that I am able to look at them in case I need to debug something.
    • Error URL: empty
    • Token Format:SWT
      • This setting is important. Originally, I had this set to SAML 2.0 but I realized after some debugging efforts that the Windows Azure Toolkit for iOS is expecting the format to be SWT (Simple Web Token).
    • Token Encryption Policy: None
    • Token Lifetime: 600 (secs)
    • Identity Providers: Facebook
    • Rule Groups: Create New Rule Group
    • Token Signing: Use Service Namespace Certificate
  4. Click Save.

Configuring Rule Groups

Rule Groups determine how incoming claims from the identity provider are mapped to output claims delivered to the relying party application.

  1. Click on Rule groups under Trust Relationships in the left pane.
  2. Click the Generate link and select the Facebook identity provider.

Clicking the Generate link will create a default set of rules for the identity provider. ACS is smart enough to tell a standard set of claims that most of the idPs provide. If the idP provided more claims than what was generated, then you can add those claims as well. Similarly, if there are claims that your relying party application doesn’t need, you can remove them as well.

Assuming you’ve set up the Facebook application and ACS (identity provider, relying party application, and rule groups) correctly, then you should be able to use the link to an ACS-hosted login page, found in the Application Integration section under Development in the left pane, to test the process. Note that unless you configured a Return URL for the Relying Party Application, you will get an ACS50011: The RP ReplyTo address is missing.’ error. It’s probably best to configure a Return URL at first for testing purposes to make sure your setup is correct.

Using ACS in the iPhone App

Modifying the Windows Azure iOS Toolkit

Now that the set up is complete, we can start to use ACS in the SpeakEasy app – except for one little detail: if you downloaded the iOS toolkit from the Master branch on Github, the toolkit currently filters out any claims that don’t start with a prefix of ‘http://schemas.xmlsoap.org/ws/2005/05/identity/claims/’. Fortunately, you have two options. The first option is to download the toolkit from the Develop branch. This branch removes the check for this prefix and just passes any claims through. The second option is to just modify the source code yourself. This is the option I took since I wasn’t sure exactly what other changes have been made to the Develop branch and I didn’t want to introduce any unknowns.

If you’re modifying the source code, find the file WACloudAccessToken.m. Around line 80, you will see the following code:

  1: 
  2: NSString* claimsPrefix = @"http://schemas.xmlsoap.org/ws/2005/05/identity/claims/";
  3: 			
  4: for(NSString* part in [_securityToken componentsSeparatedByString:@"&"])
  5: {   
  6: 	NSRange split = [part rangeOfString:@"="];
  7: 	if(!split.length)
  8: 	{
  9: 		continue; // weird
 10: 	}
 11: 	
 12: 	NSString* key = [[part substringToIndex:split.location] URLDecode];
 13: 	NSString* value = [[part substringFromIndex:split.location + 1] URLDecode];
 14: 	
 15: 	if([key hasPrefix:claimsPrefix])
 16: 	{
 17: 		key = [key substringFromIndex:claimsPrefix.length];
 18: 		[claims setObject:value forKey:key];
 19: 	}
 20: }
 21: 
 22: _claims = [claims copy];

 

Change this code to comment out the offending lines:

  1: //NSString* claimsPrefix = @"http://schemas.xmlsoap.org/ws/2005/05/identity/claims/";
  2: 
  3: for(NSString* part in [_securityToken componentsSeparatedByString:@"&"])
  4: {   
  5: 	NSRange split = [part rangeOfString:@"="];
  6: 	if(!split.length)
  7: 	{
  8: 		continue; // weird
  9: 	}
 10: 	
 11: 	NSString* key = [[part substringToIndex:split.location] URLDecode];
 12: 	NSString* value = [[part substringFromIndex:split.location + 1] URLDecode];
 13: 	
 14: // if([key hasPrefix:claimsPrefix])
 15: // {
 16: // key = [key substringFromIndex:claimsPrefix.length];
 17: 		[claims setObject:value forKey:key];
 18: // }
 19: }
 20: 
 21: _claims = [claims copy];

 

Rebuild the toolkit and re-add a reference to the SpeakEasy project.

UI Changes

Now that the Toolkit is ready, we want to add the ability for the user to post an event’s details to his Facebook newsfeed. The easiest place to start is to make changes to the user interface,

Storyboard Changes

  1. Open up the MainStoryboard.storyboard file and find the Event Details view.
  2. From the Object library, drag a Bar Button Item to the navigation bar.
  3. Select the Bar Button Item and change the Identifier to Action in the Attributes Inspector.
  4. Add a View Controller from the Object library to the Storyboard.
  5. Select the View Controller and change the Top Bar to Navigation Bar and the Bottom Bar to Tab Bar.
  6. Change the Navigation Item’s title to Post to Facebook.
  7. Add a Text View and a Round Rect Button from the Object library to the new view.
  8. Resize the Text View to be about 1/3 of the height of the view and almost the full width.
  9. Change the text of the button to Post!.
  10. CTRL+Drag from the Event Details View Controller to the Post to Facebook View Controller.
  11. Select Push for the Segue type.
  12. Set the segue identifier to PostToFacebookSegue.

The new part of the storyboard should now look like this:

image

PostToFacebookViewController

Now we need a custom view controller that will interact with the new Post To Facebook view.

  1. From the Project navigator, right-click the ViewControllers group and select New File.
  2. Select the UIViewController subclass template and click Next.
  3. Name the class PostToFacebookViewController as a subclass of UIViewController.
  4. Click Next a few times until the .h and .m files are created.
  5. Open the .h file and add #import statements for SEEvent.h and WACloudAccessToken.h.
  6. Add the following code to the interface definition:
  1: @property(nonatomic, retain) WACloudAccessToken *acsToken;
  2: @property(nonatomic, retain) SEEvent *event; 
  3: @property(nonatomic, retain) IBOutlet UITextView *messageTextView;
  4: 
  5: -(IBAction)postMessage:(id)sender;

 

The WACloudAccessToken is a class from the Windows Azure iOS Toolkit that represents an ACS access token.

The IBAction postMessage: is the action that will be called when the Post! button is pushed by the user. Synthesize the properties in the .m file and connect the two outlets to their interface counterparts in the storyboard. Add the postMessage: method to PostToFacebookViewController.m but just leave the implementation blank for now.

EventDetailsViewController

Now we need to add code to handle transitioning from the Event Details View to the Post To Facebook View. To do this, we need to modify the EventDetailsViewController.

  1. Open EventDetailsViewController.hand add the following field:
    • WACloudAccessControlClient *_acsClient;
  2. Add the following method:
    • -(IBAction)shareButtonPressed:(id)sender;
    This method is the event handler for when the user clicks on the Action button we added in the storyboard for this view.
  3. Open the EventDetailsViewController.m.
  4. Modify viewDidLoad so it looks like the following:
  1: - (void)viewDidLoad
  2: {
  3:     [super viewDidLoad];
  4:     
  5:     _acsClient = [WACloudAccessControlClient accessControlClientForNamespace:@"speakeasy" 
  6:                                                                        realm:@"uri:speakeasy.deviantpoint.com"];
  7: }

 

The above code is where we use the WACloudAccessControlClient class provided by the iOS Azure toolkit to create a client we can use to hit the ACS service. The namespace and realm passed in are the namespace/realm that was configured as part of the ACS setup.

Modify the prepareForSegue:sender: method so it looks like the following:

  1: - (void)prepareForSegue:(UIStoryboardSegue *)segue sender:(id)sender
  2: {
  3: 	if ([segue.identifier isEqualToString:@"EventPresenterSegue"])
  4: 	{
  5: 		PresenterDetailsViewController *presenterDetailsViewController = segue.destinationViewController;  
  6: 		presenterDetailsViewController.speaker = self.presenter;
  7: 	}
  8:     else if ([segue.identifier isEqualToString:@"PostToFacebookSegue"])
  9:     {
 10:         PostToFacebookViewController *postToFacebookViewController = segue.destinationViewController;
 11:         postToFacebookViewController.event = self.event;
 12:         postToFacebookViewController.acsToken = [WACloudAccessControlClient sharedToken];
 13:     }
 14: }

In the above code, we just add a new condition for the new segue we added. If the segue we’re trying to perform is the segue to move to the Facebook view, then we pass the event and the access token (which is available from the WACloudAccessControlClient after the user has successfully signed in) to that view.

Finally, to handle the Action button being clicked, add this method to the bottom of the file:

  1: -(IBAction)shareButtonPressed:(id)sender
  2: {  
  3:     [_acsClient showInViewController:self allowsClose:YES withCompletionHandler:^(BOOL authenticated) 
  4:     {   
  5:         if(authenticated) 
  6:         {
  7:             [self performSegueWithIdentifier:@"PostToFacebookSegue" sender:self];
  8:         }        
  9:     }];    
 10: }

 

This method calls the showInViewController:allowsClose:withCompletionHandler method on the WACloudAccessControlClient instance. When the method runs after the button is clicked, the facebook login screen will be shown to the client:

image image

After the user has successfully logged in, the completion handler code will run which basically checks to see if the user is logged in and if so, performs the segue to the Post To Facebook view.

Handling the segue to PostToFacebookViewController

In PostToFacebookViewController.m, add the following to viewDidLoad to add a default message to the Text View in the view:

  1: - (void)viewDidLoad
  2: {
  3:     [super viewDidLoad];
  4:     
  5:     messageTextView.text = [NSString stringWithFormat:@"I'll be attending %@ presented by %@ on %@ in %@",
  6:                             _event.eventName, _event.speakerName, _event.eventDateAsString, _event.eventLocation];
  7: }

 

This is a sample of what the view looks like when it is first loaded from the Event Details View:

image

Finally, to post the actual message to Facebook when the Post button is clicked, modify postMessage to look like the following:

  1: -(IBAction)postMessage:(id)sender
  2: {
  3:     NSString *accessToken = [acsToken.claims objectForKey:@"http://www.facebook.com/claims/AccessToken"];
  4:     NSURL *url = [[NSURL alloc] initWithString:[[NSString alloc] initWithFormat:@"https://graph.facebook.com/me/feed?access_token=%@&message=%@", 
  5:                                                 accessToken,  [messageTextView.text stringByAddingPercentEscapesUsingEncoding:NSUTF8StringEncoding]]];
  6:     
  7:     NSMutableURLRequest *request = [NSMutableURLRequest requestWithURL:url];
  8:     [request setHTTPMethod:@"POST"];
  9:     
 10:     NSURLConnection *conn = [[NSURLConnection alloc] initWithRequest:request delegate:self startImmediately:YES];
 11:     
 12:     if (conn)
 13:     {        
 14:         [[self navigationController] popViewControllerAnimated:YES];
 15:     }
 16:     else
 17:     {
 18:         //todo: add error message
 19:     }
 20: }

 

In the first line of this method, I retrieve the Facebook access token that is returned to me as a claim from Facebook after a successful login. The claims property of the WACloudAccessToken is a collection of all the claims that were passed through from the idP to the relying party application.

The rest of the lines are just used to create a POST request to Facebook. Any requests made to Facebook needs to include the access token (passed in as the access_token parameter). The message parameter is the actual text that will be posted to the feed, which must be URL-escaped.

If the post is successful, then I just navigate back to the details view (I didn’t any any error-handling in this example). A successful post to Facebook looks like this:

image

Conclusion

That’s really all to using ACS from an iPhone app using the Windows Azure Toolkit for iOS. Like I mentioned earlier, you can use ACS for your app’s authorization and authentication needs with other, multiple idPs. This is an especially low barrier-of-entry for apps that need to use Active Directory by taking advantage of ADFS through ACS.

I hope you enjoyed this post. If you have any questions, ask them in the comments section and I’ll try to answer them as best as I can. For the next part of the series, I will cover using Azure to send push notifications.