Asp.Net MVC oauth LinkedIn Email problem

Posted On // Leave a Comment


By default the LinkedinClient with oAuth in DotNetOpenAuth.AspNet.Clients does not retrieve the email of the linkedin logged user.
According to the linkedin specifications you need to provide r_emailaddress in the scope field of the request token. You will also need to ensure that in your developer linkedin account you have enabled the r_emailaddress field under OAuth User Agreement. Screenshot from linkedin below


The default Linkedin client does not follow that BUT the good thing is oauth lets you implement your own client with ease. Here is my LinkedinCustomClient class
------------------------------------------------------------------------------------------------------------
using System;
using System.Collections.Generic;
using System.Diagnostics.CodeAnalysis;
using System.IO;
using System.Linq;
using System.Net;
using System.Web;
using System.Xml;
using System.Xml.Linq;

using DotNetOpenAuth.AspNet;
using DotNetOpenAuth.AspNet.Clients;
using DotNetOpenAuth.Messaging;
using DotNetOpenAuth.OAuth;
using DotNetOpenAuth.OAuth.ChannelElements;
using DotNetOpenAuth.OAuth.Messages;

namespace Project
{
    public class LinkedInCustomClient : OAuthClient
    {
        private static XDocument LoadXDocumentFromStream(Stream stream)
        {
            var settings = new XmlReaderSettings
            {
                MaxCharactersInDocument = 65536L
            };
            return XDocument.Load(XmlReader.Create(stream, settings));
        }

        /// Describes the OAuth service provider endpoints for LinkedIn. BELOW NOTE RequestTokenEndpoint r_emailaddress

        private static readonly ServiceProviderDescription LinkedInServiceDescription =
                new ServiceProviderDescription
                {
                    AccessTokenEndpoint =
                            new MessageReceivingEndpoint("https://api.linkedin.com/uas/oauth/accessToken",
                            HttpDeliveryMethods.PostRequest),
                    RequestTokenEndpoint = new MessageReceivingEndpoint("https://api.linkedin.com/uas/oauth/requestToken?scope=r_fullprofile+r_emailaddress", HttpDeliveryMethods.PostRequest),

                    UserAuthorizationEndpoint =
                            new MessageReceivingEndpoint("https://www.linkedin.com/uas/oauth/authorize",
                            HttpDeliveryMethods.PostRequest),
                    TamperProtectionElements =
                            new ITamperProtectionChannelBindingElement[] { new HmacSha1SigningBindingElement() },
                    ProtocolVersion = ProtocolVersion.V10a
                };

        public LinkedInCustomClient(string consumerKey, string consumerSecret) :
            base("linkedIn", LinkedInServiceDescription, consumerKey, consumerSecret) { }

        /// Check if authentication succeeded after user is redirected back from the service provider.
        /// The response token returned from service provider authentication result. 
        [SuppressMessage("Microsoft.Design", "CA1031:DoNotCatchGeneralExceptionTypes",
            Justification = "We don't care if the request fails.")]
        protected override AuthenticationResult VerifyAuthenticationCore(AuthorizedTokenResponse response)
        {
            // See here for Field Selectors API http://developer.linkedin.com/docs/DOC-1014
            //const string profileRequestUrl = "https://api.linkedin.com/v1/people/~:(id,first-name,last-name,headline,industry,summary)";
            const string profileRequestUrl = "https://api.linkedin.com/v1/people/~:(id,first-name,last-name,email-address)"; // important line- this is where we are getting values out from response
 

            string accessToken = response.AccessToken;

            var profileEndpoint =
                new MessageReceivingEndpoint(profileRequestUrl, HttpDeliveryMethods.GetRequest);
            HttpWebRequest request =
                WebWorker.PrepareAuthorizedRequest(profileEndpoint, accessToken);

            try
            {
                using (WebResponse profileResponse = request.GetResponse())
                {
                    using (Stream responseStream = profileResponse.GetResponseStream())
                    {
                        XDocument document = LoadXDocumentFromStream(responseStream);
                        string userId = document.Root.Element("id").Value;

                        string firstName = document.Root.Element("first-name").Value;
                        string lastName = document.Root.Element("last-name").Value;
                        string userName = document.Root.Element("email-address").Value;
                        // string userName = firstName + " " + lastName;

                        var extraData = new Dictionary
                                            {
                                                {"accesstoken", accessToken}, 
                                                {"name", firstName + " " + lastName}
                                            };

                        return new AuthenticationResult(
                            isSuccessful: true,
                            provider: ProviderName,
                            providerUserId: userId,
                            userName: userName,
                            extraData: extraData);
                    }
                }
            }
            catch (Exception exception)
            {
                return new AuthenticationResult(exception);
            }
        }
    }
}

 OAuthWebSecurity.RegisterClient(new LinkedInCustomClient("YourKey", "YourSecret"), "LinkedIn", null); 
Rest logic is up to you that how you handle the values. For me after the callback
public ActionResult ExternalLoginCallback(string returnUrl)
{
 AuthenticationResult result = OAuthWebSecurity.VerifyAuthentication(Url.Action("ExternalLoginCallback", new { ReturnUrl = returnUrl }));
//result.UserName contains the email address
//return view code here//
}

0 comments:

Post a Comment