This project is read-only.

Project Description and Usage Overview

This Simple Web Discovery (SWD) endpoint provides a uniform access point that tells you (1) where to authenticate and (2) where to get access tokens. It makes it easier to implement OAuth2.0 in a general way. It can also make other info available if need be. Written in C#.

More Details

When creating an OAuth protected OData service (see Alex's blogs for more on this), clients need to know about two additional endpoints:
  • Consent Server: where the client actually authenticates and authorizes data access (Google, Facebook, etc.).
  • Token Endpoint: where the client exchanges an Authorization code for an Access Token, or where they acquire a new Access Token with a Refresh Token.
A client may want to draw data from many different sources, but there is no standard way to find these two extra endpoints besides hard coding each of them for every single data source. We’d like to make this easier to enable richer uses of data.

The Simple Web Discovery protocol (hereafter SWD) outlines a mechanism for discovering the location(s) of services for a particular principal. In our case the principal will be the OData Service itself. Using a slightly modified version of this protocol, we can expose a standardized endpoint discovery method.

To make this as easy as possible, we are publishing code that will serve both of these necessary service URLs. We have created an Attribute that can just be added onto an existing OData service. It will add a Simple Web Discovery endpoint that provides all of the necessary endpoint information. The code is fairly general, so it can be useful in other contexts too. Let’s get started.

Note: The SWD protocol requires that the .wellknown endpoint extend directly from the root of the domain, but with an OData service this may not be possible without significant server configuration changes. This may not be possible for everyone, so in our case the .wellknow endpoint will extend from the OData service itself. Since we are extending from the service we already know our principal, it’s already in the URL. Therefore, it does not need to be specified as a query argument to the SWD endpoint. These changes make it extremely easy to add to an existing OData service.

Simple Web Discovery Behavior

Adding a simple web discovery (SWD) endpoint is as easy as adding an attribute to your DataService. We will discuss the OAuthEndpoints class in the next section.

    [SimpleWebDiscoveryBehavior(typeof(OAuthEndpoints))]
    public class WcfDataService : DataService<WorldCupData>
    {
        // your code here
    }
In this example, our service is located at …/WorldCupData.svc. By adding this attribute you will create a queryable endpoint at
…/WorldCupData.svc/$wellknown/simple-web-discovery

If we want to discover the usable consent endpoints for our service we just query the following URL:
…/WorldCupData.svc/$wellknown/simple-web-discovery?service=urn:odata.org/auth/oauth/2.0/consent

This returns a JSON string listing all of the allowed consent servers. It will look something like this:
{ "locations" : [ "http://www.google.com", "http://www.facebook.com" ] }
This gives the client application options to authenticate through many different supported services. For instance, if the client only supports Facebook authentication, they simply choose that particular endpoint and carry on.

Endpoints

We need some way to configure the locations returned by this new endpoint. This is done by extending the Endpoints class. The Endpoint class has a dictionary field mapping service names (keys) to endpoint lists (values). When you query the SWD endpoint for a particular service, it will look the service up in this dictionary and return the locations in the corresponding list. Let’s see it in action.

Example 1

The best way to add your own endpoints is to extend the Endpoints class provided above. Once you have created your endpoint subclass, you can simply add the SimpleWebDiscoveryBehavior attribute to your service. The whole thing will look something like this:

public class OAuthEndpoints : Endpoints
{
    public OAuthEndpoints() : base()
    {
        this.AddEndpoint("consent", "http://www.google.com");
    }
}

[SimpleWebDiscoveryBehavior(typeof(OAuthEndpoints))]
public class WcfDataService : DataService<WorldCupData>
{
    // your code here
}

Now when we can navigate to
…/WorldCupData.svc/$wellknown/simple-web-discovery?service=consent

This returns a JSON string listing all of the allowed consent endpoints. It will look like this:
{ "locations" : [ "http://www.google.com" ] }

Nice! We are pretty close to having a fully functional SWD endpoint.

Example 2

There is just one last thing. To make it more difficult to make a typo when entering keys into the EndpointDictionary, we have provided the static string values ConsentEndpoints and TokenEndpoints in the Endpoints class. This way you don’t need to type out those crazy strings for every endpoint you add.

public class OAuthEndpoints : Endpoints
{
    public OAuthEndpoints() : base()
    {
        this.AddEndpoint(Endpoints.ConsentEndpoints, "http://www.google.com");
        this.AddEndpoint(Endpoints.ConsentEndpoints, "http://www.facebook.com");
        this.AddEndpoint(Endpoints.TokenEndpoints,   "http://www.bing.com");
    }
}

[SimpleWebDiscoveryBehavior(typeof(OAuthEndpoints))]
public class WcfDataService : DataService<WorldCupData>
{
    // your code here
}

Now we will have precisely the behavior described at the beginning of this post:
…/WorldCupData.svc/$wellknown/simple-web-discovery?service=urn:odata.org/auth/oauth/2.0/consent

will return
{ "locations" : [ "http://www.google.com", "http://www.facebook.com" ] }

Furthermore, we’ve seen that you can add as many endpoints as you’d like. In this case we also included a token endpoints service, so
…/WorldCupData.svc/$wellknown/simple-web-discovery?service=urn:odata.org/auth/oauth/2.0/token-endpoint

will return
{ "locations" : [ "http://www.bing.com" ] }

Great, now you have listings of the consent endpoints and token endpoints! You can also add any other endpoint that you may need, so hopefully you can use this code again when OAuth3.0 or HotNewAuthProtocol becomes the cool thing to do. Happy Authorizing!

Last edited Aug 12, 2011 at 9:25 PM by evancz, version 19