asp.netopeniddotnetopenauthasp.net-4.5liveid

Processing AuthenticationResult's from different providers in the same page


I'm integrating OpenID to my existing application with LiveID and Google providers. On my login page, in addition to the original login fields I have added 'Log in with Google' and 'Log in with Microsoft' buttons.

I can successfully read the AuthenticationResult data for both providers above, but am accomplishing this in the following manner...

For the new login buttons I crafted a return URL to differentiate them on the user's return:

Protected Sub btn_google_Click(sender As Object, e As EventArgs) Handles btn_google.Click
    Dim client As New GoogleOpenIdClient
    Dim u As New System.Uri("http://www.mytest.com/login.aspx?action=signin&provider=google")
    client.RequestAuthentication(New HttpContextWrapper(HttpContext.Current), u)
End Sub

Protected Sub btn_live_Click(sender As Object, e As EventArgs) Handles btn_live.Click
    Dim client As New MicrosoftClient("xyz", "12345")
    Dim u As New System.Uri("http://www.mytest.com/login.aspx?action=signin&provider=microsoft")
    client.RequestAuthentication(New HttpContextWrapper(HttpContext.Current), u)
End Sub

So when the user gets redirected back to login.aspx, I then have the following checks to process the login functionality:

If Not Page.IsPostBack Then
    If Request.QueryString("action") IsNot Nothing AndAlso Request.QueryString("action").Trim = "signin" Then
        If Request.QueryString("provider") IsNot Nothing AndAlso Request.QueryString("provider").Trim <> String.Empty Then
            Select Case Request.QueryString("provider").Trim
                Case "microsoft"
                    Dim client As New MicrosoftClient("xyz", "12345")
                    Dim u As New System.Uri("http://www.mytest.com/loginlive.aspx?action=signin&provider=microsoft")
                    Dim result As DotNetOpenAuth.AspNet.AuthenticationResult = client.VerifyAuthentication(New HttpContextWrapper(HttpContext.Current), u)
                    ' remainder of logic removed
                    ' ...
                Case "google"
                    Dim client As New GoogleOpenIdClient
                    Dim result As DotNetOpenAuth.AspNet.AuthenticationResult = client.VerifyAuthentication(New HttpContextWrapper(HttpContext.Current))
                    ' remainder of logic removed
                    ' ...
            End Select
        End
    End
End If

My main question here is, is this a good way to process AuthenticationResults? Or, is there a better/more secure/more clever way to accomplish the same?


Solution

  • Better way would be to use Abstract Factory pattern in combination with Command Pattern. Which can reduce the hard coding and also have the code loosely coupled, so you can extend the functionality in future for each of the authentication provider. Find the snippet of each section of the code below

    Abstract Class for "BaseAuthentication Provider"

    public abstract class BaseAuthenticationProvider
    {
        //abstract Methods that need to be invoked from the concrete class, this need to be decided based on the functionality you need to achieve. This function would be invoked using the command pattern.
        // AuthorizeUser() : this method would be invoked to authorize the user from the provider
    
       //AuthenticateUser() : this method would be invoked once the user is redirected from the provider site.
    
        //abstract Properties that will hold the base information for the authentication provider, this need to be decided based on the functionality you need to achieve
        //CustomerSecret
        //CustomerConsumerKey
    }
    

    Use the following code snippet to implement concrete class for the Gooogle, Yahoo, Microsoft etc.

    public class GoogleAuthentication : BaseAuthenticationProvider
    {
         public GoogleAuthentication()
         {
              //initialization
         }
    
         public void AuthorizeUser()
         {
              //code
         }
    
         public string CustomerSecret()
         {
              //code
         }
    
         public string CustomerConsumerKey()
         {
              //code
         }
    }
    

    Factory class to create the concrete object, to prevent from creating instance of this factory class implement a private constructor.

    public class AuthenticationProviderFactory
    {
         private AuthenticationProviderFactory()
         {
         }
    
         public static BaseAuthenticationProvider GetInstance(string Domain)
         {
              switch (Domain)
              {
                   case "google":
                        return new GoogleAuthentication();
                   case "yahoo":
                        return new YahooAuthentication();
               }
          }
     }
    

    Login.aspx : have buttons for each of the authentication provider, set the value for "CommandName" for each of the button and link all the buttons to the same event handler

    for e.g. btn_google.CommandName = "google"

    Protected Sub AuthenticationProvider_Click(sender As Object, e As EventArgs) Handles btn_google.Click, btn_yahoo.Click
        AuthenticationProviderFactory.GetInstance(((Button)sender).CommandName).AuthorizeUser();
    End Sub
    

    Respective AuthorizeUser method would call the respective provider site for authentication. When provider redirects the user to the return URL, apply the same pattern on the Page_Load event and call the Autheticate Method from the abstract class.