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?
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.