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

drop_auth() generating short lived tokens #201

Open
BrookeGibbons opened this issue Feb 7, 2022 · 13 comments
Open

drop_auth() generating short lived tokens #201

BrookeGibbons opened this issue Feb 7, 2022 · 13 comments

Comments

@BrookeGibbons
Copy link

I was using rdrop2 in a shiny app, and had the shiny app creating multiple .csv files in my dropbox account for a couple of months.
Last week I accidentally pushed my dropbox token to github, so I deleted the rdrop2 app from my dropbox account and created a new token through R and rdrop2.

Now, whenever I generate a new token I get a short-lived token (prefix sl.) They expire in 4 hours.

I need the token to be long lived (not expire).

This article says long lived tokens were retired in Sep 2021 - but I created mine in October.

Is there still a way to create long lived tokens?

@Emily-Chai
Copy link

Emily-Chai commented Feb 7, 2022

I just got back from Dropbox. Here is the reply:

"Dropbox is in the process of switching to only issuing short-lived access tokens (and optional refresh tokens) instead of long-lived access tokens. You can find more information on this migration here.

Apps can still get long-term access by requesting "offline" access though, in which case the app receives a "refresh token" that can be used to retrieve new short-lived access tokens as needed, without further manual user intervention. You can find more information in the OAuth Guide and authorization documentation.

For reference, while the creation of new long-lived access tokens is now deprecated, we don't currently have a plan to disable existing long-lived access tokens. (If that changes, we will of course announce that ahead of time.) That being the case, you can continue using existing long-lived access token(s) without interruption, if you have any. Also, note though that after the change you won't be able to create new long-lived access tokens.

While the change began on September 30th, we're releasing it gradually, so you may not have seen your app(s) affected yet/until now. Once it applies to your app, it would apply regardless of the "Access token expiration" setting for your app, and that setting may no longer be available for your app."

I mark the answers to your questions in bold.

@Emily-Chai
Copy link

Emily-Chai commented Feb 7, 2022

I need the token to be long lived (not expire).

Did you try "implement refresh tokens" mentioned here? What I did so far is after calling drop_auth() in R, in the pop-up webpage, I added "&token_access_type=offline" to the end of the URL to generate "a long-lived refresh_token that can be used to request a new, short-lived access token." It seems working after 4 hours.

@AfonsoCampos
Copy link

@Emily-Chai I'm having the same problem here. I tried adding the "&token_access_type=offline" to the URL, but it didn't work. Did you manage to solve the problem @BrookeGibbons ?

@Emily-Chai
Copy link

After calling drop_auth() in R, in the pop-up webpage, I added "&token_access_type=offline" to the end of the URL, then hit enter to refresh the page, then authorize as usual. In this way, there should be "a long-lived refresh_token that can be used to request a new, short-lived access token" generated to your app folder. Then I published the app as usual, my authorization problem was solved.

@jcarbaut
Copy link

jcarbaut commented Feb 24, 2022

I had the same problem, and the solution is to change one line in the rdrop2::drop_auth function: dropbox_token <- httr::oauth2.0_token(dropbox, dropbox_app, cache = cache, query_authorize_extra = list(token_access_type = "offline")). With this, the token includes both a short lived token and a refresh token. Then, this token can be refreshed with dropbox_token$refresh(), which returns a new short lived token. It would be nice to add this as an empty list parameter to drop_auth, as it's supposed to be by default in httr::oauth2.0_token. See this for the introduction of this argument in httr 1.4.0.

@BrookeGibbons
Copy link
Author

@Emily-Chai's fix also worked for me too.

@Intangibil
Copy link

Intangibil commented Mar 17, 2022

@Emily-Chai @BrookeGibbons
So, if I do token <- drop_auth() and then add "&token_access_type=offline" to the URL, I get "a long-lived refresh_token that can be used to request a new, short-lived access token".
I can save that with saveRDS(token, TOKEN_PATH).
However, I am unsure how I need to use it further.
How do I request a new short-lived token? (do I do token <- readRDS(TOKEN_PATH) and then token$refresh(), or how does that work?)
When does this need to happen? (at each application start?)

@bsh2
Copy link

bsh2 commented Mar 21, 2022

@karthik any updates on this? In particular in relation to @jcarbaut 's fix? I am currently having the same issue with short lived tokens.

@jcarbaut
Copy link

jcarbaut commented Mar 22, 2022

@karthik any updates on this? In particular in relation to @jcarbaut 's fix? I am currently having the same issue with short lived tokens.

In the meantime, you can use the fix on a copy of the drop_auth function (the code is straightforward), or alternately Emily-Chain's trick above (I have not tested this, but it should work too). Once you have a refresh token, you can store it and use it in your app to get a short-lived token with token$refresh(). It will work for 4 hours: you may create this token at the beginning of a shiny app session, for instance.

If you need longer runs, it will be more difficult: if I understand correctly, the httr package does not do it automatically (it's one of the purposes of httr2, which rdrop2 does not currently use), so you'll have to do it "by hand" in your app: basically, each time you use a token, check its validity. This could be done by wrapping each reference to the token with a function that checks this.

@bsh2
Copy link

bsh2 commented Mar 22, 2022

@jcarbaut Thank you, very helpful. Have made the changes in my copy of the drop_auth function based on your PR. 4 hours should work for me, a user is on average only going to use each session for 30 minutes maximum. Hopefully in the future rdrop2 will move to httr2. Thanks again, Ben

@TessaEPronk
Copy link

I'm trying to make my Shiny app work in combination with dropbox, however I still didn't manage. It would really be helpfull for non-developers if the package was updated with this functionality. Alternatively, it would help if anyone could provide the complete example code for a working shiny app including the fix. Thank you so much, if anyone can do that!

@TessaEPronk
Copy link

After one morning of fiddling I managed, here's for the not-so apt like me: 1. make sure you are in the folder of your shiny-app. 2. In the console, load the library rdrop2. 3. In the console type drop_auth() 4. in the website that opens, do what Emily-Chai suggested: After calling drop_auth() in R, in the pop-up webpage, I added "&token_access_type=offline" to the end of the URL, then hit enter to refresh the page. 5. In your console, save the token saveRDS(token, "droptoken.rds") 6. Then go to the code in your shiny-app and below the calling of the other libraries put: outputDir <- "put here yours"; token<-readRDS("droptoken.rds"); token$refresh() 7. run your shiny app. I put this in both my ui and server scripts, don't know if that is neccesary. Now after one day, it still works, thanks everyone above again for the ingredients!

joshuaylevy pushed a commit to BoothStiglerCenter/capitalisntDashboard that referenced this issue Aug 11, 2022
The ShinyApp is going to access the underlying data files by through Dropbox. rdrop2, the Dropbox API hooks for R require a valid OAuth 2.0 authorization token to work properly (without asking the user for manual interaction to authorize which would not be possible at build time). Inspiration for this solution was taken from: https://stackoverflow.com/questions/71406126/store-oauth-token-as-a-github-secret

localDevTools.R
- This file is new and is the workhorse of this process. This encryption workflow relies on two R libraries: cyphr and sodium. This file should really only be run LOCALLY -- ie not on the cloud/through GitHub Actions. It has temporarily been .gitignored.
- First this file reads in a locally stored (and .gitignored) JSON file that contains a string keyphrase.  (This keyphrase is also stored as a SECRET in GitHub Actions.) That string is converted to raw and then turned into a binary hash. Sodium then uses that binary hash to generate a key to be used for symmetric en/decryption.
- This script also customizes rdrop2's drop_auth() function. The custom function adds an extra argument `query_authorize_extra = list(token_access_type = "offline")` to make sure that our Dropbox authorization token has a refresh token built in.
-- karthik/rdrop2#201 
-- Starting in 2021 Dropbox stopped issuing no-timeout tokens and all tokens are short-life (expire in 4 hours). Adding the refresh token request allows us to push this locally authenticated token just once and have it persistently work in the cloud (on shiny.io).
-- https://www.dropboxforum.com/t5/Dropbox-API-Support-Feedback/Getting-refresh-token-for-shiny-app-using-rdrop2-and-drop-auth/td-p/582280
-- karthik/rdrop2#201
- The custom_drop_auth() function is then run and that token is encrypted and stored locally, "drop_token_rds.rds", in preparation to be pushed to GitHub.

.gitignore
- Making sure to ignore the unencrypted .httr-auth
- ignoring the localDevTools.R script as it should only be run locally for the time being.

deployApp.R
- Adding extra functionality to handle the encrypted Dropbox OAuth (RDS) Token.
- This script is supposed to be run by a Docker file through GitHub actions. It's primary purpose is to push updated functionality to shiny.io. Because that Shiny server is going to get wiped when we push new functionality, we also need to push the token with the other core Shiny files.
- This script reads in a GitHub secret ("SODIUM_ENCRYPTION_PHRASE") and uses the same process as `localDevTools.R` to create a sodium key (by way of a hashed binary from a string) for symmetric en/decryption.
- It then takes the top-level, encrypted "drop_token_rds.rds", decrypts it and then stores the unencrypted version of the token with the other core Shiny files in "capitalisntDashboardApp/". It then makes sure to re-encrypt the top-level RDS file and **delete** the one that was stored in the "capitalisntDashboardApp/" folder so that the unencrypted one is never exposed to the public.

global.R
- Misc/cleanup

README.md
- Updating the filetree in advance of writing out some more thorough documentation.
@adamcohen3
Copy link

adamcohen3 commented Dec 9, 2022

  1. In your console, save the token saveRDS(token, "droptoken.rds")

@TessaEPronk @Emily-Chai Can you clarify? At what step in 1-5 is a token variable created in the workspace?

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging a pull request may close this issue.

8 participants