Saturday, May 21, 2011

OpenID authentication:Passing values to RP from provider using dotnetopenauth attribute exchange

Hi all,
I had a recent opportunity to integrate two portals. In the start things looked easy as the sample application of open id rocked and also we had the chance to code both provider and relying party. But things starts to take an ugly turn when i need to pass a number of values from the provider to relying party. There were not many documentations or blogs which came to my rescue hence thought that i will place this here.
I will try to focus on how the attribute exchange been accomplished using DotNetOpenAuth and will not detail out how the openid authentication be done end to end. [you will be able to find out a sample working openid authentication demo code from net]
OK let’s start straight to business:
Aim:
Pass following values (class level properties) from open id provider to RP
- ProviderID
- SelectedCustomerID
- SaleAmount

Note: The XRDS documents used at both ends and how they are working are not in the scope of this blog.

/// OpenID Provider code to send unsolicited assertion.
----------------------------------------------------

Namespace:
using DotNetOpenAuth.OpenId;
using DotNetOpenAuth.OpenId.Extensions.AttributeExchange;
using DotNetOpenAuth.OpenId.RelyingParty;

// get the relying party url from config
string rpSite = Identifier.Parse(Convert.ToString(ConfigurationManager.AppSettings["SSOURL"]) );
// get the provider end point
Uri providerEndpoint = new Uri(Request.Url, Page.ResolveUrl("~/Server.aspx")); // DotNetOpenAuth.OpenId.Provider.ProviderEndpoint
OpenIdProvider openIdProvider = new OpenIdProvider();
FetchResponse fetchResponse = new FetchResponse();
AXUtilities.Add(fetchResponse.Attributes, " ProviderID ", new string[] { this. ProviderID });

AXUtilities.Add(fetchResponse.Attributes, " SelectedCustomerID ", new string[] { this.SelectedCustomerID });
AXUtilities.Add(fetchResponse.Attributes, " SaleAmount", new string[] { this. SaleAmount });

// request to the Relying party
openIdProvider.SendUnsolicitedAssertion(providerEndpoint, rpSite, Util.BuildIdentityUrl(), Util.BuildIdentityUrl(), new IExtensionMessage[] {
fetchResponse });

--------------------------------------------------------
// Relying party code to receive back the data send across

Namespaces:

using DotNetOpenAuth.Messaging;
using DotNetOpenAuth.OpenId;
using DotNetOpenAuth.OpenId.Extensions.AttributeExchange;
using DotNetOpenAuth.OpenId.Provider;
using OpenIdProviderWebForms.Code;



OpenIdRelyingParty openid = this.CreateRelyingParty();
var response = openid.GetResponse();
if (response != null)
{
    switch (response.Status)
    {
        case AuthenticationStatus.Authenticated:
            // var fetch = response.GetExtension();
            if (fetch != null)
            {
                string ProviderID = fetch.Attributes.Contains(" ProviderID ")
                    ? fetch.Attributes[" ProviderID "].Values.FirstOrDefault()
                    : string.Empty;
                string SelectedCustomerID = fetch.Attributes.Contains(" SelectedCustomerID ")
                        ? fetch.Attributes[" ProviderID "].Values.FirstOrDefault() : string.Empty;
                string ProviderID = fetch.Attributes.Contains("SaleAmount")
                    ? fetch.Attributes[" SaleAmount"].Values.FirstOrDefault() : string.Empty;
            }
            break;
        case AuthenticationStatus.Canceled:
        case AuthenticationStatus.Failed:
        default:
            // signal error in Elmah
            // redirect to ERROR PAGE
            break;
    }
}
// Note: we can use normal instantiation of the relying party instead of this but the below one is the recommended approach
private OpenIdRelyingParty CreateRelyingParty()
{
    OpenIdRelyingParty openid = new OpenIdRelyingParty();
    int minsha, maxsha, minversion;
    if (int.TryParse(Request.QueryString["minsha"], out minsha))
    {
        openid.SecuritySettings.MinimumHashBitLength = minsha;
    }
    if (int.TryParse(Request.QueryString["maxsha"], out maxsha))
    {
        openid.SecuritySettings.MaximumHashBitLength = maxsha;
    }
    if (int.TryParse(Request.QueryString["minversion"], out minversion))
    {
        switch (minversion)
        {
            case 1: openid.SecuritySettings.MinimumRequiredOpenIdVersion = ProtocolVersion.V10; break;
            case 2: openid.SecuritySettings.MinimumRequiredOpenIdVersion = ProtocolVersion.V20; break;
            default: throw new ArgumentOutOfRangeException("minversion");
        }
    }
    return openid;
}


Best of luck and i am sure will be able to see a lot of materials in the internet to achieve openid authentication. [The stories about xrds schema and how the meta data discovery and page posting works will be a good read in net].