Skip to content

Instantly share code, notes, and snippets.

@rcravens
Created July 28, 2012 15:38
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save rcravens/3193766 to your computer and use it in GitHub Desktop.
Save rcravens/3193766 to your computer and use it in GitHub Desktop.
HTTP REST-like API Design Notes
Notes:
1. API Keys: private, public
- Every user will be issued a API keys (public and private) that can be used to access the API on their behalf. Requests via the API keys have the same priviledges as requests by the user.
2. API implements a HTTP based REST-like interface
- Root URL (e.g. https://example.com/api/1)
- HTTP Verbs:
* Create - POST
* Read - GET
* Update - PUT
* Delete - DELETE
- Each request is complete on its own. All necessary data is contained in the request. Sessions and such are not available.
3. Authentication uses the HTTP 'Authorization' header
- Scheme based on a keyed-HMAC (Hash Message Authentication Code) for authentication
- Uses HTTP Authorization header with the following format:
Authorization: authstring
- Requires the use of SSL for securing the authstring token which is transmitted as clear text.
- Client-side Processing
* create the authstring following the protocol described below
* use private key to calculate the HMAC of the string
* the HMAC process is called 'signing the request'
* the output of the HMAC algorithm is called the 'signature'
* HMAC algorithm is RFC2104HMAC-SHA1 so selected elements
authstring = APP_NAME + ":" + public + ":" + signature
APP_NAME: This is an additional parameter to ID the application.
public: The
signature = Base64(
HMAC-SHA1(
secret,
UTF-8-Encoding( jsonString )
)
)
jsonString = String in JSON format. JSON libraries exist in most languages. This format allows the signature to be extended to include additional data in the future. Here is an example with the currently supported variables:
jsonString = '{
"API-Version":1,
"HTTP-Verb":"POST",
"Date":1343484659,
"Output":"JSON"
}'
API-Version: The API version being used. This is mostly for confirmation. The version is in the API Root URL.
HTTP-Verb: The HTTP-Verb parameter is used for the request method. This parameter overrides the HTTP request method. It is recommended that the HTTP-Verb parameter matches the HTTP request method. However, the HTTP-Verb method allows access to the REST-like API via languages that don't support all the HTTP verbs.
Date: Date is required and represents the request time (UTC). The format of the data is epoch time (number of seconds that have elapsed since January 1, 1970 0:0:0 UTC). The server will only process requests within 15 minutes of the server time. Other requests will return a 'request time too skewed' error. This helps mitigate 'replay' of intercepted requests. For stronger protection use HTTPS transport.
Output: Response data can be returned in a variety of supported formats. Currently the only support format is JSON, but this variable will allow other formats to be supported.
- Server-side Processing
* missing Authorization header
- returns HTTP 401 Unauthorized
* the authString is split yielding: APP_NAME, public, signature
- if not correct format or missig, return HTTP 400 Bad Request
* the public key is used to fetch the associated private kye
- if fetch fails, return HTTP 403 Forbidden
* the signature is decrypted yielding jsonString
- if decryption fails, return HTTP 403 Forbidden
* jsonString.Date is used to help mitigate 'replay' of intercepted requests in the case the SSL has failed?
* jsonString.HTTP-Verb is used to override (or confirm) the HTTP verb and determine the final routing.
- if the route/resource doesn't exist, return HTTP 404 Not Found
* jsonString.Output is used to shape the result into the expected format (more on API responses protocol below).
* the shaped result is returned with the HTTP 200 OK status code.
5. API responses will always be of the following format (JSON used in these examples):
{
"success":true,
"message":"A user-friendly message. Can be an empty string. This is used to provide additional info from the server.",
"payload":{
*** JSON formatted data ***
}
}
4. Examples
Create a user
-----------------------------------
Signature Data
jsonString = '{
"API-Version":1,
"HTTP-Verb":"POST",
"Date":1343484659,
"Output":"JSON"
}'
public = "1234"
signature of jsonString = "ABCD" (not actual, but simplified)
HTTP Request Headers
POST /api/1/user/ HTTP/1.1
Host: example.com
Authorization: APP_NAME:1234:ABCD
*** Note that there may be other HTTP headers in the request ***
HTTP Request Payload
{
"fname": "Bob",
"lname": "Cravens",
"email": "bcravens@example.com"
}
HTTP Response Headers
HTTP/1.0 200 OK
Content-Type: application/json
*** Note that there may be other HTTP headers in the response ***
HTTP Response
{
"success": true,
"message": "",
"payload": {
"id": 7
"fname": "Bob",
"lname": "Cravens",
"email": "bcravens@example.com"
}
}
Read a user
-----------------------------------
Signature Data
jsonString = '{
"API-Version":1,
"HTTP-Verb":"GET",
"Date":1343484799,
"Output":"JSON"
}'
public = "1234"
signature of jsonString = "ABCD" (not actual, but simplified)
HTTP Request Headers
GET /api/1/user/7 HTTP/1.1
Host: example.com
Authorization: APP_NAME:1234:ABCD
*** Note that there may be other HTTP headers in the request ***
HTTP Request Payload
*** None ***
HTTP Response Headers
HTTP/1.0 200 OK
Content-Type: application/json
*** Note that there may be other HTTP headers in the response ***
HTTP Response Payload
{
"success": true,
"message": "",
"payload": {
"id": 7,
"fname": "Bob",
"lname": "Cravens",
"email": "bcravens@example.com"
}
}
Update a user
-----------------------------------
Signature Data
jsonString = '{
"API-Version":1,
"HTTP-Verb":"PUT",
"Date":1343499659,
"Output":"JSON"
}'
public = "1234"
signature of jsonString = "ABCD" (not actual, but simplified)
HTTP Request Headers
PUT /api/1/user/7 HTTP/1.1
Host: example.com
Authorization: APP_NAME:1234:ABCD
*** Note that there may be other HTTP headers in the request ***
HTTP Request Payload
{
"id": 7,
"fname": "Bob",
"lname": "Cravens",
"email": "new_email@example.com"
}
HTTP Response Headers
HTTP/1.0 200 OK
Content-Type: application/json
*** Note that there may be other HTTP headers in the response ***
HTTP Response
{
"success": true,
"message": "",
"payload": {
"id": 7
"fname": "Bob",
"lname": "Cravens",
"email": "new_email@example.com"
}
}
Delete a user
-----------------------------------
Signature Data
jsonString = '{
"API-Version":1,
"HTTP-Verb":"PUT",
"Date":1343499779,
"Output":"JSON"
}'
public = "1234"
signature of jsonString = "ABCD" (not actual, but simplified)
HTTP Request Headers
DELETE /api/1/user/7 HTTP/1.1
Host: example.com
Authorization: APP_NAME:1234:ABCD
*** Note that there may be other HTTP headers in the request ***
HTTP Request Payload
*** None ***
HTTP Response Headers
HTTP/1.0 200 OK
Content-Type: application/json
*** Note that there may be other HTTP headers in the response ***
HTTP Response
{
"success": true,
"message": "",
"payload": {
"id": 7
"fname": "Bob",
"lname": "Cravens",
"email": "new_email@example.com"
}
}
Missing Authorization header
-----------------------------------
HTTP Request Headers
GET /api/1/user/7 HTTP/1.1
Host: example.com
*** Note that there may be other HTTP headers in the request ***
HTTP Request Payload
*** None ***
HTTP Response Headers
HTTP/1.0 401 Unauthorized
Content-Type: application/json
*** Note that there may be other HTTP headers in the response ***
HTTP Response
{
"success": false,
"message": "This request requires authentication.",
"payload": ""
}
Malformed authentication header
-----------------------------------
HTTP Request Headers
GET /api/1/user/7 HTTP/1.1
Host: example.com
Authorization: not_propert_format
*** Note that there may be other HTTP headers in the request ***
HTTP Request Payload
*** None ***
HTTP Response Headers
HTTP/1.0 400 Bad Request
Content-Type: application/json
*** Note that there may be other HTTP headers in the response ***
HTTP Response
{
"success": false,
"message": "This request requires authentication.",
"payload": ""
}
Invalid private key
-----------------------------------
HTTP Request Headers
GET /api/1/user/7 HTTP/1.1
Host: example.com
Authorization: APP_NAME:9876:ABCD
*** Note that there may be other HTTP headers in the request ***
HTTP Request Payload
*** None ***
HTTP Response Headers
HTTP/1.0 403 Forbidden
Content-Type: application/json
*** Note that there may be other HTTP headers in the response ***
HTTP Response
{
"success": false,
"message": "This request requires authentication.",
"payload": "" }
Date is out of bounds
-----------------------------------
Signature Data
jsonString = '{
"API-Version":1,
"HTTP-Verb":"PUT",
"Date":000000000000,
"Output":"JSON"
}'
public = "1234"
signature of jsonString = "ABCD" (not actual, but simplified)
HTTP Request Headers
DELETE /api/1/user/7 HTTP/1.1
Host: example.com
Authorization: APP_NAME:1234:ABCD
*** Note that there may be other HTTP headers in the request ***
HTTP Request Payload
*** None ***
HTTP Response Headers
HTTP/1.0 412 Precondition Failed
Content-Type: application/json
*** Note that there may be other HTTP headers in the response ***
HTTP Response
{
"success": false,
"message": "This request fails to meet all security conditions.",
"payload": ""
}
Resource not found (e.g. URL not valid)
-----------------------------------
Signature Data
jsonString = '{
"API-Version":1,
"HTTP-Verb":"PUT",
"Date":1343499779,
"Output":"JSON"
}'
public = "1234"
signature of jsonString = "ABCD" (not actual, but simplified)
HTTP Request Headers
GET /api/1/books/7 HTTP/1.1
Host: example.com
Authorization: APP_NAME:1234:ABCD
*** Note that there may be other HTTP headers in the request ***
HTTP Request Payload
*** None ***
HTTP Response Headers
HTTP/1.0 404 Not Found
Content-Type: application/json
*** Note that there may be other HTTP headers in the response ***
HTTP Response
{
"success": false,
"message": "Resource not found.",
"payload": ""
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment