DotNetOpenAuth Provider for REST Service

dotnetopenauth, rest

Solution

I had the same problem a couple of days ago. This answer is ridiculously long, maybe there's an easier way.

Personally, I don't use DNOA anymore because it is designed towards self-validating (i.e. encrypted tokens) so you don't need to hit the DB with every request. A very important side effect of this is that an access revocation will not become effective immediately, but only after the token must be renewed. Also, access tokens will become quite long (around 500 bytes).

As a very first step, make sure you know what you need:

OAuth / OAuth2 look easy at first, but it's important to understand how the authorization workflows are designed. Also, their terminology can be irritating, for instance 'Client' refers to what I would naively call client application. It's not the user (who is called 'resource owner' in OAuth terms). My suggestion: Read RFC 6749. It looks dull, but it's an interesting read (and you can skip half of it...)

A key question is: Do you need 2-legged OAuth or 3-legged OAuth (or both?). Which grant types do you need to support?

If you basically want to replace HTTP Basic Auth, the simple "Resource owner password credentials flow" will do. The facebook/twitter kind type of "have this application access my profile information" is 3-legged OAuth.

There's an IBM Documentation that comes with nice grant type diagrams.

Now to DNOA, take a look at `Samples/OAuthAuthorizationServer`.

A good entry point is the `OAuthController.cs` file. Note that the `Authorize` and `AuthorizeResponse` actions are required only if you want to enable your users to give access to third party applications (3-legged OAuth).

In a 2-legged scenario, users access the OAuth `token` endpoint directly and simply request an access token. In any case you will need such a controller in your REST application.

The key to the inner workings is the `OAuth2AuthorizationServer` class (NOT the `AuthorizationServer` class). Look at `Code/OAuth2AuthorizationServer.cs`. It implements `IAuthorizationServerHost`.

Half of that class deals with data storage (which you might want to modify if you're working with a different datastore), and half of it deals with the encryption of the access tokens. You will need to implement IAuthorizationServerHost for your application, too.

Make sure you have a line `#define SAMPLESONLY` in your code, so it will accept the hardcoded certificate.

To actually authorize the request, it is helpful to write a custom `ActionFilterAttribute`. Here's some super condensed code, not production ready:

public sealed class BasicAuthenticationAttribute : System.Web.Http.Filters.ActionFilterAttribute
{
    private readonly OAuthResourceServer _authServer; 
    public override void OnActionExecuting(HttpActionContext actionContext)
    {
        if (actionContext.Request.Headers.Authorization.Scheme == "Bearer"
            || actionContext.Request.Properties.ContainsKey("access_token"))
        {
            authenticatedUser = _authServer.VerifyOAuth2(request, required_claims);
            HttpContext.Current.User = authenticatedUser;
            Thread.CurrentPrincipal = authenticatedUser;
        }
    }
}

// See OAuthResourceServer/Code/OAuthAuthorizationManager.cs in DNOA samples
public sealed class OAuthResourceServer
{
    public IPrincipal VerifyOAuth2(HttpRequestMessage httpDetails, params string[] requiredScopes)
    {
        // for this sample where the auth server and resource server are the same site,
        // we use the same public/private key.
        using (var signing = CreateAuthorizationServerSigningServiceProvider())
        {
            using (var encrypting = CreateResourceServerEncryptionServiceProvider())
            {
                var tokenAnalyzer = new StandardAccessTokenAnalyzer(signing, encrypting);
                var resourceServer = new ResourceServer(_myUserService, tokenAnalyzer);
                return resourceServer.GetPrincipal(httpDetails, requiredScopes);
            }
        }
    }
}

The resource server is still missing

public sealed class MyResourceServer : ResourceServer
{
    public override System.Security.Principal.IPrincipal GetPrincipal([System.Runtime.InteropServices.OptionalAttribute]
        [System.Runtime.InteropServices.DefaultParameterValueAttribute(null)]
        HttpRequestBase httpRequestInfo, params string[] requiredScopes)
    {
        AccessToken accessToken = this.GetAccessToken(httpRequestInfo, requiredScopes);
        string principalUserName = !string.IsNullOrEmpty(accessToken.User)
            ? this.ResourceOwnerPrincipalPrefix + accessToken.User
            : this.ClientPrincipalPrefix + accessToken.ClientIdentifier;
        string[] principalScope = accessToken.Scope != null ? accessToken.Scope.ToArray() : new string[0];

        // Now your own code that retrieves the user 
        // based on principalUserName from the DB:
        return myUserService.GetUser(userName);
    }
}

Next, modify web.config so DNOA doesn't complain about missing SSL connections in development:

<configSections>
      <sectionGroup name="dotNetOpenAuth" type="DotNetOpenAuth.Configuration.DotNetOpenAuthSection, DotNetOpenAuth">
        <section name="openid" type="DotNetOpenAuth.Configuration.OpenIdElement, DotNetOpenAuth" requirePermission="false" allowLocation="true" />
        <section name="oauth" type="DotNetOpenAuth.Configuration.OAuthElement, DotNetOpenAuth" requirePermission="false" allowLocation="true" />
        <sectionGroup name="oauth2" type="DotNetOpenAuth.Configuration.OAuth2SectionGroup, DotNetOpenAuth">
          <section name="authorizationServer" type="DotNetOpenAuth.Configuration.OAuth2AuthorizationServerSection, DotNetOpenAuth" requirePermission="false" allowLocation="true" />
        </sectionGroup>
        <section name="messaging" type="DotNetOpenAuth.Configuration.MessagingElement, DotNetOpenAuth" requirePermission="false" allowLocation="true" />
        <section name="reporting" type="DotNetOpenAuth.Configuration.ReportingElement, DotNetOpenAuth" requirePermission="false" allowLocation="true" />
      </sectionGroup>    
  </configSections>
  <dotNetOpenAuth>
    <!-- Allow DotNetOpenAuth to publish usage statistics to library authors to improve the library. -->
    <reporting enabled="true" />
    <openid>
      <provider>
        <security requireSsl="false">
        </security>
      </provider>
    </openid>
    <oauth2>
      <authorizationServer >
      </authorizationServer>
    </oauth2>
    <!-- Relaxing SSL requirements is useful for simple samples, but NOT a good idea in production. -->
    <messaging relaxSslRequirements="true">
      <untrustedWebRequest>
        <whitelistHosts>
          <!-- since this is a sample, and will often be used with localhost -->
          <add name="localhost"/>
        </whitelistHosts>
      </untrustedWebRequest>
    </messaging>
  </dotNetOpenAuth>

Problem

I've created a REST API in an Area of my ASP.NET MVC 4 web application. The API is working properly, and I'd now like to secure it. Is there a very simple example of how I can do this? I'm going through the samples that came with the DotNetOpenAuth download and I'm completely lost on it.

Original source