Skip to main content

Implementing OAuth2 consumer in C#

Recently I worked on a project that involved integrating with social networking and jobs websites like Elance. These days almost all major services that allow applications to access users' data, perform authorization/authentication using OAuth1.0 or OAuth2. OAuth2 compared to OAuth1 is very easy to implement. OAuth1 involves generating nonce,timestamp,signaturebase and signing the request with any algorithm like HMAC-SHA1 and appending data to query string of URL and passing in Authorization header of HTTP. OAuth2 removed all these requirements. Following is OAuth2 process in a nutshell
1) Redirect user to Authorization url passing client_id and redirect_url in query parameters
2) If user authenticates successfully through service provider it will redirect user to the redirect_url passed with authorization access code in query parameters.
3) After getting authorization access code you exchange this to receive access token. You make a POST HTTP request to a URL passing client_id , client_secret and authorization code.
4) After access token is received you can call API of service provider using that access token.
In this blog post I will implement OAuth2 for Elance in a desktop C# app. Elance provide you API Key (client_id) and secret code (client_secret) when you apply for new API keys.

Lets start by creating a class named OAuth2 that will hold variables that is common to all OAuth2 consumers.

namespace Usmani.OAuth2
{
    public abstract class OAuth2
    {
        public string AuthorizationUrl { get; protected set; }
        public string AccessTokenUrl { get; protected set; }
        public string RedirectUrl { get; protected set; }
        public string AuthorizationCode { get; protected set; }
        public string ClientId { get; private set; }
        public string ClientSecret { get; private set; }
        public string AccessToken { get; protected set; }
        public string RefreshToken { get; protected set; }

        protected string AuthorizationCodeParameter { get; set; }
        protected string ClientIdParameter { get; set; }
        protected string ClientSecretParameter { get; set; }

        public OAuth2(string clientId, string clientCode,string rUrl)
        {
            this.ClientId = clientId;
            this.ClientSecret = clientCode;
            this.RedirectUrl=rUrl;
        }

        protected string GetFullAuthorizationUrl(string queryStringToAppend)
        {
            return AuthorizationUrl + "?" + ClientIdParameter + "=" + ClientId + "&" + queryStringToAppend;
        }

        protected void SetAuthorizationCode(string queryStringPart)
        {
            NameValueCollection nvc = HttpUtility.ParseQueryString(queryStringPart);
            this.AuthorizationCode = nvc[AuthorizationCodeParameter];
        }

        protected string GetAccessTokenResponse(string queryStringToAppend, string postDataToAppend)
        {
            string fullUrl = AccessTokenUrl + "?" + queryStringToAppend;
            string postData = AuthorizationCodeParameter+"=" + HttpUtility.UrlEncode(AuthorizationCode);
            postData += "&" + ClientIdParameter + "=" + HttpUtility.UrlEncode(ClientId);
            postData += "&" + ClientSecretParameter + "=" + HttpUtility.UrlEncode(ClientSecret);
            postData += "&" + postDataToAppend;

            return HttpClient.postFormData(fullUrl,postData);
        }

        public abstract void GetAccessToken();
        public abstract void UserRedirectedAfterAuthorization(string queryPart);
    }
}

Now we will create class specific to Elance OAuth2 which will inherit from OAuth2.

namespace Usmani.OAuth2
{
    public class ElanceOAuth2 : OAuth2
    {
        public ElanceOAuth2(string apiKey,string consumerSecret,string redUrl)
            : base(apiKey,consumerSecret,redUrl)
        {
            this.AuthorizationUrl = "https://api.elance.com/api2/oauth/authorize";
            this.AccessTokenUrl = "https://api.elance.com/api2/oauth/token";
            this.RedirectUrl = "http://yourredirecturl";
            this.ClientIdParameter = "client_id";
            this.ClientSecretParameter = "client_secret";
            this.AuthorizationCodeParameter = "code";
        }

        public override void GetAccessToken()
        {
            string response = GetAccessTokenResponse(string.Empty, "grant_type=authorization_code");
            //Elance return response in Json format.I am using Newtonsoft Json library here
            JToken jobj = JToken.Parse(response);
            jobj = jobj.Value<JObject>("data");
            AccessToken = jobj.Value<string>("access_token");
            RefreshToken = jobj.Value<string>("refresh_token");
        }

        public override void UserRedirectedAfterAuthorization(string queryPart)
        {
            SetAuthorizationCode(queryPart);
        }
    }
}

GUI Part
We can perform following part in our GUI to allow user to login to elance and perform authorization and authentication.

ElanceOAuth2 elApi=new ElanceOAuth2("YOUR_API_KEY","CONSUMER_SECRET","REDIRECT_URL");
WebBrowser brw=new WebBrowser();
this.Controls.Add(brw);
brw.Navigated+=new WebBrowserNavigatedEventHandler(browser_Navigated);

brw.Navigate(elApi.GetFullAuthorizationUrl("scope=basicInfo&response_type=code"));

public void browser_Navigated(object sender, WebBrowserNavigatedEventArgs e)
{
    if (e.Url.AbsoluteUri.StartsWith(el.RedirectUrl))
    {
        elApi.UserRedirectedAfterAuthorization(e.Url.Query);
    }
}

Comments

  1. Whats up very cool blog!! Man .. Beautiful .. Superb ..
    I'll bookmark yiur blog and take the feeds additionally?
    I'm satisfied to find a lot of helpful info right here within the post, we'd like develop more techniques on this regard, thank
    you for sharing. . . . . .

    ReplyDelete

Post a Comment

Share your wisdom

Popular posts from this blog

Decoding JPEG image file using libavcodec

I got a chance to work on a video encoding application that decodes series of jpeg files and convert them into ogg theora video file. I used the infamous libavcodec library that is used in FFMPEG . I decided to write blog posts explaining how I decode jpeg images and convert them into ogg video file. This is the first part and in this I will explain how to decode jpeg images using libavcodec. To learn how to write decoded images as a ogg video file please read http://random-stuff-mine.blogspot.com/2017/07/encoding-raw-images-to-ogg-theora-video.html Before reading this blog post you must be aware of using and setting up libavcodec. I highly recommend this tutorial to get basics of using libavcodec http://www.ffmpeg.org/doxygen/0.6/api-example_8c-source.html Allocating input format context We will first allocate input format for reading the file. We will use avformat_open_input function that will allocate AVFormatContext structure passed to it , the function detects input typ...

Multithreaded C# TCP server to handle multiple clients

I decided to write a minimal multithreaded TCP based server as a blog post. Following class can serve as a skeleton for any small or large scale multithreaded TCP socket server. It do not contain much error handling , it is only to give an idea that how multithreaded server works and how it can process multiple clients using threading. using System; using System.Text; using System.Net; using System.Net.Sockets; using System.Threading; using System.Collections.Generic; namespace RandomStuffMine { public class MTServer {     public int Port{get;set;}     public Socket ServerSocket{get;set;}     private List<Client> Clients=new List<Client>();     private bool runServer=true;     public MTServer(int port)     {         Port=port;         ServerSocket = new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp);     }   ...

CryptographicException: An error occurred while trying to encrypt the provided data. Refer to the inner exception for more information

I created a new Blazor Server app in Visual Studio 2019 and tried to run it. But I was getting this error CryptographicException: An error occurred while trying to encrypt the provided data. Refer to the inner exception for more information. I couldn't find any reason or solution to this problem. I tried creating the project multiple times but same error. I created a new .Net Core Web App and added a new razor component and included that component in a razor page (cshtml file) like this @(await Html.RenderComponentAsync<GeofenceWork>(RenderMode.ServerPrerendered)) and <component type="typeof(GeofenceWork)" render-mode="serverprerendered" /> As soon as I navigate to this page that has component added I got the same error: CryptographicException: An error occurred while trying to encrypt the provided data. Refer to the inner exception for more information. This was very frustrating. After hours of trying and searching I figured out the solution.  ...