Skip to content

How it Works

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

What is HMAC?

Hash-based message authentication code (HMAC) is a mechanism for calculating a signature by using a hash function in combination with a secret key. This can be used to verify the integrity and authenticity of a message.

The way it works is like this. A web service restricts API access by giving each API caller an API key and a secret key. Clients construct each request by providing certain parameters, including a signature generated using the secret key and some parts of the request itself. When the server receives the request, it recalculates the signature using the same method as the client and compares it to the one passed by the client. If they match, then the request is considered authentic and processing continues. Otherwise, the request is rejected as unauthorized.

Because the secret key is not passed across the "wire", the signatures will only match if the caller has a valid secret key. Also, since the signature is generated using various parts of the request itself, the signatures will not match if those aspects of the request were tampered with during transmission or if the request is modified and retransmitted for malicious purposes. If the signatures match, then the client is considered to be a trusted source and the request is deemed authentic.

Client/Server Protocol

The following is the protocol that jersey-hmac-auth implements for authenticating requests:

  1. The client is granted an API key and secret key.

When a new API caller needs access to your API, you generate new keys (however you like) and provide them to the caller. They will then use these when generating requests to the API.

  1. The client generates a request as follows:
GET /pizza?apiKey=my-api-key HTTP/1.1
X-Auth-Version: 1
X-Auth-Timestamp: 2014-02-10T06:13:15.402Z
X-Auth-Signature: yrVWPUAPAlV0sgAh22MYU-zR5unaoTrNTaTl11XjoMs=

The API key is passed on the request as a query parameter. This will be used by the server to identify the caller.

The secret key is not passed directly, since it is intended to be a secret that only the client and server know. Instead, it is used when generating the signature. The server will try to regenerate the signature using the secret key that it stores locally for this particular client and will authenticate the request by making sure that the signatures match.

The signature is passed on the X-Auth-Signature header and is generated by using the following algorithm (which is demonstrated here in pseudo-code):

method = {HTTP request method - e.g. GET, PUT, POST}
timestamp = {the current UTC timestamp in ISO8601 format}
path = {the request path including all query parameters - e.g. "/pizza?apiKey=my-api-key"}
content = {the content in the request body, if any is specified on the request}

data = {method + '\n' + timestamp + '\n' + path}
if content:
    data += {'\n' + content}
digest = hmac(secretKey, data.encode('utf-8'), sha256).digest()
return base64.urlsafe_b64encode(digest).strip()
  1. The server receives and authenticates the request.

The server uses the API key to identify the caller and retrieve their secret key from wherever it stores it. It then generates a signature in the exact same way that the client did when building the request, and compares the signature to the one passed by the client. If the signatures match, then the request is valid and the server can proceed. Otherwise, the server returns a "401 (Unauthorized)" HTTP status code.