Skip to content

How to Use

Benton Porter edited this page Jul 8, 2014 · 20 revisions

This page shows how to use jersey-hmac-auth on the server to secure your API and also in client libraries that you build to interface with the API.

Server

Maven Setup

To implement jersey-hmac-auth on the server, add this dependency to your application:

<dependency>
    <groupId>com.bazaarvoice.auth</groupId>
    <artifactId>jersey-hmac-auth-server</artifactId>
    <version>${version}</version>
</dependency>

Securing Resource Methods

You can secure your API endpoints by adding the @HmacAuth annotation to their corresponding Jersey resource methods. For example:

@Path("/pizza")
@Produces(MediaType.TEXT_PLAIN)
public class PizzaResource {
    @GET
    public String get(@HmacAuth Principal principal) {
        // This gets control only if the request is authenticated. 
        // The principal identifies the API caller (and can be of any type you want).    
    }
}

If a caller makes an HTTP GET request to the /pizza endpoint, then by default the resource method will get control only if the request is authenticated successfully. If authentication fails, then jersey-hmac-auth will automatically reject the request and return a "401 (Unauthorized)" HTTP status code before this method gets control.

You can control the default authentication behavior (e.g. to make authentication optional) by using a non-default request handler. There are more details about request handlers below.

Authenticators

Requests are authenticated on the server by an Authenticator class that you provide.

The authenticator is responsible for authenticating a request given the credentials on the request (e.g. the API key, the signature provided by the caller, and other such parameters). The authenticator also returns a "principal" object representing the authenticated API caller. The jersey-hmac-auth library will take the returned principal and inject it into the Jersey resource method that gets control to handle the request.

The following is an example authenticator:

public class MyAuthenticator extends AbstractCachingAuthenticator<Principal> {
    // some code is intentionally missing 
    
    @Override
    protected Principal loadPrincipal(Credentials credentials) {
        // return the principal identified by the credentials from the API request
    } 

    @Override
    protected String getSecretKeyFromPrincipal(Principal principal) {
        // return the secret key for the given principal
    }
}

The jersey-hmac-auth library provides abstract authenticator classes that you can extend for your own use. If you need something more custom, then you are welcome to create your own authenticator that implements the Authenticator interface.

The AbstractAuthenticator class provides the following features:

  • Validates the API key to ensure that it specifies a non-null principal.
  • Validates the signature by generating a new signature and comparing it to the one provided by the caller. This ensures that the caller had a valid secret key (they are a trusted source) and that the request has not been tampered with after being sent.
  • Validates the request timestamp. This ensures that the timestamp does not fall outside of an allowed time range. This is used to reduce the window of time for which a replay attack can occur.

The AbstractCachingAuthenticator class provides the same features as AbstractAuthenticator, but also:

  • Caches the principal so that your authenticator implementation does not have to retrieve it on every request made by the same caller. This is especially helpful if your authenticator retrieves the principal by querying another system (e.g. a database or some other service).

Request Handlers

Todo...

Registering your Authenticator with Jersey

Todo...


  1. Register the authentication provider with Jersey.

If using Dropwizard:

environment.addProvider(new HmacAuthProvider(new DefaultRequestHandler(new MyAuthenticator())));

If using straight Jersey, you basically do the same, but add the HmacAuthProvider to your Jersey environment.

Both implementations require specifying (or implementing your own) RequestHandler. There are three RequestHandlers provided for use:

* [DefaultRequestHandler](server/src/main/java/com/bazaarvoice/auth/hmac/server/PassThroughRequestHandler.java) - for general use, requires all requests to include proper authentication
* [OptionalRequestHandler](server/src/main/java/com/bazaarvoice/auth/hmac/server/OptionalRequestHandler.java) - relaxed, does not require authentication, but will authenticate if credentials are provided
* [PassThroughRequestHandler](server/src/main/java/com/bazaarvoice/auth/hmac/server/PassThroughRequestHandler.java) - for testing, simply returns the Principal passed in

Clients

The following clients are available, but note that you can write your own in any language as long as it follows the appropriate contract.

Java Client

To implement a Java client (using Jersey) that constructs requests encoded for HMAC authentication:

(1) Add this maven dependency:

<dependency>
    <groupId>com.bazaarvoice.auth</groupId>
    <artifactId>jersey-hmac-auth-client</artifactId>
    <version>${version}</version>
</dependency>

(2) Add the HmacClientFilter to your Jersey client:

Client client;              // this is your Jersey client constructed someplace else
client.addFilter(new HmacClientFilter(apiKey, secretKey, client.getMessageBodyWorkers()));

Python Client

To implement a Python client that constructs requests encoded for HMAC authentication, please refer to the python-hmac-auth library.