### Optaining an Authentication Token Construct a URL to a page on the service providers site where the user you're authenticating on behalf of can grant persmission. Client libraries will need to allow the authentication request end point to be configurable; clients must construct the URL to include an oauth_key GET parameter, and should provide a mechanism to add additional application-specific parameters (e.g., a callback url, or permissions / scope) Assuming permission is granted the service provider will create a one-time token which can be exchanged for an Oauth authentication token (oauth_token). The token is sent back to the client by way of a redirect to a callback URL (ideally the callback url is pre-configured), with a GET parameter oauth_temporary_token. The client then makes a SIGNED REQUEST (see below) to a second endpoint, to which the server will respond with a UTF-8 XML version of the valid token that is re-usable for subsequent requests. e.g.: 0A3BB4C If the request is not successful, the server should respond with 401 Not Authorized, and maybe include some further XML-encased details about why the request wasn't authorized. Open questions: - validity of verified tokens ### How to Sign an API Call All API calls using Oauth authentication tokens need to be signed. API call signing is performed using several ingredients, and a common normalization algorithm. To make an authenticated, signed Oauth call you'll need the following: - oauth_key: an API key. This is an abritrary application specific identifier. - an Oauth secret: a shared secret associated with an API key. The secret is not passed with the API call, but is used in the signing - oauth_token: an Oauth authentication token. See "Obtaining an Authentication Token" - the requested resource: the relative URL of the request - the normalized request parameters: (see "How to Normalize Request parameters") - a nonce (see "Nonce, WTF?" and "Generating a Nonce") - a timestamp. an integer representing the time the request was sent, expressed in number of seconds after January 1, 1970 00:00:00 GMT. - oauth_sigal: a hashing algorithm that the client and the server agree to use. Servers and clients should reasonable expect the other to implement common algorithms such as md5 and sha1, and might additionally require more require options such as HMAC-SHA1, HMAC-SHA256, or RSA depending on the application's needs. Each authenticated request contains the following Oauth specific parameters in addition to any application specific ones - oauth_key, oauth_token, oauth_sig, oauth_sigalg, oauth_nonce, oauth_ts ### How to Normalize Request parameters 1. Sort the request parameters alphabetically by parameter name: e.g., page=3, count=50 becomes count=50, page=3 2. Concatenate the name-value pairs: e.g., count50 and page3 3. Concatenate all the resulting parameter strings: e.g., count50page3 Data should remain in whatever encoding and format it will be in when received by the server. (This is the part where we skip questions about encoding) in Ruby, you can do the above with the following code (assuming a Hash of request parameters named request_params): request_params.sort { |a,b| a[0].to_s <=> b[0].to_s }.map { |v| v.join('') }.join('') The equivalent in PHP is as follows, assuming an Array of request parameters named $args: ksort($args); $normalized = ''; foreach ($args as $k => $v){ $normalized .= $k . $v; } Note you'll need to make sure to remove the oauth_* parameters from the $args/request_params hashes. ### How to generate a Nonce (what's a Nonce?) A nonce is just a random value that allows us to verify that this request has never been made before. You can read more about nonces over at wikipedia, but in the meantime all you need to know is that it's a random value. We suggest generating a random 64-bit integer. In most languages (?) you can do: nonce = rand((2**64)-1) ### An Example Fetch the 3rd page of friends updates from Twitter for user 123456 The basic API request looks like so: http://twitter.com/statuses/friends/123456.json?page=3&count=50 If we wanted to make an authenticated Oauth call against this API for a user for whom we had obtained an authentication token we first need to caculate the oauth_sig. An oauth_sig is defined as oauth_sig=oath_sig_alg(secret SP api_key SP token SP resource SP normalized_request_parameters SP nonce SP timestamp) SP is just a space. API key and secret are arbitrary values issued by the application being called. But they are explicitly tied to the authentication token. (see above) Assuming your: API key is: 0685bd91 Shared secret is: 3a2cd35 And Oauth token is: 540ad18 We calculate the oauth_sig as: oauth_sig=oauth_sig_alg("3a2cd35 0685bd91 540ad18 /statuses/friends/123456.json count50page3 MTgzNTYxODk4Mw 1181537927") We need to pass to the server (in cleartext) the API key (oauth_key), the Token (oauth_token), the timestamp (oauth_ts), the nonce (oauth_nonce), the signing algorithm (oauth_sigalg), and the signed request (oauth_sig). Clients MUST NOT send the shared secret with the request, since doing so would compromise the entire request, as well as subsequent requests. So the Oauthed API request is now: http://twitter.com/statuses/friends/123456.json?page=3&count=50&oauth_key=0685bd91&oauth_token=540ad18&oauth_nonce=MTgzNTYxODk4Mw&oauth_ts=1181537927&oauth_sigalg=sha1&oauth_sig=7f762ca98931f60715c5452b09a9b56914d68568 ### Why Signing? Signing your API calls will keep attackers from being able to impersonate users, replay API calls, or hijack sessions. ### Implementation Suggestions Timestamps should be good for 5 minutes, this gives sufficient time to deal with network latency, without becoming a security hole, or placing an unbearable burden on the server for tracking and storing nonces. Ideally servers would provide a method for retrieving what they think the current time is. # Future / Open Questions - Signing with SSL certificates should be possible - Error messages & HTTP response codes - HTTP Headers - How to sign POSTs / PUTs etc. - Expiration of tokens, provisioning of API keys and secrets are beyond the scope. As are application specific permissions. # Error codes (and they all need HTTP codes and XML stanzas) - Invalid key - Invalid token - Invalid sig - Unsupported signing algorithm (the suggested / basic one should be UNIVERSALLY IMPLEMENTED and provide an ADEQUATE level of security given the threat model) - Token expired - Bad nonce - Bad timestamp /// scratch A Timestamp that matches (give or take 5 minutes) with the server API call signing is performed using an application specific API key, shared secret, and authentication token, plus a common normalization algorithm.