6 Replies Latest reply on Mar 3, 2015 1:32 AM by ztenerowicz

    External Storage Framework refresh token flow in SDK returns a bad token

    ztenerowicz

      As mentioned in https://community.jivesoftware.com/message/1505201

       

      We're having trouble keeping our storage provider connected to Jive for longer than a token's TTL. We're using the Node.js SDK.

       

      1. on group creation we get a code for auth flow

      2. we make a request to exchange this code for an oauth token

      3. we use the token to send stuff with jive-sdk.community.doRequest method

      4. if the token is too old already, the doRequest method goes through the refresh token procedure

      5. after that the new token is incorrect - 401, incorrect scope

       

      This is a major issue, because our external storage provider can only maintain updates to a group for a day and then it's forever broken.

       

      Is community.doRequest not the right method to call? Or do we need to pass additional data to get the scope right? (scope is a parameter of an existing token, so we didn't think we need to)

        • Re: External Storage Framework refresh token flow in SDK returns a bad token

          Aron Racho - This sounds like the SDK isn't auto-refreshing their token.  Have you seen this?  Last I checked, when you called "community.doRequest" the corresponding OAuth tokens were automatically refreshed if the status codes came back wrong?

           

          zbigniew tenerowicz - can you provide a snippet of your node.js calls using the jive-sdk, and also confirm that your OAuth tokens are being stored in the community structure, or if they are being stored in another object?

            • Re: External Storage Framework refresh token flow in SDK returns a bad token
              ztenerowicz

              As I said, doRequest refreshes the token. The new token is what the problem is about

               

              You have access to a snapshot of our entire codebase from ~2 weeks ago as far as I know, but for the sake of locality:

               

              1. in place creation handler:

              jive.community.requestAccessToken(registerReq.jiveUrl, registerReq.oauthCode)

                      .then(function (oauthResponse) {

                          registerReq.oauth = oauthResponse.entity;

               

                          return jive.community.findByJiveURL(registerReq.jiveUrl)

                              .then(function (community) {

                                  community.oauth = oauthResponse.entity;

                                  return jive.community.save(community);

                              });

                      })

               

              2. when an update needs to be posted:

              - we fetch the community using jive.community.findByJiveURL

              and then:

              jive.community.doRequest(community, reqOptions);

               

              where reqOptions only contain url, path and body

               

              So the oauth token and all is stored in the community object and jive-sdk performs the token refreshing (and brags about it in logs a lot ) as a part of making the request. Then right after getting the fresh token it retries and returns 401. I don't have much insight into what happens in the retry and what the request looks like.

                • Re: External Storage Framework refresh token flow in SDK returns a bad token

                  In your scenario, can you output the local copy of the community object's scope before and after it goes dark.  is the scope /api or something similar?  Is it possible you are trying to access services that are not part of the /api suite?

                   

                  It seems odd that the original request scope is fine, and I've never heard of the scope changing after a refresh as that is defined in the registration flow and should remain static IMO.

                   

                  Have you tried adding the following properties to your jiveclientconfiguration.json - Definition

                      "development": true,

                      "logLevel" : "DEBUG",

                   

                  to see if any of that extra debug output can display some additional information of the token refreshing.  If you'd like, you can probably change the OAuth token lifetime in the Add-On Menu > Add-On > (Gear Icon) > Advanced Settings ... and shrink the lifespan down so its reproducable in a shorter period.

                   

                  I'll continue to reach out to Aron Racho to see if he has any thoughts, but my guess is that the scope is different ... or you are calling services that are in fact outside of the /api scope.

                    • Re: External Storage Framework refresh token flow in SDK returns a bad token
                      ztenerowicz

                      I'm writing this the second time, because the editor broke, so I'll be brief.

                       

                      RE: "Is it possible you are trying to access services that are not part of the /api suite?"

                      I already described the situation. "our external storage provider can only maintain updates to a group for a day and then it's forever broken"

                      So we are sending requests to the ESF api for a place and they succeed. On the next day requests to the same exact urls, with new updates, fail.

                      Please, refrain from TL;DR-ing this thread. I know I write long posts, but this is important.

                       

                      Here's what the community object looks like at first:

                       

                      { _id: 542e<numbers>d723,

                        jiveUrl: 'https://jivedemo-egnyte.jiveon.com',

                        version: 'post-samurai',

                        tenantId: 'af254af1-<numbers>-e8e9315aed99',

                        clientId: 'a6n7ec<numbers>dijl6w5tpvm.i',

                        clientSecret: '19uu<numbers>4mw8tk.s',

                        jiveCommunity: 'jivedemo-egnyte.jiveon.com',

                        id: 'https://jivedemo-egnyte.jiveon.com',

                        oauth:

                        { access_token: 'd16b2u<numbers>h9snns81ce.t',

                        refresh_token: 'ca90x3fb<numbers>bmfqyh1h9.r',

                        token_type: 'bearer',

                        expires_in: '59',

                        scope: 'uri:/api/core/v3/exstorage/containers/1516/' } }

                       

                      And after the refresh sequence we get this:  (note missing scope)

                       

                      { _id: 542e<numbers>d723,

                        jiveUrl: 'https://jivedemo-egnyte.jiveon.com',

                        version: 'post-samurai',

                        tenantId: 'af254af1-<numbers>-e8e9315aed9',

                        clientId: 'a6n7ec<numbers>dijl6w5tpvm.i',

                        clientSecret: '19uu<numbers>4mw8tk.s',

                        jiveCommunity: 'jivedemo-egnyte.jiveon.com',

                        id: 'https://jivedemo-egnyte.jiveon.com',

                        oauth:

                         { access_token: 'hosffbott9<numbers>km3j9vxw7v.t',

                           refresh_token: 'ca90x3fbe<numbers>ndgbmfqyh1h9.r',

                           token_type: 'bearer',

                           expires_in: '59' } }

                       

                      But the scope for the token is still limited to the initial value.

                       

                      And this is the answer here, because the scope is "uri:/api/core/v3/exstorage/containers/1516/" - so it points to a certain container.

                      We don't define the scope, so I guess this is already associated with the auth code in the place creation call.

                      Jive-sdk keeps only one community object per remote Jive instance, so keeping the auth on the community object is useless if you have more than one place to handle, because the key for place1 will be overwritten by the key for place2.

                       

                      Two questions then:

                      1. Can we make the scope look more like "uri:/api/core/v3/exstorage" ?

                      2. If not, is there a guide to implementing doRequest with multiple oauth holders?

                        From reading the code I know there must be a way to define a callback that would store the updated credentials after refresh (instead of them being pushed to the community object). Unfortunately, mulitple fields are overwritten if matching fields are present in the community object, so if anything updates the community object just once, we lose control over what's happening.

                        Therefore:

                      3. Could you reconsider forcing everybody to create community objects if they are harmful to certain cases? see jive.community.doRequest looks for community for no reason · Issue #33 · jivesoftware/jive-sdk · GitHub

                         I even hacked the persistence to return a promise resolved to an empty object and it more or less worked, so it shouldn't be hard to make it optional.

                        • Re: External Storage Framework refresh token flow in SDK returns a bad token

                          In the short term, if you have changes you'd like to make to the SDK to make your solution work that is acceptable, and if you could contribute that back we can evaluate to incorporate as part of the SDK.

                           

                          I'll pass this information along to the engineer looking into this issue so he better knows how to replicate.

                           

                           

                          Appreciate the details....

                            • Re: External Storage Framework refresh token flow in SDK returns a bad token
                              ztenerowicz

                              I implemented storing oauth credentials in our place objects and updating them with tokenPersistenceFunction passed in options. I didn't have the time to remove the requirement for a community object, so we kept it as a dummy object that doesn't hold any important information.

                               

                              Thanks for suggesting where to look for debugging details. If you ever figure out how to get a more general scope (1) let us know (via e-mail possibly).

                              (2) should be simple to document - just highlight that the community object can't contain oauth data and it has to be passed in options and updated via options.tokenPersistenceFunction.

                              (3) doRequest should accept null as community object and require all request data in the options if no community is provided.

                               

                              We can consider this thread closed.

                              This was, to my knowledge, our last unanswered development/documentation question. There still are some bug reports and feature suggestions going on, but now our knowledge is enough.