Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

What is the best practice to switch accounts without re login? #367

Open
fiasko131 opened this issue Jun 13, 2018 · 13 comments
Open

What is the best practice to switch accounts without re login? #367

fiasko131 opened this issue Jun 13, 2018 · 13 comments
Labels
documentation improvement or addition needed to documentation enhancement

Comments

@fiasko131
Copy link

fiasko131 commented Jun 13, 2018

Hello,
My application uses multiple cloud providers and for each provider uses multiple accounts.
Once I create an account I store the accessToken and the refreshtoken etc ... in database.
Then when I want to reconnect or change account here is how I proceed:

    // inition of BoxConfig
    BoxConfig.IS_LOG_ENABLED = false; //<------???
    BoxConfig.CLIENT_ID = BOX_CLIENT_ID;
    BoxConfig.CLIENT_SECRET = BOX_CLIENT_SECRET;
    BoxConfig.REDIRECT_URL = "https://localhost";

                                                   
    mBoxSession = new BoxSession(getActivity(),null);
    // here i set the stored accessToken and refreshtoken for a given account
    mBoxSession.getAuthInfo().setAccessToken(item.accessToken);
    mBoxSession.getAuthInfo().setRefreshToken(item.refreshToken);
													
    mBoxSession.authenticate().addOnCompletedListener(new 
    BoxFutureTask.OnCompletedListener<BoxSession>() {
               @Override
               public void onCompleted(BoxResponse<BoxSession> response) {
                                                     
                  if (response.isSuccess()) {
                      // if success i do my stuff
                  } else {
		      // if not i call onRefreshed 
                    onRefreshed(mBoxSession.getAuthInfo());
                                                             
                  }
              }
    });			                                                									
													
    @Override
    public void onRefreshed(final BoxAuthentication.BoxAuthenticationInfo info) {
	// here i set the new accessToken and refreshToken
        currentBoxAccessToken = info.accessToken();
        currentBoxRefreshToken = info.refreshToken();
        mBoxSession.getAuthInfo().setAccessToken(currentBoxAccessToken);
        mBoxSession.getAuthInfo().setRefreshToken(currentBoxRefreshToken);
        mFolderApi = null;
        mFileApi = null;
       // and i store the new access and refresh token
       // and finaly i do my previous stuff
    }

But that does not prevent sometimes that the view of login appears ???

@doncung
Copy link
Contributor

doncung commented Jun 13, 2018

If you do not want the login activity to ever appear you will want to set an AuthenticationRefreshProvider either globally in BoxAuthentication or locally in your session. Then you can decide what to do when launchAuthUi is called.

If you are still using our login UI for the beginning login and to handle log out situations you can provide an AuthStorage implementation in BoxAuthentication. This will allow you to store and retrieve users from your database instead of our default shared pref based implementation.

@fiasko131
Copy link
Author

fiasko131 commented Jun 13, 2018

So according to you, my logic of storing and refreshing tokens is not enough to prevent login screen appears?
Do you have an example using AuthStorage and especially an example of the reconnection process?
Thanks a lot.

@fiasko131
Copy link
Author

fiasko131 commented Jun 14, 2018

So I try to understand the logic of your sdk:

Can you tell me where is the code that triggers the account selection view (with the toast: "you have been logged out")?
I found the ChooseAuthenticationFragment fragment that filled the listview.
Failing I could try to ensure that only the account interested in the login out is present in the list because here is what I get with three accounts: screenshot

As you can see, the listview has 2 empty cells and the 3rd one does not correspond to the account concerned. if the user clicks on an "empty" cells the application crash with a nullpointer.

So can yo help me to intercept the log out and see what I can do with the authStorage , or to fill this list only with the right account?

Thank you for your patient

@doncung
Copy link
Contributor

doncung commented Jun 15, 2018

The reason you are seeing the choose account UI is because the BoxSession object is being constructed with null for the user id. This has a special meaning to the session object, being that the user id will be chosen by the user and so when authenticate is called the UI is shown so that the user chooses which user to authenticate. If you want to login a particular user, you can just specify the userId in which case the logic will get the required access and refresh tokens from the LocalAuthStorage implementation (default to shared prefs). By default if the BoxSession is constructed with just the context it will default to the last authenticated user.

You will want to call BoxAuthentication.setAuthStorage sometime early in the lifetime of your application, ideally inside of your application's onCreate flow. At that point the SDK will refer to your implementation of this class to populate the UI. I assume what has happened is that some odd values have been put into your shared prefs which has caused the empty cells and the unknown account.

@fiasko131
Copy link
Author

fiasko131 commented Jun 15, 2018

OK...
So instead of set the tokens I set the userId that I get in my database:

    mBoxSession = new BoxSession(getActivity(),userId);//<--- that i stored	during account's creation											
    mBoxSession.authenticate().addOnCompletedListener(new 
    BoxFutureTask.OnCompletedListener<BoxSession>() {
               @Override
               public void onCompleted(BoxResponse<BoxSession> response) {//<--- never returned                             
                  if (response.isSuccess()) {
                      // if success i do my stuff
                  } else {
		      // if not i call onRefreshed 
                    onRefreshed(mBoxSession.getAuthInfo());                                        
                  }
              }
    });	

Yes but in this case the onCompleted is never returned?

So I do without:

 activity.mBoxSession = new BoxSession(getActivity(),mBoxUserId);
 activity.mBoxSession.setSessionAuthListener(FragmentClouds.this);//<----FragmentClouds implements 
 //the listener (onRefreshed, onFailure ,onAuthCreated...etc

 //do my stuff

It works .... but when the refrestime has expired and I want to reconnect, nothing happens. the login UI does not appear at all ???

and i obtain the system:out:

I/System.out: falsefalsefalsefalsefalsefalsefalsefalsefalsefalsefalsefalseunhandled json member '250xxx610' xxx # this is the userid that i want to reconnect{"access_token":"psnGxQaTcWxxxxxxxOfGtxK7drPsR","refresh_token":"pSY7lRQPiyhUkbrsLC3n1eZFdGAopuNVkxxxxxxxxxxxxxls6YqOBaWwGGCgfj","refresh_time":1529046901512,"client_id":"ejmdy01ay28rm6fok7wb8fhxyga3iawn","user":{"type":"user","id":"250942610","name":"my name","login":"xxxxxx.......
I/System.out: unhandled json member '221xxx241' xxx {"access_token":"gGWgJd1L64tLyDEEKxxxxxxSeFNL","refresh_token":"Bap0wN1tn7jeQbO2YmF6SLwAZqaJKlmagjORU6aeGOKH93IBKknaKrIXlm7SjnPT","refresh_time":1529047563773,"client_id":"ejmdy01ay28rm6fok7wb8fhxyga3iawn","user":{"type":"user","id":"221113241","name":"[email protected]","login":"[email protected]",
I/System.out: unhandled json member '189xxx696' xxx {"access_token":"PaYed94PcOnfz7lxxxx0Fi5c6qcQ8G","expires_in":3927,"restricted_to":"[]","refresh_token":"WbA1laRU508CoWBTdwfJgmv2tARADLNq7ARKifuzwl8NgMxxxxxxsokROOTOu","token_type":"bearer","user":{"type":"user","id":"189574696","name":"[email protected]","login":"[email protected]","created_at
I/System.out: unhandled json member 'restricted_to' xxx "[]" current object class com.box.androidsdk.content.auth.BoxAuthentication$BoxAuthenticationInfo
I/System.out: unhandled json member 'token_type' xxx "bearer" current object class com.box.androidsdk.content.auth.BoxAuthentication$BoxAuthenticationInfo

and:

I/BoxContentSdk: Request (GET): https://api.box.com/2.0/folders/0/items?offset=0&limit=1000&fields=size%2Ccreated_at%2Cextension%2Cid%2Cparent%2Cname%2Cpath_collection%2Cid%2Ccreated_at%2Cparent
I/BoxContentSdk: Request Header: Authorization:Bearer PaYed94PcOnfz7l6gJ0E30Fi5c6qcQ8G
I/BoxContentSdk: Request Header: User-Agent:com.box.sdk.android
I/BoxContentSdk: Request Header: Accept-Encoding:gzip
I/BoxContentSdk: Request Header: Accept-Charset:utf-8
I/BoxContentSdk: Request Header: Content-Type:application/json
I/BoxContentSdk: Response (401):

401 unauthorised? the

catch (BoxException e) {
      e.printStackTrace()
}

catch nothing.

What i am doing wrong?

I don't understand why is so tricky to switch between accounts :((

@doncung
Copy link
Contributor

doncung commented Jun 15, 2018

Generally shouldn't be, we use the SDK in our application. Are you still using your database to keep any refresh/access tokens or did you logout any of the users? It looks like the api call being made is for a different account 221113241 in this case. Logout will invalidate both the access and refresh tokens for a given user so it is likely that is the cause of the error.

After setting mBoxSession to your new BoxSession with the 250xxx610 you have to recreate any BoxApi objects you are using to make requests. The SDK is designed to allow you to make api calls in parallel with multiple users, you just need to construct your api objects with the session tied to the user you want. Based off your log the call is being made for a different user.

@fiasko131
Copy link
Author

My goal like with other cloud providers that I use, is to move from one account to another without having to identify me again once the creation is done.

So with the latest version I used, ie by building the session with the userid new BoxSession (context, userId), and never calling logout ... is there a reason for the sdk Logout himself?

Whenever I build a BoxSession with the userId, of course I rebuild FileApi and FolderApi with the new session.

So can you confirm that by doing this:

//click to connect account 1
mBoxSession = null;
mFileApi = null;
mFolderApi = null
mBoxSession = new BoxSession(getActivity(),userId1);
mFileApi = new FileApi(mBoxSession)
mFolderApi = new FolderApi(mBoxSession)
// build my list of items account 1
 
//click to connect account 2
mBoxSession = null;
mFileApi = null;
mFolderApi = null
mBoxSession = new BoxSession(getActivity(),userId2);
mFileApi = new FileApi(mBoxSession)
mFolderApi = new FolderApi(mBoxSession)
// build my list of items account 2

it's enough to go from one account to another and I would not log out with the toast "you have been logged out"? and so in this case no more listener is useful?

What is strange is that it works for a given time the refreshtime?

@dblankety
Copy link

dblankety commented Jun 16, 2018 via email

@fiasko131
Copy link
Author

fiasko131 commented Jun 16, 2018

OK @dblankety
so for the listener issue if I proceed as follows ?:

//click item1 to connect account 1
mUserId = item1.getUserId();
activity.mBoxSession.setSessionAuthListener(null);
mBoxSession = null;
mFileApi = null;
mFolderApi = null
mBoxSession = new BoxSession(getActivity(),mUserId);
activity.mBoxSession.setSessionAuthListener(listener);
mFileApi = new FileApi(mBoxSession)
mFolderApi = new FolderApi(mBoxSession)
// build my list of items account 1
 
//click item2 to connect account 2
mUserId = item2.getUserId();
activity.mBoxSession.setSessionAuthListener(null);
mBoxSession = null;
mFileApi = null;
mFolderApi = null
mBoxSession = new BoxSession(getActivity(),mUserId);
activity.mBoxSession.setSessionAuthListener(listener);
mFileApi = new FileApi(mBoxSession)
mFolderApi = new FolderApi(mBoxSession)
// build my list of items account 2
...........
 BoxAuthentication.AuthListener listener = new BoxAuthentication.AuthListener() {
               @Override
                public void onRefreshed(BoxAuthentication.BoxAuthenticationInfo   info) {
                          // or if i get the same listener
                          if (info.getUser().getId() == mUserId) // do stuff
                }
                .......
  };
                                                    

Also as @doncung explained to me that the box application was using your Sdk, i installed it. I configured three accounts about ten hours ago ... for the moment I did not have to redo a login , unlike my case???

After how long does the security logout occur with the default refreshtime?

@doncung
Copy link
Contributor

doncung commented Jun 21, 2018

Looks like it should work. The SDK handles refreshing automatically so as long as you don't explicitly call logout or get an event that would trigger an invalid refresh the users should remain logged in.

@fiasko131
Copy link
Author

fiasko131 commented Jun 25, 2018

@doncung
no, the principle mentioned above does not work. After a certain duration ?? the requests are no longer accepted ...
To try to use your sdk in my application I did this, every time i change my session for an userId:

activity.mBoxSession = new BoxSession(getActivity(),mBoxUserId);
activity.mBoxSession.setSessionAuthListener(listener);
activity.mBoxSession.refresh().addOnCompletedListener(new BoxFutureTask.OnCompletedListener<BoxSession>() {
        @Override
        public void onCompleted(BoxResponse<BoxSession> response) {
        if (response.isSuccess()){
                   // do my stuff                                             
        }else {
                   //do my stuff                                             
        }                                                
 }); 

It works but the response.issucces() return false....
But sometimes after a while onCompleted is not returned ???
And I have to restart the application ... but at least I do not have to log in again ...
Really I do not understand the problem and the logic of this sdk, and examples with the multiple accounts are inexistent.

@doncung
Copy link
Contributor

doncung commented Jun 25, 2018

We will try to improve our documentation for this use case.
A failed refresh can cause a logout of the user since it means the access and refresh tokens cannot be used and requires a new login. Is there an exception you are seeing in the response.getException() in the situation it returns false. I'll see if I can reproduce with that sample, but if not the stack trace of the exception can help.

Also when you say after a while, what do you mean? the OnCompletedListener added to the refresh is specific to that forced refresh() call. The listener added to the session on the other hand should be getting called as long as that session is still in memory. You can also register a listener on BoxAuthentication.getInstance() which should get a call as long as your listener is in memory.

@fiasko131
Copy link
Author

fiasko131 commented Jun 27, 2018

@doncung
I will try to catch the exception with response.getException() and give it to you.
But in the mentionned case, onCompleted() is not called at all.
Thank you again for your support.

@PreciselyAlyss PreciselyAlyss added documentation improvement or addition needed to documentation enhancement labels May 21, 2019
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
documentation improvement or addition needed to documentation enhancement
Projects
None yet
Development

No branches or pull requests

4 participants