Knowledge is power. We love to share it.

News related to Mono products, services and latest developments in our community.

pajo

MonoX: ASP.NET support for OpenSocial

03/23/2011Categories: MonoX
The number of social networking platforms has been growing at a steady rate over the last couple of years. Diversity is a good thing, but it would be even better if you wouldn't need to keep up with a growing list of site-specific APIs. To overcome this problem, the OpenSocial group was formed in 2007 to define a common API for social applications across multiple websites. The good news is that the upcoming release of MonoX includes the support for the OpenSocial Server API 1.1. To the best of our knowledge, it is the first downloadable ASP.NET CMS application with such level of support for the OpenSocial standard.

First we'll give a brief technical overview of the OpenSocial server implementation and later we'll take a look on how it is implemented in MonoX.

OpenSocial server is standalone library that handles oAuth authentication plus REST and OpenSocial protocol details. To enable OpenSocial server you need to add the server configuration section in the web.config:

<configSections>
<section name="openSocial" type="OpenSocial.Configuration.OpenSocialConfiguration, OpenSocial"   requirePermission="false" />
</configSections>

After that we can continue by specifying MonoX-specific implementation details:

<openSocial tokenManager="MonoSoftware.MonoX.BusinessLayer.OAuthTokenManagerBLL, MonoX"
authorizationEndpoint="/MonoX/OpenSocial/OAuth.ashx"
authenticationPage="/MonoX/OpenSocial/OpenSocialAuthorizationPage.aspx"
dataAccessAdapter="MonoSoftware.MonoX.OpenSocial.MonoXOpenSocialDataAccessAdpater, MonoX" />

All configuration attributes displayed above are required. 
tokenManager points to the class that implements the oAuth token manager that subsequently requires implementation of the two interfaces: DotNetOpenAuth.OAuth.ChannelElements.ITokenManager and DotNetOpenAuth.OAuth.ChannelElements.IServiceProviderTokenManager. Both interfaces are a part of the dotnetOpenAuth thrid party library used for handling oAuth protocol details. 
dataAccessAdapter is pointing to the implementation of the OpenSocial services data access methods that require implementation of the OpenSocial.Service.IOpenSocialDataAccessAdapter interface. 
authorizationEndpoint is defining the physical path to the oAuth handler used to negotiate and authenticate oAuth requests.
authenticationPage specifies the authorization page where users can decide if they will allow access to their resources or not. This is the point where token will be authorized. To fully implement the authentication page you have to posses some basic knowledge about the oAuth protocol and the dotnetOpenAuth library. Since oAuth protocol allows flexible and temporal permissions, specific authorization implementation details can vary. MonoX sample implementation gives access to all resources for unlimited time. At this moment, OpenSocial server supports only 3-legged oAuth, you can find more about oAuth use for the OpenSocial REST API in this paper.

Below is the code for our sample authentication page:

Front end:
<%@ Page Title=""
    Language="C#"
    MasterPageFile="/MonoX/MasterPages/Default.master"
    AutoEventWireup="true"
    CodeBehind="OpenSocialAuthorizationPage.aspx.cs"
    Inherits="MonoSoftware.MonoX.OpenSocial.OpenSocialAuthorizationPage" %>
 
<asp:Content ID="Content1" ContentPlaceHolderID="cp" runat="server">
 
<%= String.Format
(MonoSoftware.MonoX.Resources.DefaultResources.OpenSocial_RequestMessage,
CurrentToken.OaConsumer.Name) %>
 
<asp:Button ID="btnAllow" runat="server" Text="Allow" />
<asp:Button ID="btnDeny" runat="server" Text="Deny" />
 
</asp:Content>

Code behind:

using System;
using System.Security;
using DotNetOpenAuth.OAuth.Messages;
using MonoSoftware.MonoX.DAL.EntityClasses;
using OpenSocial.Service;
   
namespace MonoSoftware.MonoX.OpenSocial
{
    public partial class OpenSocialAuthorizationPage : BasePage
    {
        #region Properties
        private OaTokenEntity _currentToken = null;
        protected OaTokenEntity CurrentToken
        {
            get
            {
                if (_currentToken == null)
                {
                    ITokenContainingMessage requestTokenMessage = OpenSocialOAuthServiceProvider.PendingAuthorizationRequest;
                    _currentToken = 
                    (OaTokenEntity)
OpenSocialOAuthServiceProvider.ServiceProvider.TokenManager.GetRequestToken
(requestTokenMessage.Token);
                }
   
                return _currentToken;
            }
        
        #endregion
   
        #region Page Events
        protected void Page_Init(object sender, EventArgs e)
        {
            if (!Page.User.Identity.IsAuthenticated)
            {
                throw new SecurityException();
            }
   
            btnAllow.Text = MonoSoftware.MonoX.Resources.DefaultResources.OpenSocial_Allow;
            btnDeny.Text = MonoSoftware.MonoX.Resources.DefaultResources.OpenSocial_Deny;
   
            btnAllow.Click += new EventHandler(btnAllow_Click);
            btnDeny.Click += new EventHandler(btnDeny_Click);
        }
   
        protected void Page_Load(object sender, EventArgs e)
        {
        
        #endregion
   
        #region UI Events
        protected virtual void btnDeny_Click(object sender, EventArgs e)
        {
            OpenSocialOAuthServiceProvider.PendingAuthorizationRequest = null;
        }
   
        protected virtual void btnAllow_Click(object sender, EventArgs e)
        {
            // Use OpenSocialOAuthServiceProvider for handling OAuth tokens
            var tokenManager = OpenSocialOAuthServiceProvider.ServiceProvider.TokenManager;
            // Pending authorization request is passed through session
            var pendingRequest = OpenSocialOAuthServiceProvider.PendingAuthorizationRequest;
   
            var requestToken = CurrentToken;
   
            requestToken.UserId = MonoSoftware.MonoX.Utilities.SecurityUtility.GetUserId();
            // You can set token expiration date using requestToken.ExpirationDate 
            // e.g. requestToken.ExpirationDate = DateTime.UtcNow.AddHours(3);
            // Use requestToken.Scope for a scope of permissions requested
            tokenManager.UpdateToken(requestToken);
               
            var response = OpenSocialOAuthServiceProvider.AuthorizePendingRequestTokenAsWebResponse();
            if (response != null)
            {
                // The consumer provided a callback URL that can take care of everything else.
                response.Send();
            }
        
        #endregion
    }
}


OpenSocialOAuthServiceProvider is a static class inside OpenSocial server library that provides methods for handling OAuth authorization tasks. When a service recevie user authorization request token it will store this request in a session and transfer execution to the authentication page. Using this token we can access OaTokenEntity, a MonoX object representing OAuth token, and set all required parameters before authorizing it. In this case we expect consumer to provide a callback URL in its request parameters; a scenario where callback url is known to the server in advance is not currently covered in MonoX.

Now that we have covered the OpenSocial server configuration we can proceed to the server implementation. OpenSocial server is implemented using WCF and requires .net framework 3.5 to run. Currently it implements three OpenSocial services - people, groups and activites - and supports JSON and XML output formats, while JSON is the only supported input format at this moment.

The OpenSocial server is running inside the same process as MonoX CMS. This allows for the easy creation of the authentication page as we can use all the goodies MonoX offers. On the other hand, all service requests are pushed through asp .net request lifecycle and this can affect the behavior of the service. We have experienced several problems when Forms authentication is activated. One of the features of the FormsAuthentication module is that it will redirect all responses with 401-unauthorized status to the login page. As a result, the client will see 301-moved status  instead of 401-unauthorized. To avoid this we had to add another module that disables FormsAuthentication module when service request is processed. In order for OpenSocial server to work correctly with Forms authentication, you have to add the OpenSocialModule in your cofiguration:

<httpModules>
    <add name="OpenSocialModule" type="MonoSoftware.MonoX.OpenSocial.OpenSocialHttpModule, MonoX" />
</httpModules>

This setting will disable FormsAuthentication module for a default location. Subsequently, it is not possible to move the OpenSocial server to a different location without changing the the module. The server will work correctly with default MonoX settings, but you should be careful if you're adding new modules to the  pipeline and you want to use the OpenSocial server.

The default location of the OpenSocial server is "/MonoX/OpenSocial/OpenSocial.svc". To test if the server component is running, you can type this address in your browser (for example, http://yourdomain/MonoX/OpenSocial/OpenSocial.svc) and you should get the XRDS discovery response with list of supported services and their endpoints:

<XRDS xmlns="xri://$xrds">
  <XRD version="2.0" xmlns="xri://$XRD*($v*2.0)" xmlns:simple="http://xrds-simple.net/core/1.0">
  <Type>xri://$xrds*simple</Type>
  <Service>
  </Service>
  <Service>
  </Service>
  <Service>
  </Service>
  </XRD>
</XRDS>

If you get a similar response your OpenSocial server is up and running. By default, all resources are protected and you have to authenticate to access them through OpenSocial server. To setup client oAuth provider set all endpoints - authorizeUrl, accessTokenUrl and requestTokenUrl  - to OAuth.ashx handler (its default address is http://yourdomain/MonoX/OpenSocial/OAuth.ashx).  OpenSocial server is currently in a test phase and at the moment you can't register applications with MonoX. You will need to use a test consumer to authenticate with OpenSocial server - use "Ud4IhqeyIEmEHEmApOWN-A" as the applicationKey and "anonymous" as the secret value. You can find all consumer details in the OaConsumer table inside MonoX database. Application key is constructed from the consumer id as its ShortGuid string representation, while secret is a column in the DB table and can be of any value.
Rated 5.00, 7 vote(s).