This recipe describes how to connect a KRL application to Dropbox using the OAuth 1.0a protocol that Dropbox supports. This recipe should be readily adaptable to other APIs using OAuth 1.0a and will offer hints at how to do OAuth 2, which is considerably easier to support.
We will be using the PLAINTEXT signature method in OAuth 1.0a which means that this is only secure over SSL. Since Dropbox uses SSL for all API interactions, this won't be a problem.
Registering Our Application with Dropbox
The first step is to register our application with Dropbox and get our App key and App secret. Go to the Dropbox Developer Home and click on the App Console menu item on the left. Click the "Create an app" button and fill in the fields. We'll be creating a "Core" application. The Core API is what allows programatic access to the linked Dropbox account. You'll be asked whether you want full access to the user's Dropbox or just a single directory in the /Apps
directory. For purposes of this recipe, I chose the a single directory. At this point, you should have an App key and App secret from Dropbox. You'll put these in the Keys section of the meta directory of your ruleset:
key dropbox { "app_key" : "<dropbox app key>", "app_secret" : "<dropbox app secret>" }
Creating the OAuth Header Value
We'll have to create an OAuth Authorization
header for various interactions with the Dropbox server. This is a complicated string that is better to create using a function than by hand each time it's needed. For getting the request token, the header is simpler and looks like this:
Authorization: OAuth oauth_version="1.0", oauth_signature_method="PLAINTEXT", oauth_consumer_key="<app-key>", oauth_signature="<app-secret>&"
For getting the access token and interactions with the API, we need to add the token and token secret like so:
Authorization: OAuth oauth_version="1.0", oauth_signature_method="PLAINTEXT", oauth_consumer_key="<app-key>", oauth_token="<request-token>", oauth_signature="<app-secret>&<request-token-secret>"
This function will create both. If the function is given just two parameters, then it creates the first Authorization
header, if given four, it creates the second Authentication header shown above:
create_oauth_header_value = function(key, key_secret, token, token_secret) { 'OAuth oauth_version="1.0", oauth_signature_method="PLAINTEXT", oauth_consumer_key="'+ key + (token => '", oauth_token="'+token+'", ' | '", ') + 'oauth_signature="' + key_secret + '&' + token_secret + '"'; }
We'll use this function often in the step below.
Get a Request Token
The Dropbox API provides an endpoint for getting request tokens:
POST https://api.dropbox.com/1/oauth/request_token
This has to be made with the Authorization
header set to the correct value using the App key and App secret.
To get and process the request token, we'll use a pair of functions. The first will call the request_token endpoint in the Dropbox API. The second will process the response.
Getting the access token requires POSTing to the endpoint with the correct Authorization header. The following rule, which is selected when the user launches the application we're writing inside CloudOS and fires when the application is not authorized to access Dropbox does that:
rule get_access_token { select when oauth response if(not authorized) then { http:post(dropbox_base_url+"/oauth/request_token") with body = {} and headers = {"Authorization" : create_oauth_header_value(keys:dropbox('app_key'), keys:dropbox('app_secret')) } and autoraise = "request_token" } }
The POST has no body, uses the function we defined above for creating the Authorization
header, and automatically raises a new event with the result of the POST which we expect to have the request token and request token secret.
When the http:post()
action autoraises an event, the event has event domain http
and event type post
. The value of the autoraise
parameter in the action above will be in the event attribute label
so that we can write a rule that responds specifically to this autoraise. The following rule is selected by that autoraised event, creates a UI panel with a "Click to Link Dropbox" button, and saves the request token and request token secret in entity variables for later use.
rule process_request_token { select when http post label "request_token" pre { tokens = decode_content(event:attr('content')); callback = 'http://' + meta:host() + '/blue/event/oauth/response/' + meta:rid() + '/' + math:random(999999); url = "https://www.dropbox.com/1/oauth/authorize?oauth_token=" + tokens{'oauth_token'} + "&oauth_callback=" + callback; my_html = << <div style="margin: 0px 0px 20px 20px"> <a href="#{url}" class="btn btn-large btn-primary">Click to Link to Dropbox<a> </div> >>; } CloudRain:createLoadPanel("Link to Dropbox", {}, my_html); always { set ent:request_token_secret tokens{'oauth_token_secret'}; set ent:request_token tokens{'oauth_token'}; } }
The tokens are in the event attribute content and are URL encoded. You can see how decode_content()
works in another recipe. The callback is the event we want Dropbox to raise when it responds with the access token. The event is an oauth:response
event. We take pains using meta:host()
and meta:rid()
to ensure that this code is portable between KRE instances and rulesets. The form of the URL that calls Dropbox is described in their documentation.