Sunday, February 2, 2014

Jersey and OAuth

Introduction

Jersey contains support for the signing and verififcation of requests, per the OAuth Core 1.0 specification. There are three modules in the Jersey contributions section that provide support for OAuth:
  • OAuth signature library: provides core support for handling OAuth signatures
  • OAuth Jersey client filter: outgoing requests are automatically signed with OAuth signature
  • OAuth Jersey server request wrapper: wraps Jersey server requests to verify OAuth signature
For sample code, check out the oauth-tests in svn co https://svn.java.net/svn/jersey~svn/trunk contribs/jersey-oauth/oauth-tests

OAuth signature library

The OAuth signature library provides core support for generation, verification and signing of requests.
It supports the signature methods outlined in OAuth Core 1.0 specification: HMAC-SHA1, RSA-SHA1, and PLAINTEXT. Additional signature methods can be implemented by third parties and automatically loaded at the time the signature library JAR file is loaded.
Code that utilizes the OAuth signature library implement the OAuthRequest interface to expose the request to the library for signature generation/verification. Additionally, an OAuthParameters object contains the parameters used in signing, and OAuthSecrets object is used to specify the secrets that back the consumer key and/or access/request token. The OAuthSignature class is used to sign and verify requests.
Example usage:
// wrap an existing request with some concrete implementation
OAuthRequest request = new SomeConcreteOAuthRequestImplementation();
 
// establish the parameters that will be used to sign the request
OAuthParameters params = new OAuthParameters().consumerKey("dpf43f3p2l4k3l03").
 token("nnch734d00sl2jdk").signatureMethod(HMAC_SHA1.NAME).
 timestamp().nonce().version();
 
// establish the secrets that will be used to sign the request
OAuthSecrets secrets = new OAuthSecrets().consumerSecret("kd94hf93k423kf44").
 tokenSecret("pfkkdhi9sl3r4s00");
 
 // generate the digital signature and set in the request
 OAuthSignature.sign(request, params, secrets);

OAuth Jersey client filter

The OAuth Jersey client filter uses the OAuth signature library to automatically signs outgoing requests with established parameters and secrets. A filter instance can be added at one of two levels:
  • Client: all outgoing requests are signed with established parameters and secrets
  • WebResource: all requests to the resource are signed with established parameters and secrets
As WebResource objects are inexpensive to create, if the same resource must be signed with different parameters and/or secrets, new instances of the resource should be created to add a filter instance to.
The filter will not sign a request if an Authorization header is already present in the outgoing request. This allows previous filters in the chain to override behavior.
Example usage:
// baseline OAuth parameters for access to resource
OAuthParameters params = new OAuthParameters().signatureMethod("HMAC-SHA1").
consumerKey("key").setToken("accesskey").version();
 
// OAuth secrets to access resource
OAuthSecrets secrets = new OAuthSecrets().consumerSecret("secret").setTokenSecret("accesssecret");
 
// if parameters and secrets remain static, filter can be added to each web resource
OAuthClientFilter filter = new OAuthClientFilter(client.getProviders(), params, secrets);
 
// OAuth test server resource
WebResource resource = client.resource("http://term.ie/oauth/example/request_token.php");
 
// filter added at the web resource level
resource.addFilter(filter);
 
// make the request (signing it in the process)
String response = resource.get(String.class);
Notice in this example that timestamp and nonce are not explicitly set. When not set in the OAuthParameters object, the client will automatically set to the current time in seconds since epoch, and select a random nonce value. If a value is explicitly set, it is presumed to be intended to be sent in the request, and will not be overwritten.

OAuth Jersey server request wrapper

The OAuth Jersey server request wrapper uses the OAuth signature library to allow a Jersey server resource to manually verify the signature of an incoming request. It is a concrete implementation of the OAuthRequest interface in the OAuth signature library.
Example usage:
@GET
public String getVerified(@Context HttpContext hc) {
 
    // wrap incoming request for OAuth signature verification
    OAuthServerRequest request = new OAuthServerRequest(hc.getRequest());
 
    // get incoming OAuth parameters
    OAuthParameters params = new OAuthParameters();
    params.readRequest(request);
 
    OAuthSecrets secrets = new OAuthSecrets();
    ... set secrets based on consumer key and/or token in parameters ...
 
    try {
        return Boolean.toString(OAuthSignature.verify(request, params, secrets));
    }
    catch (OAuthSignatureException ose) {
        return "false";
    }
}
To comply with the OAuth protocol, this contrived example above should actually return a 400 or 401 status code in response rejecting the consumer request, depending on the reason of rejection.
Performing signature verification per-resource is generally discouraged; using a server filter method to verify incoming requests for groups of protected resources is far preferable. For example, the OpenSSO project has a working ServletFilter implementation in its OAuth extension that sets the user principal in the security context based on the OAuth signature. This allows JSPs and servlets to call the HttpServletRequest.getUserPrincipal method to determine the identity of the user that authorized the issuance of the access token.

Simple OAuth Authentication with a Container filter

Simple OAuth authentication for a servlet or filter may be set up using a Container Filter, which filters the request before the request is matched and dispatched to a root resource class. The Container Filter is registered usinginitialization parameters which point to a user defined class, such as the following:
public class OAuthAuthenticationFilter implements ContainerRequestFilter {
    @Override
    public ContainerRequest filter(ContainerRequest containerRequest) {
        // Read the OAuth parameters from the request
        OAuthServerRequest request = new OAuthServerRequest(containerRequest);
        OAuthParameters params = new OAuthParameters();
        params.readRequest(request);
        
        // Set the secret(s), against which we will verify the request
        OAuthSecrets secrets = new OAuthSecrets();
        // ... secret setting code ...
        
        // Check that the timestamp has not expired
        String timestampStr = params.getTimestamp();
        // ... timestamp checking code ...
        
        // Verify the signature
        try {
            if(!OAuthSignature.verify(request, params, secrets)) {
                throw new WebApplicationException(401);
            }
        } catch (OAuthSignatureException e) {
            throw new WebApplicationException(e, 401);
        }
        
        // Return the request
        return containerRequest;
    }
}

OAuth implementations using Jersey

  • OAuth4J is an OAuth implementation using Jersey.
Labels: