box / box-java-sdk

The Box SDK for Java.
http://opensource.box.com/box-java-sdk/
Apache License 2.0
156 stars 186 forks source link

Authentication using Access and Refresh token #241

Closed bhaswati6 closed 8 years ago

bhaswati6 commented 8 years ago

I am using the steps elaborated in the Authentication resource to authenticate to my instance of Box. One thing however I am not clear as to why my Authentication works just runs once (after I manually get the access and refresh token). Here is my code snippet

//Using Java SDK -- Start--
BoxAPIConnection api = new BoxAPIConnection(CLIENT_ID,
                CLIENT_SECRET, ACCESS_TOKEN, REFRESH_TOKEN);

if(api.needsRefresh()){ // this always come in as true
    apiReal.refresh(); //works the first time I run the program and after that I keep getting error (listed below)
    String refreshToken = api.getRefreshToken();
    String accessToken = api.getAccessToken();

    api = new BoxAPIConnection(CLIENT_ID,
            CLIENT_SECRET, accessToken, refreshToken);
}

Iterable<BoxUser.Info> users = BoxUser.getAllEnterpriseUsers(apiReal);
for(Iterator<BoxUser.Info> i = users.iterator(); i.hasNext(); ) {
    BoxUser.Info item = i.next();
    System.out.println("Name of patron: "+item.getName());
}   
// Using Java SDK -- Finish --

When I run the code for the second time, it fails in the line apiReal.refresh() with the error "Exception in thread "main" com.box.sdk.BoxAPIException: The API returned an error code: 400"

I can get my work done once (even within the validity period of the access token i.e.. 2 hours), but if I have to rerun the codebase again, I need to manually get the auth code, access and refresh token and move ahead. Even when I do not use the apiReal.refresh() (since it's suggested that tokens refreshes automatically), I cannot get updated refresh/access token.

Could you please suggest what am I doing wrong or if I need to solve the issue differently.

Thanks, Bhaswati

dann815 commented 8 years ago

Hi Bhaswati -

Where are you getting the initial tokens for this call?

//Using Java SDK -- Start--
BoxAPIConnection api = new BoxAPIConnection(CLIENT_ID,
                CLIENT_SECRET, ACCESS_TOKEN, REFRESH_TOKEN);

I think you are forgetting to save the new tokens back to that location after you call api.refresh(); For 1 hour, your initial static ACCESS_TOKEN will still be valid. Once you .refresh() then the Refresh Token has been used and will not be valid on your next run.

Also, where is the apiReal object coming from? You should not need to recreate the api object after the refresh()

bhaswati6 commented 8 years ago

Thanks for getting back Dan. So my final codes look like this.

List<ExStudent> exStudentList; // list of ex students from the DB that we need to deprovision from our Box instance.
BoxAPIConnection api = new BoxAPIConnection(CLIENT_ID, CLIENT_SECRET, ACCESS_TOKEN, REFRESH_TOKEN);

for (int i = 0; i< exStudentList.size();i++) {
    Iterable<BoxUser.Info> users = BoxUser.getAllEnterpriseUsers(api,exStudentList.get(i).getLdapid()); // *
    for(Iterator<BoxUser.Info> j = users.iterator(); j.hasNext(); ) {
        BoxUser.Info item = j.next();
        System.out.println("Name of patron: "+item.getName());
        //item.removeEnterprise();   
    }
}

I am getting the AccessToken and RefreshTOken from the Box interface by passing on the AuthCode (similar to how it's done in Postman). What I am trying to do is remove ex students from our Enterprise intance of Box. For that I am using removeEnterprise() method in Box.User class (its not working either and that's a new question). The first time I get the Access and Refresh token and run my code, it works and I am able to get about 150 ex students from our Box instance (the current list of ex students stands at 1003). After that the program stops and I suspect the AccessToken expires. When I run it a second time, I get a 400 error message as shown below (exception is thrown at the line marked with *). Not sure how I should refresh the AccessToken and RefreshToken

Servlet.service() for servlet [dispatcherServlet] in context with path [] threw exception [Request processing failed; nested exception is com.box.sdk.BoxAPIException: The API returned an error code: 400] with root cause
com.box.sdk.BoxAPIException: The API returned an error code: 400
        at com.box.sdk.BoxAPIResponse.<init>(BoxAPIResponse.java:70) ~[box-java-sdk-2.1.1.jar:na]
        at com.box.sdk.BoxJSONResponse.<init>(BoxJSONResponse.java:30) ~[box-java-sdk-2.1.1.jar:na]
        at com.box.sdk.BoxAPIRequest.trySend(BoxAPIRequest.java:423) ~[box-java-sdk-2.1.1.jar:na]
        at com.box.sdk.BoxAPIRequest.send(BoxAPIRequest.java:209) ~[box-java-sdk-2.1.1.jar:na]
        at com.box.sdk.BoxAPIRequest.send(BoxAPIRequest.java:184) ~[box-java-sdk-2.1.1.jar:na]
        at com.box.sdk.BoxAPIConnection.refresh(BoxAPIConnection.java:464) ~[box-java-sdk-2.1.1.jar:na]
        at com.box.sdk.BoxAPIConnection.lockAccessToken(BoxAPIConnection.java:595) ~[box-java-sdk-2.1.1.jar:na]
        at com.box.sdk.BoxAPIRequest.trySend(BoxAPIRequest.java:362) ~[box-java-sdk-2.1.1.jar:na]
        at com.box.sdk.BoxAPIRequest.send(BoxAPIRequest.java:209) ~[box-java-sdk-2.1.1.jar:na]
        at com.box.sdk.BoxAPIRequest.send(BoxAPIRequest.java:184) ~[box-java-sdk-2.1.1.jar:na]
        at com.box.sdk.JSONIterator.loadNextPage(JSONIterator.java:74) ~[box-java-sdk-2.1.1.jar:na]
        at com.box.sdk.JSONIterator.loadNextJsonObject(JSONIterator.java:90) ~[box-java-sdk-2.1.1.jar:na]
        at com.box.sdk.JSONIterator.hasNext(JSONIterator.java:32) ~[box-java-sdk-2.1.1.jar:na]
        at com.box.sdk.BoxUserIterator.hasNext(BoxUserIterator.java:20) ~[box-java-sdk-2.1.1.jar:na]
        at edu.ucmerced.idm.controller.BoxController.removeUser(BoxController.java:78) ~[classes/:na]
        at edu.ucmerced.idm.controller.BoxController.runController(BoxController.java:53) ~[classes/:na]

In addition to remove users from Enterprise instance of Box, should I use the Box.User's removeEnterprise() method? Any suggestion will be highly appreciated. Thanks again Bhaswati

dann815 commented 8 years ago

It's hard to tell from here what's giving you the 400 response but 400 does not mean that you're having token problems.

Is it failing at the same position in your list every time? Are your LdapId objects all properly formatted emails? You may want to URL encode the emails in case there is a '+' or some other special character.

shubham16 commented 8 years ago

Hi Bhaswati,

I am facing the same problem.Did you find any solution to that?

dann815 commented 8 years ago

@shubham16 Could you share your code too?

shubham16 commented 8 years ago

@dann815 Yeah, sure.

import com.box.sdk.BoxAPIConnection; import com.box.sdk.BoxFile; import com.box.sdk.BoxFolder; import com.box.sdk.BoxItem;

import java.io.*;

class BoxTest { public static void main(String ar[]) { getBoxContent(); }

public static void fetchContent(BoxAPIConnection api, BoxFolder rootFolder, String doc_id){
    BoxFile file = new BoxFile(api, doc_id);
    try{
        OutputStream stream = new FileOutputStream("dump.txt");
        file.download(stream);
    }catch(Exception e){
        e.printStackTrace();
    }
}

public static void getBoxContent(){
    BoxAPIConnection api = new BoxAPIConnection(client-id, client-secret, access-token, refresh-token);
BoxFolder rootFolder = BoxFolder.getRootFolder(api);
    for(BoxItem.Info itemInfo : rootFolder){
        System.out.println("Fetching the content of document with id " + itemInfo.getID() + " and storing it in file dump.txt...");
        fetchContent(api, rootFolder, itemInfo.getID());
}
}

}

dann815 commented 8 years ago

@shubham16 What issue are you seeing? This is not all of your code because it doesn't show your token management. Is this the code that you are experiencing an error with? What is the error?

The most common problems I see are in token management because the refresh token can only be used one time. If you initialize your program and it runs successfully once and then not again, look into how you store the refresh token back to DB or flat-file for initialization on the next pass.

shubham16 commented 8 years ago

@dann815 Thanks.

The issue was Box SDK was refreshing my tokens even before 1 hour. I thought that refreshing takes place only when the tokens have expired but auto refresh refreshes the tokens even when they are valid and I was not storing the new tokens anywhere. That is why I was getting confused why my tokens don't work even 10 mins after generating them (because they have already been auto refreshed and rendered invalid).

Is there a neat way to store and access tokens? The best I can think of right now is keeping a txt file and writing and reading the tokens from it.

kendomen commented 8 years ago

Working with Rory Paap, I found that I needed these 2 helper methods for token management

private static BoxAPIConnection getBoxAPIConnection(String client_id, String client_secret, String token, String refresh_token) {

    String state = null;
    try {
        InputStream fis = new FileInputStream("state.txt");
        InputStreamReader isr = new InputStreamReader(fis, Charset.forName("UTF-8"));
        BufferedReader br = new BufferedReader(isr);
        state = br.readLine();
    } catch (Exception e) {
        e.printStackTrace();
    }

    BoxAPIConnection api = null;
    if (null == state || "".equals(state)) {
        api = new BoxAPIConnection(client_id, client_secret, token, refresh_token);
    } else {
        System.out.print("Restoring state..." + state);
        api = BoxAPIConnection.restore(client_id, client_secret, state);
    }
    return api;
}

private static void saveBoxAPIConnect(BoxAPIConnection api) {
    // OK - this was the key!  If you don't have this, you'll get a 400 everything on the first api call
    api.setExpires(3600000L);
    api.setLastRefresh(System.currentTimeMillis());

    String state = api.save();
    try {
        PrintWriter writer = new PrintWriter("state.txt", "UTF-8");
        writer.println(state);
        writer.close();
    }
    catch (Exception e) {
        e.printStackTrace();
    }
}
gcurtis commented 8 years ago

Hi @bhaswati6, were you able to resolve this issue? If not, please feel free to reopen it and we can investigate further.

ahimanshu7 commented 7 years ago

Hello All,

I am getting the below exception.

While debug , found all the values for variables properly set.

Exception in thread "main" com.box.sdk.BoxAPIException: The API returned an error code: 400 at com.box.sdk.BoxAPIResponse.(BoxAPIResponse.java:69) at com.box.sdk.BoxJSONResponse.(BoxJSONResponse.java:30) at com.box.sdk.BoxAPIRequest.trySend(BoxAPIRequest.java:423) at com.box.sdk.BoxAPIRequest.send(BoxAPIRequest.java:209) at com.box.sdk.BoxAPIRequest.send(BoxAPIRequest.java:184) at com.box.sdk.BoxDeveloperEditionAPIConnection.authenticate(BoxDeveloperEditionAPIConnection.java:245) at com.box.sdk.BoxDeveloperEditionAPIConnection.tryRestoreUsingAccessTokenCache(BoxDeveloperEditionAPIConnection.java:305) at com.box.sdk.BoxDeveloperEditionAPIConnection.getAppEnterpriseConnection(BoxDeveloperEditionAPIConnection.java:169) at net.codejava.upload.BoxTry2.main(BoxTry2.java:52)


response "{"error":"invalid_grant","error_description":"OpenSSL unable to verify data: "}" (id=555)

yashdeeph709 commented 7 years ago

Hi @bhaswati6 , I am facing a very different issue. I want to create shared link for all the files in a folder for making it fast I have 5 threads. Now First the program with complete the oauth flow and then it will start iterating through the folder and createShared link and store in a file. First user browses his box account files at my program then he selects one folder and start the procedure of shared link dump. Now when I am doing the normal folder calls and all it was going fine when I have started the thread it said we have a 400:Refresh token expired error. And when thread fails and the application again comes to normal node it again uses the same connection instance and this time it box api serves seemlessly.

    if (selection <= folderNumber && selection > 0) {
        System.out.println("Do you want to dump this folder:(y)");
        if (br.readLine().charAt(0) == 'y') {
            dumpManager(folder, infoList.get(selection - 1));
        }
    } else if (selection > infoList.size()) {
        System.out.println("Errorneos Selection: Please choose a valid selection next time");
        return id;
    }
    return infoList.get(selection - 1);
}

public static void dumpManager(BoxFolder folder, String id) throws IOException, InterruptedException {
    PartialCollection<BoxItem.Info> pc = folder.getChildrenRange(0, 100, "name","shared_link");
    long size=pc.fullSize();
    System.out.println("Total Number of Items in folder "+size);
    int totalThread=1;
    long chunkSize=(size/totalThread);
    System.out.println("Per Thread Will get a chunk of "+chunkSize);
    int threadNumber=1;
    long startRange=0;
    long endRange=chunkSize;
    AuthData auth=new AuthData(boxsession.getAccess_token(), boxsession.getRefresh_token());
    BoxAPIConnection api=new BoxAPIConnection(boxsession.getAPP_KEY(),boxsession.getAPP_SECRET(),boxsession.getAccess_token(),boxsession.getRefresh_token());
    for(;threadNumber<=totalThread;threadNumber++){
        BoxThread thread=new BoxThread(folder.getID(), api);
        thread.setStartRange(startRange);
        thread.setEndRange(endRange);
        thread.setLimit(200);
        startRange+=chunkSize;
        endRange+=chunkSize;
        Thread t=new Thread(thread);
        t.start();
    }
}
yashdeeph709 commented 7 years ago

I got the solution.