Authentication
Overview
Summon Search API uses an authentication scheme based on secret key verification via an HMAC-SHA1 digest. The API performs both authentication and authorization via the same HTTP Authorization header. Creating an Authorization header for an API request requires an access ID, a paired secret key, a client key, and access to certain elements of the HTTP request being authenticated. To create the Authorization header, specific request elements must be assembled into a string that uniquely identifies the request. This ID string is then turned into a digest using the HMAC-SHA1 algorithm as defined in RFC 2104. The digest is then Base64-encoded according to RFC 2045 and assembled into a header along with the access ID.
When the API server receives a request, the first thing it does is check the x-summon-date header to make sure that it is within a reasonable margin of the server time. If the header time is not within one hour of the server time, then authentication fails immediately. If the request timestamp is acceptable, then the server goes on to check that Summon authentication is being used in the Authorization header. If Summon authentication is being used, the server extracts the access ID, client key and encoded authentication digest from the Authorization header, and looks up the paired secret key using the access ID. Finally, the server performs the same algorithm described in the previous paragraph to produce a test digest, and compares the test digest against the digest provided in the Authorization header. If the two digests are identical, the request is authenticated.
Example – A correctly constructed Search API request
GET /2.0.0/search?s.q=forest&s.ff=ContentType,or,1,15 HTTP/1.1
Host: api.summon.serialssolutions.com
Accept: application/xml
x-summon-date: Tue, 30 Jun 2009 12:10:24 GMT
x-summon-session-id: Jp+vWdRgypOOrJQPdzc86mOWFVo=
Authorization: Summon test;rYiYzRaaZ9/QYpiQFZADqpkgJfM=
Assembling The Identification String
The identification string consists of the following components:
- Accept header value
- x-summon-date header value
- Host header value
- Path portion of the resource URI
- Sorted query string unencoded
ID string components are appended to the ID string with a newline (‘\n’) character after each component. The last component has a trailing newline character like all of the other components so that the last character in the ID string is always a newline.
The first component in the request ID string is the content type as it appears in the Accept header. This will be either application/xml or application/json.
The second component is the date string as it appears in the x-summon-date header. The date string must conform to RFC 2616. The time represented by the date string must be the current time. The request will fail to authenticate if the timestamp differs too much from the server time.
The third component is the host as it appears in the Host header. The host will have just the host name.
The fourth component is the path. The path is everything in the URI that comes after the host and before the query string.
The final component in the ID string is a concatenated list of query parameters, unencoded, sorted in alphabetical order, and separated by ‘&’.
Example – Constructing an ID string from a request
The request:
GET /2.0.0/search?q=forest&ff=ContentType,or,1,15 HTTP/1.1
Host: api.summon.serialssolutions.com
Accept: application/xml
x-summon-date: Tue, 30 Jun 2009 12:10:24 GMT
x-summon-session-id: Jp+vWdRgypOOrJQPdzc86mOWFVo=
The constructed ID string:
“application/xml” + “\n” +
“Tue, 30 Jun 2009 12:10:24 GMT” + “\n” +
“api.summon.serialssolutions.com” + “\n” +
“/2.0.0/search” + “\n” +
“ff=ContentType,or,1,15” + “&” + “q=f6orest” + “\n”
Example – Java code for building an ID string
private String computeIdString(String acceptType, String date, String host, String path, Map<String, String[]> queryParameters) { return appendStrings(acceptType, date, host, path, computeSortedQueryString(queryParameters)); } private String computeSortedQueryString(Map<String, String[]> queryParameters) { List<String> parameterStrings = new ArrayList<String>(); // for each parameter, get its key and values for (Map.Entry<String, String[]> entry : queryParameters.entrySet()) { // for each value, create a string in the format key=value for (String value : entry.getValue()) { parameterStrings.add(entry.getKey() + "=" + value); } } // sort the individual parameters Collections.sort(parameterStrings); StringBuilder queryString = new StringBuilder(); // append strings together with the '&' character as a delimiter for (String parameterString : parameterStrings) { queryString.append(parameterString).append("&"); } // remove any final trailing '&' if (queryString.length() > 0) { queryString.setLength(queryString.length() - 1); } return queryString.toString(); } // append the strings together with '\n' as a delimiter public static String appendStrings(String... strings) { StringBuilder stringBuilder = new StringBuilder(); for (String string : strings) { stringBuilder.append(string).append("\n"); } return stringBuilder.toString(); }
Computing the Digest
The digest is computed using the HMAC-SHA1 algorithm as defined in RFC 2104 and then encoded using Base64 encoding as defined in RFC 2045. The ID string must be UTF-8 encoded.
Example – Building a digest from an ID string
The constructed ID string:
“application/xml” + “\n” +
“Tue, 30 Jun 2009 12:10:24 GMT” + “\n” +
“api.summon.serialssolutions.com” + “\n” +
“/2.0.0/search” + “\n” +
“ff=ContentType,or,1,15” + “&” + “q=forest” + “\n”
Hypothetical secret key:
ed2ee2e0-65c1-11de-8a39-0800200c9a66
Computed digest:
3a4+j0Wrrx6LF8X4iwOLDetVOu4=
Example – Java code for building a digest from an ID string
public static String buildDigest(String key, String idString) throws SignatureException { try { String algorithm = "HmacSHA1"; Charset charset = Charset.forName("utf-8"); SecretKeySpec signingKey = new SecretKeySpec(key.getBytes(), algorithm); Mac mac = Mac.getInstance(algorithm); mac.init(signingKey); return new String(Base64.encodeBase64(mac.doFinal(idString.getBytes(charset))), charset); } catch (Exception e) { throw new SignatureException("Failed to generate HMAC : " + e.getMessage()); } }
The Base64 class used in the above code is from Apache Commons.
Assembling the Authorization Header
The Authorization header consists of the following parts in order:
- The authentication scheme identifier — Summon
- The access ID — (i.e.; the client shortname – <shortname>.summon.serialssolutions.com)
- The HMAC-SHA1 digest
The authentication scheme identifier “Summon” must be followed by a space, and the access ID and the digest must be separate by a semicolon (with no space).
Example – Building an Authorization header
Access ID:
test
HMAC-SHA1 Digest:
rYiYzRaaZ9/QYpiQFZADqpkgJfM=
Formatted as an Authorization header string:
Summon test;rYiYzRaaZ9/QYpiQFZADqpkgJfM=
If a given access ID has access to multiple client keys, then an additional client key component must be added to the Authorization header. The client key comes after the access ID and before the HMAC-SHA1 digest. Like the access ID, it is separated by a semicolon and no space.