Header Image

Using Scribe OAuth Library with Layer 7’s OAuth Toolkit

Overview

 

The Layer 7 OAuth Toolkit is one of the most configurable and powerful OAuth implementations available on the market. A SecureSpan Gateway with OAuth Toolkit installed can play the role of the OAuth authorization server, the OAuth client or the resource server but it can also interact with other OAuth implementations (Please see http://oauth.net/code for a list of the most popular OAuth libraries).

In this tutorial, we will walk through the steps required to use the open source project Scribe-Java to create a client application that can use OAuth 1 or OAuth 2 tokens to access protected services on a SecureSpan Gateway.

 

Using Scribe-Java

 

The Scribe project has built-in support for many popular services that use OAuth, such as Facebook, Twitter and Google. The classes shown in this tutorial are already built in to a fork of the master branch of Scribe available on GitHub. This tutorial describes how the classes were implemented and illustrates how they can be used.

To begin, review Scribe’s Getting Started page for the installation requirements. The requirements (as of Scribe 1.3) are Scribe-1.3.0 and the Apache commons-codec-1.6.

 

Requirements

 

To use the examples below you will need access to a Layer 7 SecureSpan Gateway (minimum version 6.1.5) with the following OAuth Toolkit modules:

  • OVP/TokenStore/ClientStore and database
  • OAuth 1 (For OAuth 1 only)
  • OAuth 2 Authorization Server (for OAuth 2)
  • OAuth 2 Resource Server (for OAuth 2)
  • OAuth Manager webapp
 

For demonstration purposes, a pre-configured SecureSpan Gateway is available at preview.layer7tech.com. Contact Layer 7 support to register a client application or use the default values in the examples that follow.

 

A Note About SSL

The OAuth Toolkit’s OAuth 1.0 and 2.0 implementations require HTTPS on most endpoints to enhance security. This enhanced security requires that the Gateway’s certificate is trusted or signed by a trusted CA. If the Gateway certificate is self-signed, Scribe will not be able to connect without explicitly adding an exception to the JRE security settings. As of June 2012 the preview.layer7tech.com site is using a self-signed certificate and will require that the certificate is added to the trust store.

 

OAuth 1.0

 

Extend DefaultApi10a

All OAuth 1.0 providers need to “extend” the class “DefaultApi10a”. There are three methods that must be overridden, one for each of the OAuth 1.0 endpoints:

 
public class Layer7Api extends DefaultApi10a
{
 private static final String AUTHORIZE_URL = "%s://%s:%s/auth/oauth/v1/authorize?oauth_token=%s";
 private static final String REQUEST_TOKEN_RESOURCE = "%s://%s:%s/auth/oauth/v1/request";
 private static final String ACCESS_TOKEN_RESOURCE = "%s://%s:%s/auth/oauth/v1/token";
 
 private static String host, method, port;
 
 @Override
 public String getAccessTokenEndpoint()
 {
    setHostname();
    return String.format(ACCESS_TOKEN_RESOURCE, method, host, port);
 }
 
 @Override
 public String getAuthorizationUrl(Token token)
 {
    setHostname();
    return String.format(AUTHORIZE_URL, method, host, port, token.getToken());
 }
 
 @Override
 public String getRequestTokenEndpoint()
 {
    setHostname();
    return String.format(REQUEST_TOKEN_RESOURCE, method, host, port);

 }

 Figure 1 – Layer7Api implementation for OAuth 1.0.
 

This example is set up to allow a user of this library to specify the connection information at runtime using a “.properties” file. If the properties file is not found then the default values are used. This allows greater flexibility and makes it easier to use this class in staging or test environments where the hostname may be different than it is in production. The next section shows the rest of the class, which handles loading the properties.

 
/*
   * Loads the host, port, and method from the properties file the first time this method is run.
   */
 private void setHostname()
 {
    if (null == host || null == port || null == method)
    {
      Properties prop = loadProperties();
      host = prop.getProperty("oauth1.hostname", "preview.layer7tech.com");
      port = prop.getProperty("oauth1.port", "8447");
      method = prop.getProperty("oauth1.method", "https");
    }
 }
 
 protected static Properties loadProperties()
 {
    final Properties prop = new Properties();
    try
    {
      final InputStream propertiesStream = Layer7Api.class.getResourceAsStream("/layer7.properties");
      if (propertiesStream != null)
        prop.load(propertiesStream);
    }
    catch (IOException e)
    {
      throw new OAuthException("Error while reading properties file", e);
    }
    return prop;

 }

 Figure 2 – Layer7Api Properties Implementation: The connection parameters are loaded at runtime. If the properties file is present the default values are overridden.

 

Usage

You can now use the class created in the previous section to connect to the OAuth Toolkit’s OAuth 1 server as a client. You must first register a client key and secret, using the OAuth manager page at https://<server:port>/oauth/manager.  

 
OAuthService service = new ServiceBuilder()
        .provider(Layer7Api.class)
        .apiKey("Consumer")
        .apiSecret("Secret")

        .build();

Figure 3 - Creating an OAuth Service object for OAuth 1.0.

 

Authorization

 

In order to obtain an access token to access resources on a user’s behalf, we must complete the Authorization process. 

 

Figure 4 - OAuth 1.0 Authorization Process.
 

From the client’s perspective, there are three tasks that must be done to get an access token:

 

1. Get a Request Token

See the OAuth 1.0 spec for details. The following line of code obtains the request token from the OAuth server:

 
Token requestToken = service.getRequestToken();

Figure 5 – Create an OAuth 1.0 request token.

 

2. Generate the Authorization URL

At this point in the authorization process, the resource owner is sent to the authorization server to authenticate and grant access. Usually, this is done by sending an HTTP 301 response to the user’s browser in order to redirect the user. To get the redirection URL, another line of code is used:

 

System.out.println(service.getAuthorizationUrl(requestToken));

Figure 6 – Get the redirection URL.

 

3. Get the Access Token   

If the resource owner successfully completes the authorization and authentication process, they are returned to the client with a code called a Verifier. The client uses this code to obtain the access token:

 
 Verifier verifier = new Verifier(request.parameter.token);
 
// Trade the Request Token and Verifier for the Access Token

Token accessToken = service.getAccessToken(requestToken, verifier);

Figure 7 - Use the Verifier to get the access token.

 

Once the access token is obtained, it can be used for subsequent requests to access resources protected by OAuth. The access token should be stored securely and it may expire after a certain amount of time. To consume a service on a SecureSpan Gateway that is protected by the “Require OAuth 1.0 Token” policy fragment requires an OAuthRequest object:

 

       OAuthRequest request = new OAuthRequest(Verb.POST, PROTECTED_RESOURCE_URI);
    request.addBodyParameter(
"param", "value");
         service.signRequest(accessToken, request);
   
Response response = request.send();
   
System.out.println(response.getBody());

Figure 8 – Using the access token with OAuthRequest.

 

OAuth 2.0

 

Extend DefaultApi20

To use Scribe as a client that consumes resources on a Gateway protected by the OAuth Toolkit requires that we extend the DefaultApi20 class:

 

public class Layer7Api20 extends DefaultApi20

{

Figure 9 – Extend the DefaultApi20 class.

 

As with the OAuth 1 implementation, there are some methods that need to be overridden and we will enable configuration of the hostname through a properties file that is read if it is present. If the properties file is missing then the default server is used (in this case, preview.layer7tech.com): 

 
 private static final String DEFAULT_METHOD = "https";
 private static final String DEFAULT_PORT = "8447";
 private static final String DEFAULT_HOST = "preview.layer7tech.com";
 private static final String AUTHORIZE_URL = "%s://%s:%s/auth/oauth/v2/authorize?response_type=code";
 
 private static String host;
 private static String port;
 private static String method;
 
 @Override
 public String getAccessTokenEndpoint()
 {
    setHostname();
    return String.format("%s://%s:%s/auth/oauth/v2/token?grant_type=authorization_code", method, host, port);
 }
 
 @Override
 public Verb getAccessTokenVerb()
 {
    return Verb.POST;
 }
 
 @Override
 public AccessTokenExtractor getAccessTokenExtractor()
 {
    return new JsonTokenExtractor();
 }
 
 @Override
 public String getAuthorizationUrl(OAuthConfig config)
 {
    setHostname();
    StringBuilder authUrl = new StringBuilder();
    authUrl.append(String.format(AUTHORIZE_URL, method, host, port));
 
    // Append scope if present
    if (config.hasScope())
    {
      authUrl.append("&scope=").append(OAuthEncoder.encode(config.getScope()));
    }
 
    // add redirect URI if callback isn't equal to 'oob'
    if (!config.getCallback().equalsIgnoreCase("oob"))
    {
      authUrl.append("&redirect_uri=").append(OAuthEncoder.encode(config.getCallback()));
    }
 
    authUrl.append("&client_id=").append(OAuthEncoder.encode(config.getApiKey()));
    return authUrl.toString();

 }

Figure 10 - The Layer 7 OAuth 2 implementation uses these endpoints. Some parameters are optional and hostname is loaded at runtime.

 

Note that this class, as written, requires the method “loadProperties” from the OAuth 1 class above. If this class is not available, the method can be moved into this class. The loading of the hostname at runtime is accomplished with the “setHostname” method:

 
 /*
   * sets the host, port, and method from a properties file the first time this method is run.
   */
 private void setHostname()
 {
    if (null == host || null == port || null == method)
    {
      Properties prop = Layer7Api.loadProperties();
      host = prop.getProperty("oauth2.authz.hostname", Layer7Api20.DEFAULT_HOST);
      port = prop.getProperty("oauth2.authz.port", Layer7Api20.DEFAULT_PORT);
      method = prop.getProperty("oauth2.authz.method", Layer7Api20.DEFAULT_METHOD);
    }

 }

Figure 11 - OAuth 2 setting the connection parameters.

 

Usage

 

The following example uses the “Layer7Api20” class to play the role of the client in the OAuth 2.0 authorization process. To use this code, set up a project as outlined above. Use the OAuth Manager tool to register a client application and obtain a client key and secret.

 

 Figure 12 - Registering a client Application in the OAuth Manager webapp.
 

Once your project is ready, the next step is to create the OAuth Service object using the Layer7Api20 provider, the client key, secret and callback registered earlier. The callback URL that was registered must match the “redirect_uri” parameter exactly!

 

       // Replace these with your own api key and secret
    String apiKey =
"Consumer";
    String apiSecret =
"Secret";
    OAuthService service =
new ServiceBuilder()
        .provider(Layer7Api20.
class)
        .apiKey(apiKey)

        .apiSecret(apiSecret)

       .callback(
"http://preview.layer7tech.com:8080/oauth/v2/redirect")
        .scope(
"oob")
        .build();

 Figure 13 - Creating the OAuthService for OAuth 2.

 

To make debugging easier, you can also include “.debug()” in the above. The client is supposed to begin the authorization process by redirecting the resource owner to the authorization server’s authorization endpoint. In most cases, the redirection comes from responding with HTTP 301 and the header “Location: <AUTHZ_URL>”:

 
// Obtain the Authorization URL
    System.out.println("Fetching the Authorization URL...");
    String authorizationUrl = service.getAuthorizationUrl(EMPTY_TOKEN);

    System.out.println("Got the Authorization URL!");

Figure 14 – Obtain the authorization URL.

 

In this example, a more old-fashioned technique is used – the “have the resource owner copy and paste” technique (this is for expediency and should not be used in production):

 
System.out.println("Got the Authorization URL!");
    System.out.println("Now go and authorize Scribe here (Log in as 'User1'/'Passw0rd'):");
    System.out.println(authorizationUrl);
    System.out.println("And paste the authorization code here");
    System.out.print(">>");
    Verifier verifier = new Verifier(in.nextLine());

 Figure 15 - Authorizing the client and getting the Authorization code.

 

Once the authorization code is obtained (called a “Verifier” in Scribe, which uses OAuth 1 terminology throughout the OAuth 2 implementation) after the resource owner successfully authenticates and authorizes the client, the client can request an access token from the authorization server. This is the final step in the authorization flow for the authorization code grant type.

 
Token accessToken = service.getAccessToken(EMPTY_TOKEN, verifier);

Figure 16 – Request an access token from the authorization server.

 

This token can now be used to access the user’s resources on his or her behalf. Using the token requires using the OAuthRequest and OAuthResponse classes, which in turn use java.net.HttpURLConnection for sending and receiving data. The method “signRequest” is used in Scribe with OAuth 2, even though requests are not actually signed as they are in OAuth 1:

 
    // Now let's go and ask for a protected resource!
    System.out.println("Now we're going to access a protected resource...");
    OAuthRequest request = new OAuthRequest(Verb.GET, PROTECTED_RESOURCE_URL);
    service.signRequest(accessToken, request);
    Response response = request.send();
    System.out.println("Got it! Lets see what we found...");
    System.out.println();
    System.out.println(response.getCode());
    System.out.println(response.getBody());

Figure 17 - Consuming resources using OAuth 2. 

Using Scribe OAuth Library with Layer 7’s OAuth Toolkit