Tuesday 17 September 2013

CONNECTING TO BOX API VERSION 2 USING OAUTH 2

CONNECTING TO BOX API VERSION 2 USING OAUTH 2
Box is a cloud storage provider that has an API for creating integrations.  Box provides its own AppExchange product for connecting custom and standard objects to files storage at Box.  Look for a whitepaper coming out soon that explains why there may be times when it is the best choice to store your files outside of Salesforce.
Here are the steps for connecting to Box’s REST API.  In my reading I learned that there was a direct connection option with a username and password with version 1 of the API, but with version 2 of the API you must use OAuth 2 to authenticate. 
1) Sign-up for a free personal Box account by going to this page and clicking on the ‘Sign Up’ button.
2) During the sign-up process an email will be sent to you and you will have to click on it to confirm the email account.
3) Log into your new Box account.
4) Go to this page and click on ‘Get Started Now’.
5) Go to this page and click on the ‘register for an API key’ link.
    a. Enter a new name for the application like ‘TestAPISundog’
    b. You will now receive your new API Key.  Copy that to a safe place.
6) We need to allow Salesforce to make outbound web service calls to Box.
    a. Log into Salesforce
    b. Click on Your Name, Setup
    c. Go to Administration Setup, Security Controls, Remote Site Settings
          i. Click on New Remote Site
          ii. Enter a name and the URL should be
 https://www.Box.com
          iii. Create another Remote site for
 https://api.Box.com
7) This page walks through how to connect to Box and get the OAuth code.
    a. Note that the only supported production authentication method with version 2 of the Box API seems to be OAuth 2.
    b. If you do not want to use OAuth 2 then this
 page gives some other options with using version one of the API.
    c. This
 page walks through how to verify the OAuth credentials using Postman.
    d. This
 page walks through all the steps with OAuth 2 for Box from authentication, to getting the access token, to making your first API call.
8) Now we are ready to try to connect to our Box account via the Version 2 of the API using OAuth 2.
9) Log into Salesforce
10) Create a Controller called BoxConnect.
  See code at the bottom of this post for the APEX code for that Controller…
  Again I tried to use as little code as possible to make the important pieces obvious.  A lot more needs to be done to this code before it is production ready such as responding to errors from Box.
  This code tries to do three things
    a. Redirect to Box so that the user can enter his/her credentials
          This is done in the boxConnect() method where a PageReference does this
          This code is hit when the CommandButton is clicked on the Visualforce Page
    b. Call Box to request an Access Token
          This is done in getBoxToken()
c. Use the Access Token to make any API calls
        The example API call here is in getBoxFolder() and it lists all of the files in the specified folder.
11) Create a Visualforce page called BoxConnect
      See code at the bottom of this post for the markup for the Visualforce Page…
12) To run this code add apex/BoxConnect to the end of your Salesforce URL
Upload Process Start
13) Click on the ‘Connect to Box’ CommandButton on the Visualforce Page
    a. This will redirect to Box.  Enter the Box credentials
 
Box Account Access
Box Allow Access

        This is done in the boxConnect() method where a PageReference does this
14) Fill in you username and password at Box
    a. Box will automatically send the page back to your Visualforce page because of redirect_uri parameter in the call to authorize via OAuth 2. 
 
    b. Note that the redirect_uri parameter is optional as it can be setup in the app within Box too.  If you do send it and it is not blank at Box, then it must match.
    c. You may have to change your URL from c.na9.visual.force.com to whatever your URL is.
15) Now Box will have redirected back to your page.  Now all of the action takes place in the Controller.
    a. In the constructor we check if the ‘code’ query string parameter is there.  If it is then we grab it and then try to ask for the access token.
    b. The access token code is in getBoxToken().  A HTTP POST call is made with all the needed items in the body.  Make sure you put them in the body and not as headers or query string parameters.  The values must be urlEndcoded and the ‘Content-Type’ and ‘charset’ values are also required.
    c. We now get back a JSON response that we can parse to get our access and refresh tokens.
    d. Now that we have our access token we can make any calls that we want to the API.
    e. The call in the code within getBoxFolder() is to
https://api.box.com/2.0/folders/FOLDER_ID/items, which will return all of the items, files and folders in this folder.  The only tricky part to this one is to get the Authorization header set correctly.
    f.       We again get a JSON value back which can be parsed using the built-in JSON parser in APEX.
    g. We now have the files that were in that specific folder.
         
Box Folder Response


16) Now that we have the code to A) Authenticate B) Get the Access Token and C) Make our first API call, we are now able to make any API calls that we need.
17) More code will have to be written to get a new access code using the refresh token as the access token is only good for 1 hour.  The refresh token also has a certain lifetime so if that is up, then the user will have to authenticate at Box again from the beginning of the OAuth 2.0 process.
APEX Controller Code…

public with sharing class BoxConnect {

    public string valueToShow{get;set;}
    private final string boxClientID ='myBoxClientID';
    private final string boxSecretCode ='myBoxSecretCode';
    private final string redirectURI ='https://c.na9.visual.force.com/apex/boxconnect';
    private string codeFromBox;
    private string accessToken;
    private string refreshToken;
     
    public BoxConnect(){
        valueToShow ='Start';
        //Check if we received the authorization code from Box because the redirectUI
        // URI points to this page.  It will look like this…
        codeFromBox = System.currentPageReference().getParameters().get('code');
        if(codeFromBox == null || codeFromBox ==''){
             
        }else{
            //Try to get a token and then make an API call to Box
            getBoxToken();         
        }
    }
     
    public pageReference boxConnect(){
        //Redirect to the OAuth page at Box so the login credentials can be entered.       
        PageReference pr = new PageReference('https://www.box.com/api/oauth2/authorize?' +
            'response_type=code' +
            '&client_id=' + boxClientID +
            '&redirect_uri=' + redirectURI);
        return pr;
    }  
     
    private void getBoxToken(){
    Http h = new Http();
        HttpRequest req = new HttpRequest();
        string endPointValue ='https://www.box.com/api/oauth2/token';
        req.setEndpoint(endPointValue);
        req.setBody('Content-Type=' + EncodingUtil.urlEncode('application/x-www-form-urlencoded', 'UTF-8') +
                    '&charset=' + EncodingUtil.urlEncode('UTF-8', 'UTF-8') +
                    '&grant_type=' + EncodingUtil.urlEncode('authorization_code', 'UTF-8') +
                    '&code=' + EncodingUtil.urlEncode(codeFromBox, 'UTF-8') +
                    '&client_id=' + EncodingUtil.urlEncode(boxClientID, 'UTF-8') +
                    '&client_secret=' + EncodingUtil.urlEncode(boxSecretCode, 'UTF-8') +
                    '&redirect_uri=' + EncodingUtil.urlEncode(redirectURI, 'UTF-8'));
        req.setMethod('POST');
        HttpResponse res = h.send(req);
        //Get back this… {"access_token":"RETURNED_ACCESS_TOKEN",
        //                  "expires_in":3600,
        //                  "refresh_token":"RETURNED_REFRESH_TOKEN",
        //                  "token_type":"bearer"}
        parseAuthJSON(res.getBody());
        if(accessToken != null & accessToken !=''){
            //Try to get items from a folder in Box
            getBoxFolder();
        }else{
            //Just some debug lines to see the request and response
            valuetoShow ='Get Authorization Code Return: ' + res.getBody() + ' end point value: ' + endPointValue + 'request: ' +
                req.toString() +'request headers: ' + req.getHeader('Content-Type') + '; ' + req.getHeader('charset') +
                'request body: ' + req.getBody();
        }      
    }
     
    private void getBoxFolder(){
        Http h = new Http();
        HttpRequest req = new HttpRequest();
        string endPointValue ='https://api.box.com/2.0/folders/904543862/items'; //?limit=5&offset=0
        //Returns this: {"total_count":2,"entries":[
        //    {"type":"file","id":"8308841816","sequence_id":"0","etag":"0","sha1":"6dce26ad1a3578a00a40722ec4472abc143313cf",
        //      "name":"Luschen Quote for West Fargo Property.pdf"},
        //    {"type":"file","id":"8308878690","sequence_id":"1","etag":"1","sha1":"5b595457a86396ddf45318d1ab9ddd6aa3b7bc1e",
        //      "name":"New Google Doc 1.gdoc"}
        //    ],"offset":0,"limit":100,"order":[{"by":"type","direction":"ASC"},{"by":"name","direction":"ASC"}]}
         
        //This one works to get the properties for a specific file     
        //endPointValue ='https://api.box.com/2.0/files/8308878690?fields=modified_at,path_collection,name';
        //Returns this {"type":"file","id":"8308878690","etag":"1","modified_at":"2013-05-25T16:08:52-07:00",
        //   "path_collection":{"total_count":2,"entries":[
        //    {"type":"folder","id":"0","sequence_id":null,"etag":null,"name":"All Files"},
        //    {"type":"folder","id":"904543862","sequence_id":"1","etag":"1","name":"Buy Fargo Home"}
        //    ]},"name":"New Google Doc 1.gdoc"}
        req.setEndpoint(endPointValue);
        req.setHeader('Authorization', 'Bearer ' + accessToken);
        req.setMethod('GET');
        HttpResponse res = h.send(req);
        //Now we could parse through the JSON again and get the values that we want
        valuetoShow ='Get Folder: ' + res.getBody();  
    }
     
    private void parseAuthJSON(string JSONValue){
        JSONParser parser = JSON.createParser(JSONValue);
        accessToken ='';
        refreshToken ='';
        while (parser.nextToken() != null) {
            if(parser.getCurrentToken() == JSONToken.FIELD_NAME){
                if(parser.getText() =='access_token'){
                    parser.nextToken();
                    accessToken = parser.getText();
                }
                if(parser.getText() =='refresh_token'){
                    parser.nextToken();
                    refreshToken = parser.getText();
                }
            }
            if(accessToken !='' && refreshToken != ''){
                break;
            }
        }
    }
     
}
Visualforce Code…

<apex:page showheader="false" sidebar="false" cache="false" contenttype="text/html" controller="BoxConnect">
    <apex:form>
        <apex:commandbutton id="connectToBox" value="Connect to Box" action="{!boxConnect}">
        </apex:commandbutton>
        <apex:outputtext id="outputText" value="{!valueToShow}">
        </apex:outputtext>
    </apex:form>
</apex:page>


No comments:

Post a Comment