Processing AuthenticationResult's from different providers in the same page
Asked Answered
B

1

6

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?

Blower answered 23/4, 2013 at 10:33 Comment(0)
T
1

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.

Thiamine answered 3/6, 2013 at 15:19 Comment(5)
Thanks for that input. I appreciate your points, but have some reservations, namely... I hoped to be able to log in a user automatically to my system if they were already logged in via Google with a link like this www.mydomain.com/autologin.aspx?provider=Google to save time hitting extra buttons on the normal login page. Also because string values like ConsumerKey are only used once, moving them into a class would need a recompile every time they were updated. Is that good practice? Sorry my coding skills are only average so I might have missed some key points with your approach.Blower
1. direct login : you can still achieve this by the pattern i have said above. Using the same methodologies used in the login button.Use the same on the page load of the autologin.aspx 2. Moving Consumerkey into the code : the consumer key can be kept in the config or resource file and you can refer those directly into your code. I hope this answers your query.Thiamine
Okay great. The bit I'm struggling to see is why your code is better than my approach. Again blame my experience, just some pointers would be great!Blower
your code is tightly coupled which prevents any extension of the functionality for the authentication provider. To add new authentication provider you will need to create button and write code specific code for that provider. My code is loosely coupled which gives u the ability to extend any of the provider without making change to the existing behavior. Even you can add new provider just by creating a new class for the provider and adding the new switch for the provider in "BaseAuthenticationProvider.GetInstance" method.Thiamine
Continued..... The same can be achieved using the web.config. which determines authentication provider that need to be returned by the BaseAuthenticationProvider.GetInstance method.Thiamine

© 2022 - 2024 — McMap. All rights reserved.