OAuth and .Net

Is it hard to implement oauth authentication in wcf? When you try to search the internet for the possible solutions, you will normally find a few solutions like: DotNetOpenOauth or DevDefinedOauth, the problem is the solutions provided are so complicated that you do not know where to start, there is no document of how to use these solutions, on the other hand it severely obfuscates the essence of the solution of this issue. Here I am going to demonstrate how to tackle this technique issue step by step in a complete fat free way using Eran Sandler basic c# class.

One of the confusing point of this issue is it has two faces when you are talking about Oauth, as a service provider and a consumer of services of other parties.

Another confusing point is there are two types of Oauth: two legged and three legged. even though 3 legged is slightly more complicated than 2 legged, you are about to see that they do not pose to be too different from authentication point of view.

What I am going to do is to build a wcf service that is using oauth to authenticate callers and a client to consume other oauth authentication web services, along the way I will explain what oauth is all about.

The examples are given using the two legged oauth authentication, but what is important here is to understand the role it played in web services, and personally I am fine with the web services in my project are only using two legged oauth, a full blown 3 legged service is desirable when you have large audiences like google or facebook.

Here are a few important concepts for oauth:

You can find the more info about oauth two legged or three legged from here

Actually the concept underneath oauth is quiet simple, it might be an overstatement, but it is my understanding of it:

The caller signs (hash) some information (it is called base string) using consumer secret and server signs (hash) the same information using the same consumer secret, and compare these two hashes.

Normally the hash method oauth supports are HMAC-SHA1, RSA-SHA1, and PLAINTEXT, I do not know what the point of plain text is if we want to implement oauth, hmac-sha1 is a symmetric hash, so both server and client sides must know hash key that is consumer secret. RSA-SHA1 is an asymmetric hash, so the hash involves two keys, one is public and the other is private, and normally that is where a certificate is wanted, this is beyond the purpose of this article.

For authentication to succeed we need same key and same base string on both sides, so what is in a base string? Normally it includes your url including all oauth parameters: a nonce, a time stamp, and consumer key together with your normal GET or POST parameters, do not confuse consumer key with hash key, actually consumer key is part of base string therefore public, hash key is consumer secret which is used for signing, and not transferred across the wire.

Ok enough for abstract theory, let us get down to business to see how it is actually implemented.

Service Example 1:

Background: using wcf restful of .net 4.0, authentication done at the service method level through code

Hosting Service.svc file

<%@ ServiceHost Language="C#" Debug="true" Service="Service" CodeBehind="~/App_Code/Service.cs" %>

And this is actual implementation, Service.cs

[ServiceContract]
public class Service 
{
    private static bool Authenticate(IncomingWebRequestContext context)
    {
        bool Authenticated = false;

        string normalizedUrl;
        string normalizedRequestParameters;

        //context.Headers
        NameValueCollection pa = context.UriTemplateMatch.QueryParameters;

        if (pa != null && pa["oauth_consumer_key"] != null)
        {
            // to get uri without oauth parameters
            string uri = context.UriTemplateMatch.RequestUri.OriginalString.Replace
                (context.UriTemplateMatch.RequestUri.Query,"");

            string consumersecret = "secret";

            OAuthBase oauth = new OAuthBase();
            
            string hash = oauth.GenerateSignature(
                new Uri(uri),
                pa["oauth_consumer_key"],
                consumersecret,
                null, // totken
                null, //token secret
                "GET",
                pa["oauth_timestamp"],
                pa["oauth_nonce"],
                out normalizedUrl,
                out normalizedRequestParameters
                );

            Authenticated = pa["oauth_signature"] == hash;
        }

        return Authenticated;
    }

    [WebInvoke(Method="GET", 
        ResponseFormat = WebMessageFormat.Xml,
        BodyStyle = WebMessageBodyStyle.Bare,
        UriTemplate="user/{UserId}")]
	public User  GetUser(string  UserId)
	{
        if (Authenticate(WebOperationContext.Current.IncomingRequest))
        {
            return new User
            {
                UserName = "UserName",
                UserPassword = "UserPassword"
            };
        }
        else
        {
            WebOperationContext.Current.OutgoingResponse.StatusCode = HttpStatusCode.Unauthorized;
            
            throw new WebFaultException<string>("Unauthorized Request.", HttpStatusCode.Unauthorized);

        }
	}

}

And User class definition

[DataContract]
public class User
{
    public User()
    {

    }
    [DataMember]
    public string UserName {get;set;}
    [DataMember]
    public string UserPassword {get;set;}
}

And web.config for this wcf restful service:

<system.serviceModel>
     
 <services>
     <service name="Service">
         <endpoint address="" binding="webHttpBinding" contract="Service"
                   behaviorConfiguration="restendpointbehavior">
         </endpoint>
     </service>

 
     </services>
  <behaviors>
   <serviceBehaviors>
    <behavior name="">
     <serviceMetadata httpGetEnabled="true" />
     <serviceDebug includeExceptionDetailInFaults="false" />
    </behavior>

    </serviceBehaviors>
      <endpointBehaviors>
          <behavior name="restendpointbehavior">
              <webHttp/>
          </behavior>
      </endpointBehaviors>
  </behaviors>
  <serviceHostingEnvironment multipleSiteBindingsEnabled="true" />
 </system.serviceModel>

A couple of points:

1. It needs and only needs OAuthBase.cs from here

2. This is standard restful wcf web service, valid uri without oauth parameters will be like :

http://localhost:49262/TestProject/Service.svc/user/123

3. Expecting incoming calls like :

http://localhost:49262/TestProject/Service.svc/user/123?oauth_consumer_key=key&oauth_nonce=10a33ed37b549301644b23b93fc1f1c5&oauth_signature=cUobFDxVB5wjPe9X2XICJ6awmnE%3D&oauth_signature_method=HMAC-SHA1&oauth_timestamp=1289976718&oauth_version=1.0

There are two ways to attach oauth parameters, one is through headers, another is through query string, both are valid, so I choose to attach oauth parameters in query string. There is not any problem to retrieve them from IncomingWebRequestContext.Headers if those oauth parameters are in headers

4. I add the authentication check in just service method, all it needs to do is double check the hash with incoming oauth_signature to ensure they are the same.

5. consumer_key is ‘key’, it can be any value and the secret is hard coded, I am sure you know how to get secret from a matching key from a datastore like a configuration file in a real situation

6. Token and token secret are actually null, they are only used when it is 3 legged

7. The consumer key and secret authentication are safe without https, but whole request and response content are not protected if not using https.

Service Example 2:

Background: using wcf restful of .net 4.0, compared with Service Example 1 authentication done by ServiceAuthorizationManager,

The benefit of this example is you only need to implement authentication at the service level, you do not have to do it in every service call as Example 1 does, and this is actually preferred way to implement wcf authentication, the reason I list Example 1 is just to exclude all irrelevant stuff and expose the solution in most straight way, the point is there tends to be a danger about beautifully strongly typed languages like asp.net they are so easy to get bloated that the real problem about an issue is actually hard to come by.

Hosting Service2.svc file

<%@ ServiceHost Language="C#" Debug="true" Service="Service2" CodeBehind="~/App_Code/Service2.cs" %>

And this is actual implementation, Service2.cs

[ServiceContract]
public class Service2
{    
    [WebInvoke(Method = "GET",
        ResponseFormat = WebMessageFormat.Xml,
        BodyStyle = WebMessageBodyStyle.Bare,
        UriTemplate = "user/{UserId}")]
    public User GetUser(string UserId)
    {
            return new User
            {
                UserName = "UserName",
                UserPassword = "UserPassword"
            };
     
    }
    
}

And web.config for this wcf restful service:

<system.serviceModel>
     
 <services>

     <service name="Service2" behaviorConfiguration="Oauth">
         <endpoint address="" binding="webHttpBinding" contract="Service2"
                   behaviorConfiguration="restendpointbehavior">
         </endpoint>
     </service>
     
     </services>
  <behaviors>
   <serviceBehaviors>
       <behavior name="Oauth">
           <serviceMetadata httpGetEnabled="true" />
           <serviceDebug includeExceptionDetailInFaults="false" />
           <serviceAuthorization serviceAuthorizationManagerType="OAuth.OAuthAuthorizationManager,App_Code"/>
        </behavior>
       
    </serviceBehaviors>
      <endpointBehaviors>
          <behavior name="restendpointbehavior">
              <webHttp/>
          </behavior>
      </endpointBehaviors>
  </behaviors>
  <serviceHostingEnvironment multipleSiteBindingsEnabled="true" />
 </system.serviceModel>

And the implementation of ServiceAuthorizationManager

namespace OAuth
{
    /// <summary>
    /// Summary description for OAuthAuthorizationManager
    /// </summary>
    public class OAuthAuthorizationManager : ServiceAuthorizationManager
    {
        protected override bool CheckAccessCore(OperationContext operationContext)
        {
            bool Authenticated = false;

            string normalizedUrl;
            string normalizedRequestParameters;

            base.CheckAccessCore(operationContext);

            // to get the httpmethod
            
            HttpRequestMessageProperty requestProperty = (HttpRequestMessageProperty)
(operationContext.RequestContext.RequestMessage). 
Properties[HttpRequestMessageProperty.Name];

            string httpmethod = requestProperty.Method;

            // HttpContext.Current is null, so forget about it 
            // HttpContext context = HttpContext.Current; 

            NameValueCollection pa = HttpUtility.ParseQueryString(operationContext.
  IncomingMessageProperties .Via.Query);

            if (pa != null && pa["oauth_consumer_key"] != null)
            {
                // to get uri without oauth parameters
                string uri = operationContext.IncomingMessageProperties
                .Via.OriginalString.Replace
                   (operationContext.IncomingMessageProperties
                .Via.Query, "");

                string consumersecret = "secret";

                OAuthBase oauth = new OAuthBase();

                string hash = oauth.GenerateSignature(
                    new Uri(uri),
                    pa["oauth_consumer_key"],
                    consumersecret,
                    null, // totken
                    null, //token secret
                    httpmethod,
                    pa["oauth_timestamp"],
                    pa["oauth_nonce"],
                    out normalizedUrl,
                    out normalizedRequestParameters
                    );

                Authenticated = pa["oauth_signature"] == hash;

            }

            return Authenticated;

        }
    }
}

A couple of points:

1. The authentication is actually done in OAuthAuthorizationManager

2. The way to apply a customized authorization manager is adding

<serviceAuthorization serviceAuthorizationManagerType="OAuth.OAuthAuthorizationManager,App_Code"/>

to servicebehaviour configuration node, ‘App_Code’ can be replace by assembly name if you have one, but my test project is an asp.net web site, so there is no explicit assembly name for it, while this can not be omitted, otherwise you will get error message as:

Could not load type xxx from assembly System.ServiceModel

3. In CheckAccessCore do not use HttpContext.Current as it is null instead use operationContext, everything is easy to be retrieved except httpmethod.

4. Everything else basically the same as Example 1

It won’t be complete if an asp.net oauth client is not presented

Service consumer Example: Oauth .Net client

string uriendpoint=" http://localhost:49262/TestProject/Service.svc/user/123";

 OAuth.OAuthBase oauth = new OAuth.OAuthBase();


        string normalizedurl;
        string normalizedqueryparameters;


        string sig = oauth.GenerateSignature(
            new Uri(uriendpoint),
            "key",
            "secret",
            null, //token
            null, //token secret
            "GET",
            oauth.GenerateTimeStamp(),
            oauth.GenerateNonce(),
            out normalizedurl,
            out normalizedqueryparameters);


   string finalurl = normalizedurl + "?" + normalizedqueryparameters +
 "&oauth_signature=" +oauth.UrlEncode(sig);
 
        // to make the service call

        HttpWebRequest request = (HttpWebRequest)WebRequest.Create(finalurl);

        HttpWebResponse response = (HttpWebResponse)request.GetResponse();

        XPathDocument xml = new XPathDocument(response.GetResponseStream());

        //.....

A couple of points:

1. It will generate url like :

http://localhost:49262/TestProject/Service.svc/user/123?oauth_consumer_key=key&oauth_nonce=10a33ed37b549301644b23b93fc1f1c5&oauth_signature=cUobFDxVB5wjPe9X2XICJ6awmnE%3D&oauth_signature_method=HMAC-SHA1&oauth_timestamp=1289976718&oauth_version=1.0

2. Token and token secret are used for 3 legged.

To sum up :

The examples show that oauth can be done by just using OAuthBase.cs, both as a service provider and a service consumer in .Net.

From my other article a php example of oauth is also presented.

Tags:

This entry was posted on Wednesday, November 17th, 2010 at 8:38 am and is filed under ASP.NET. You can follow any responses to this entry through the RSS 2.0 feed. You can leave a response, or trackback from your own site.

49 Responses to “OAuth and .Net”

  1. Prince says:

    Hi I was just trying implement the example u have provided in this article.. unfortunately i am facing one error “Configuration binding extension ‘system.serviceModel/bindings/webHttpb’ could not be found. Verify that this binding extension is properly registered in system.serviceModel/extensions/bindingExtensions and that it is spelled correctly”

    i have mentioned the binding as Httpbinding inside the tag..
    could you please help me to resolve this problem ??

  2. Prince says:

    Thanks alot !! it worked !!

  3. Prince says:

    Is it possible to do oAuth call from Ajax client to WCF Service?

    • CleanCodeNZ says:

      In theory you can I think, but it defeats the authentication purpose of OAuth, everything in a browser is no secret.

  4. Prince says:

    My requirement was to call WCF service from Ajax. Also the query string values in the url needs to change dynamically based on the user input. Could you please suggest me any other better authentication method for WCF from ajax client? How about WCFRestServiceWithKey? I am just trying this.. hope it works :)

    • CleanCodeNZ says:

      In the case of asking user to enter key and secret, you still can use oauth as the authentication method, you can find a javascript library of oauth here.

      Another way of doing this is to expose wcf webservices just to authenticated users. So users will need to login first before you allow them to the ajax page.

  5. softsan says:

    i get the following error 400: bad request

    • CleanCodeNZ says:

      400 error means your web service url does not exist.check your service url or request is to the right place.

      • yurets says:

        Hi, I’ve got the same “400 bad request” error. Fix is a very simple. You need to add a namespace for the “Service” in the web.config:
        1.
        2. <endpoint … contract="MyWcfService.Service"
        Think it can help.

  6. MSanchezDev says:

    I am implementing a WCF that needs to use OAuth 2 legged (for now), the client will send a username and password and the server needs to verify the credentials and send back the token and other stuff. So I have two questions:

    1- I notice that my final url does not contain the /user/123 (is 123 the password?)

    2- When I try your code, I set up everything but in the consumer part it never reaches the running web service, I would have expected the web service to do something when calling HttpWebResponse response = (HttpWebResponse)request.GetResponse();

    • CleanCodeNZ says:

      About sending credentials through web service, this might not be the point of OAuth, my understanding is both sides have the same secret, not to exchange secret through net.

      1. 123 in my example is id of a user as passed in parameter in restful style of url
      2. Not sure what you mean by ‘never reaches running web service’, is it never comes to the line of code of GetResponse(due to exceptions?) or nothing returned from GetResponse(that will be server problem?)

  7. Daniel says:

    Very Nice!!! exactly what I was looking for!

  8. Sam says:

    Hi,
    I am using Devdefined OAuth Lib to implement Consumer side. Any idea how I should give a POST method call?

    Thanks

    • CleanCodeNZ says:

      Not sure about Devdefined. If you know how to make a GET call, then I guess you need change method from GET to POST, while submitted data is part of url(querystring) if using GET, in case of POST, it might not be part of url. I have not done that actually, but easy to try it out.

      • sam says:

        Thanks for the reply. I am now going to follow the methd you have described above. In this case how will i give camm to POST method?

        • CleanCodeNZ says:

          In my client example, you change GET to POST, and send post data in stream which is not presented here, but you can find heaps of sample code on the internet.Cheers

  9. Sam says:

    Many Thanks for you reply. In the GET method call, I am getting 400 Bad request error. I have checked the web service url and all seems to be fine at that end. The breakpoint in constructor for my web service gets hit but the actual method never gets hit and the server throws 400 Bad request. Please guide

    • CleanCodeNZ says:

      It sounds to me the url might be mal-formed, just check simple url(without query parameters) from a browser and see how your server behaves? then add those query parameters one by one, if it is due to security reason, you might get something like authentication failed and with a different status code from 400.Goof luck.

  10. hugovin says:

    Man you code looks pretty simple but im wondering if do you have the code working.. i been trying to make it work but im unable to do it

  11. Clay says:

    Great example. Simple and concise. For those looking to implement this solution, keep in mind that the service authentication code should check the nonce. A secure service provider should check that a nonce/timestamp/consumer key combination has never been requested before.

    Otherwise, requests can be intercepted and reissued indefinitely.

  12. Victor says:

    Great example, thanks. To implement the full 2 legged OAuth protocol I will need to create an out of band login page and once the user has successfully logged in I have to send back the consumer key and a consumer secret? Am I correct in assuming above?

    Thank You

    • CleanCodeNZ says:

      Hi Victor: I am not too sure about what you want to achieve using oAuth, it is mainly used by communications between two applications.

  13. Dileep says:

    Hi,
    i have an restful service where my business login interacts with it(restful service) while user logs in, in order to check credentials.so my question here is how can i use this Oauth to interact with my restful service.

    • CleanCodeNZ says:

      Sorry, can not help, as I do not quite know what your situation is.

      • Dileep says:

        Hi,
        Let me be Clear with my requirment.My UI Layer interacts with restful service in order to authenticate the user.If the user is authorized then he will be allowed to login.In order to do this we are calling the ‘uri’ of restful service from our UI layer.So my question here is how can i plug in the oauth concept into my restful service.

        • CleanCodeNZ says:

          Hi Dileep:

          Thanks for more info.

          If your url restful service is a different application from the app with login ui, that is fine to use oauth.

          But if the app with login ui is the same app of restful service (for example they have same domain name), there are standard ways to do user login in most of web servers like tomcat, iis or apache and through https to protect use login.

          Again my understanding is oauth is used between two applications, one application to authorize the other. that is why I was not too sure about your intension to use it to authorise user login.

          Cheers

  14. Tiger33 says:

    Great example

    how can you configure this for WCF Data Services?

  15. RRZ says:

    Doing some research I found your site – good work!

    fyi concerning Plaintext, I stumbled upon:
    http://hueniverse.com/oauth/guide/security/

  16. Jem Savery says:

    Hi,

    Regards from Jem.

    I am having ConsumerKey & ConsumerSecret values. This code is going to be used in Webservice (C#.Net). I just want to get another two values AccessToken & AccessTokenSecret from these two values using TwitterAPI.

    How can I get this? Without passing the values to url and redirecting to another pages please.

    Please help.

    • CleanCodeNZ says:

      Hi Jem:

      This is 3-leg OAuth, Access Token and Access Token Secret will be available after the first leg, and can be accessed in headers or query parameters, if first leg authentication is successful. My example only covers 2 leg OAuth scenario. Cheers

  17. Allan says:

    My only issue is that the following

    string uri = operationContext.IncomingMessageProperties.Via.OriginalString.Replace(operationContext.IncomingMessageProperties.Via.Query, “”);

    is returning my a uri with server name not the domain?

    ie if i call http://api.hook.com/etc…. the uri is returning http://servername/etc….

    Am i missing something in the setting

    Thanks

    Allan

    • CleanCodeNZ says:

      That part is just trying to get back uri, it should be the same as the one going into hash. put a break point to see if there is any other places to get back original uri.

      • BobTodd says:

        Awesome. Exactly what I was looking for oAuth demystified with something showing how simple it is with a nice straightforward implementation.

        I also had the issue with localhost being translated into a FQDN – all I’ve done is added “localhost” to my IIS bindings for the host header setting and it now works a treat.

  18. BobTodd says:

    …btw doesnt seem to work if your path already has some query parameters in it

    string uriendpoint=” http://localhost:49262/TestProject/Service.svc/users?searchText=dave&page=1&pageSize=20” = wont ever authenticate

    • BobTodd says:

      not stripping out the query from the originating request seems to be the solution for me…

      // to get uri without oauth parameters
      string uri = operationContext.IncomingMessageProperties.Via.OriginalString;
      //.Replace(operationContext.IncomingMessageProperties.Via.Query, String.Empty);

  19. MrZer0 says:

    Great article. But in the original OAuthBase class the method UrlEncode is protected not public! I think this is an error, isn’t it?

    http://oauth.googlecode.com/svn/code/csharp/OAuthBase.cs

    • CleanCodeNZ says:

      Sorry I can not know for sure about this as I do not have the source code at moment, if it is the case, I do not see any harm you make a small change like this, or extend it in a sub class and make it public. Cheers

      • MrZer0 says:

        Maybe it’s better to use HttpUtility.UrlEncode for encoding. While trying to authenticate with Dropbox API I had some trouble with UrlEncode from OAuthBase.
        But that’s just my experience ;-)

  20. Jay says:

    It is very nice article. My req is i want to call OAuth enabled restful wcf service from Jquery.

    I had done lots of research, but not finding exact solution.

    Please help me with any code samples.

  21. Djissi says:

    Hello
    the example 2 works fine in local with visual studio, but when i publish on windows azure, I have always “Access denied”..
    Have you got an idea or issue for this?

    thanks

    • CleanCodeNZ says:

      Hi there: From the top of my head, I gather ServiceAuthorizationManager might not be available in wcf, another thing is have you tried your wcf on AZure without OAuth first? normally it is using https. You can add a few logging or tracing to see where it actually failed.

  22. Imthiyas says:

    Hello,I am getting 400 Bad Request Error while testing from REST client. can you advice on this pls

    • CleanCodeNZ says:

      Just read some of the comments here some one mentioned that a wrong namespace caused that, generally speaking it means the service url is not right.

  23. AUDev says:

    I am not sure method 2 is really one time authentication. As client will call another function on WCF (or same function), client requires to pass finalurl, which will again go and check authentication on manager.

    Probably, my understanding is not correct here. I was thinking once client is authenticated, they should just pass token for follow up calls (for few minutes expiry).

    Anyone please explain. Thanks in advance.

    • CleanCodeNZ says:

      Hi There:
      You are right that if one client needs to talk to wcf service for several calls aka in a session, you better build a token and check token in the session. it is also possible that it only needs wcf service one off, in which case there is no need for a token. When you implement wcf security in a more traditional way using Transport or Message secure mode, there is an option you can turn establishSecurityContext off to disable the session.

  24. nadia says:

    Hello ,
    Great example , plz need to know how i can use this service web?

Leave a Reply

*