j9essg
Last Updated: February 25, 2016
·
15.94K
· analogj
57a90b685ef5eb6b17edf33fa91c4ea8

DotNetOpenAuth.AspNet Twitter,Facebook,Google,Microsoft,LinkedIn Authentication

UPDATE - The fix I used to get the LinkedIn client working can be found here

I've been looking at the AspNet module (written by Microsoft developers) that comes with the DotNetOpenAuth library for a while. The AspNet module is referenced in one of the built in Membership providers included in Visual Studio 2012 and can be used with MVC4. However my application was written in MVC3 and for the life of me I couldn't find many examples of how to use the AspNet module for the popular OAuth and OpenID service providers: Google, Facebook, Twitter, LinkedIn and Microsoft. After some trial and error, I was able to get all but LinkedIn working using the following code.

I first created an AuthenticationHelper class that I can reuse at anytime to retrieve the AspNet Client that I require. As you can see, all the class is doing is creating the specified client with the OAuth/OpenId Consumer Key and Consumer Secret that you will be given by each service provider.

public static class AuthenticationHelper
{

    public static FacebookClient AuthFacebookClient()
    {
        String facebookKey =
        ConfigurationManager.AppSettings["Auth_FacebookClient_Key"];
        String facebookSecret = 
        ConfigurationManager.AppSettings["Auth_FacebookClient_Secret"];
        return new FacebookClient(facebookKey, facebookSecret);
    }



    public static MicrosoftClient AuthMicrosoftClient()
    {
        String microsoftKey = 
        ConfigurationManager.AppSettings["Auth_MicrosoftClient_Key"];
        String microsoftSecret = 
        ConfigurationManager.AppSettings["Auth_MicrosoftClient_Secret"];
        return new MicrosoftClient(microsoftKey, microsoftSecret);
    }

    public static LinkedInClient AuthLinkedInClient()
    {
        String linkedinKey = 
        ConfigurationManager.AppSettings["Auth_LinkedInClient_Key"];
        String linkedinSecret = 
        ConfigurationManager.AppSettings["Auth_LinkedInClient_Secret"];
        return new LinkedInClient(linkedinKey, linkedinSecret);
    }

    public static TwitterClient AuthTwitterClient()
    {
        String twitterKey = 
        ConfigurationManager.AppSettings["Auth_TwitterClient_Key"];
        String twitterSecret = 
        ConfigurationManager.AppSettings["Auth_TwitterClient_Secret"];
        return new TwitterClient(twitterKey, twitterSecret);
    }

}

Then in my MVC3 Login Controller I created a new action for Authentication like the following snippet. Keep in mind that error handling is not included. You will notice that the Authentication action method seems to create a url and store it in the authString variable and also calls the RequestAuthentication method. The authString is actually the calculated return url that the authentication provider will redirect the user to after they have validated the user. My format was the following:

http://www.example.com/Login/Authentication/{ServiceProvider}
Where ServiceProvider was Google,Facebook, LinkedIn, Microsoft, etc.

[HttpPost]
 public ActionResult Authentication(Constants.AuthenticationType authType)
 {
     String authString = Url.Action("AuthenticationResult","Account", new {authType = authType.ToString()},"http");
     Uri authUrl = new Uri(authString);

     RequestAuthentication(authType, authUrl);

     //should never reach this point
     return null;
 }

One non standard thing to keep in mind with DotNetOpenAuth is that when the RequestAuthentication method is called on each client, the application will redirect the user immediately, and no code should be expected to be executed after.

 /// <summary>
/// Requests the authentication from various openid and oauth services. Should redirect the user within th DotNetOpenAuth Dll.
/// </summary>
/// <param name="authType">Type of the auth.</param>
/// <param name="authUrl">The redirect url, used after the service has authenticated the user.</param>
private void RequestAuthentication(Constants.AuthenticationType authType, Uri authUrl)
{

    if (authType == Constants.AuthenticationType.LinkedIn)
    {
        var auth = AuthenticationHelper.AuthLinkedInClient();
        auth.RequestAuthentication(HttpContext, authUrl);
    }
    else if (authType == Constants.AuthenticationType.Facebook)
    {
        var auth = AuthenticationHelper.AuthFacebookClient();
        auth.RequestAuthentication(HttpContext, authUrl);
    }
    else if (authType == Constants.AuthenticationType.Google)
    {
        var auth = new DotNetOpenAuth.AspNet.Clients.GoogleOpenIdClient();
        auth.RequestAuthentication(HttpContext, authUrl);
    }
    else if (authType == Constants.AuthenticationType.Microsoft)
    {
        var auth = AuthenticationHelper.AuthMicrosoftClient();
        auth.RequestAuthentication(HttpContext, authUrl);
    }
    else if (authType == Constants.AuthenticationType.Twitter)
    {
        var auth = AuthenticationHelper.AuthTwitterClient();
        auth.RequestAuthentication(HttpContext, authUrl);
    }
    else if (authType == Constants.AuthenticationType.Yahoo)
    {
        var auth = new DotNetOpenAuth.AspNet.Clients.YahooOpenIdClient();
        auth.RequestAuthentication(HttpContext, authUrl);
    }
    else
    {
        var auth = new DotNetOpenAuth.AspNet.Clients.OpenIdClient("OpenId", openid_identifier);
        auth.RequestAuthentication(HttpContext, authUrl);
    }
}

If you return back up to the Authenticate action method, you will see that the return url has an action is named AuthenticateResult, which looks like the following snippet. You will note that after the VerifyAuthentication method returns the res variable will be populated and you can check if validation was successful. At that point you can save the Authentication type as well as the ProviderUserId, create a membership cookie and then redirect the user to an authenticated page.

[HttpGet]
public ActionResult AuthenticationResult(Constants.AuthenticationType authType)
{
    DotNetOpenAuth.AspNet.AuthenticationResult res;

    String authString = Url.Action("AuthenticationResult", "Account", new { authType = authType.ToString() }, "http");
    Uri authUrl = new Uri(authString);

    VerifyAuthentication(authType, out res, authUrl);

    if (!res.IsSuccessful)
    {
        throw new Exception("Authentication Provider Error: Sorry, it seems that an error occured while validating your Authentication Provider's response.");
                    }
    else
    {
            //Store res.ProviderUserId and Authentication Type in the database. Create a membership  cookie and then redirect the user here.
    }
}

/// <summary>
/// Verifies the authentication from the various OpenId and OAuth services, returns generic Authetnication Result in res variable.
/// </summary>
/// <param name="authType">Type of the auth.</param>
/// <param name="res">The res.</param>
private void VerifyAuthentication(Constants.AuthenticationType authType, out DotNetOpenAuth.AspNet.AuthenticationResult res,  Uri authUrl)
{
    if (authType == Constants.AuthenticationType.LinkedIn)
    {
        var validate = AuthenticationHelper.AuthLinkedInClient();
        res = validate.VerifyAuthentication(HttpContext);
    }
    else if (authType == Constants.AuthenticationType.Facebook)
    {
        var validate = AuthenticationHelper.AuthFacebookClient();
        res = validate.VerifyAuthentication(HttpContext, authUrl );
    }
    else if (authType == Constants.AuthenticationType.Google)
    {
        var validate = new DotNetOpenAuth.AspNet.Clients.GoogleOpenIdClient();
        res = validate.VerifyAuthentication(HttpContext);


    }
    else if (authType == Constants.AuthenticationType.Microsoft)
    {
        var validate = AuthenticationHelper.AuthMicrosoftClient();
        res = validate.VerifyAuthentication(HttpContext);
    }
    else if (authType == Constants.AuthenticationType.Twitter)
    {
        var validate = AuthenticationHelper.AuthTwitterClient();
        res = validate.VerifyAuthentication(HttpContext);
    }
    else if (authType == Constants.AuthenticationType.Yahoo)
    {
        var validate = new DotNetOpenAuth.AspNet.Clients.YahooOpenIdClient();
        res = validate.VerifyAuthentication(HttpContext);
    }
    else
    {
        var validate = new DotNetOpenAuth.AspNet.Clients.OpenIdClient("OpenId", "");
        res = validate.VerifyAuthentication(HttpContext);

    }
}

And thats it, at this point you should have a working authentication controller with support for Facebook, Google, Twitter, Microsoft and any other OpenId provider.

Say Thanks
Respond

8 Responses
Add your response

1585

Hi Jason!
Your solution is very clear and elegant! :)
Only a question: how can i have email information in Facebook result?
I searched by google a solution to apply to your code... but i have not found it... :(
Can you help me? :)
Thank you in advance for any suggestion!! :DDD

over 1 year ago ·
1875
57a90b685ef5eb6b17edf33fa91c4ea8

Hey @rmacone , I noticed that almost none of the built in Asp.Net clients included email addresses. I created a pull request to add facebook's email scope (which you can find (here)[https://github.com/DotNetOpenAuth/DotNetOpenAuth/pull/208#issuecomment-10701452]) I'll probably create a new coderwall post soon with the custom clients I created to retrieve email address from the other services.

over 1 year ago ·
1885
Default profile 1 normal

Thank you very much!!! :D
Reading you post, I wrote my FacebookClient adding the email parameter... and it's running... :D

over 1 year ago ·
2884
Default profile 5 normal

Dear god reading this is impossible. Dude make your code available on github or something because this is just unbearable.

Very pretty, but very impractical.

over 1 year ago ·
2885
57a90b685ef5eb6b17edf33fa91c4ea8

Hey @fabriciomrtnz this code has already been integrated into the ASP.Net client. Just pull down the latest version of dotnetopenauth from nuget or github.The coderwall post is more of a how to guide for creating customized ASP.Net clients

over 1 year ago ·
6479

url = "https://api.twitter.com/1.1/account/verify_credentials.json";
xml = oAuth.oAuthWebRequest(oAuthTwitter.Method.GET, url, String.Empty);

The remote server returned an error: (401) Bad Request.

over 1 year ago ·
13337
5080a36f7400d8c5cca9d9fbd3ae46ab normal

could you please attach the sample project ?

over 1 year ago ·
18043

public ActionResult Authentication(Constants.AuthenticationType authType) showing me error.How to resolve it.AuthenticationType does not exist in Constants.

over 1 year ago ·