cancel
Showing results for 
Show  only  | Search instead for 
Did you mean: 
Announcements
Musicians, convert your MuseScore files to PDF to play music on the go! Learn more here.

Dropbox API Support & Feedback

Find help with the Dropbox API from other developers.

cancel
Showing results for 
Show  only  | Search instead for 
Did you mean: 

Re: JavaScript GitHub Action to Upload Build Artifact to Dropbox

JavaScript GitHub Action to Upload Build Artifact to Dropbox

MikeKell
New member | Level 2

Hi... first confessions - while I understand OAuth flow at a high-level, I'm not super familiar with the details.  I've poked around quite a bit here and on Stack Overflow and cannot find the answer to what seems to me to be a pretty simple request.

 

I have a Wiki on GitHub.  As part of a commit Action, I want to bundle some of the pages from the Wiki into a PDF and push that to a folder on Dropbox where others can access it.  This is used as a basic introduction to the company and so it's not appropriate for the recipients of this PDF to have access to the wiki which is why we're doing it this way.  I have it working to generate the PDF and to push it to Dropbox using some code I found that uses the JavaScript API and filesUpload.  

 

The problem, of course, is authentication and security.  The code I found is a few years old and is apparently back from when Dropbox Access Tokens were long-lived.  Now they are short-lived for security reasons.  So I have the problem that everyone has reported which is the short-lived tokens starting with "sl." that are easily obtained from the Dropbox App console work for only 4 hours.  This led me down the rabbit hole of how to authenticate properly in this scenario.  Key aspects of this scenario:

 

  1. This is a NodeJS GitHub action - there is no UI so an OAuth UI flow is not possible.
  2. I get from comments e.g. here that the Dropbox API was designed to have an authorization flow to enable access to a particular Dropbox user's account from the app using the API.  That is not the scenario here - I want to push these files into my Dropbox account where they will land in a shared folder that others can access (e.g. to send mail to new people with these PDFs as attachments).  In the referenced Stack Overflow, Greg says, "The Dropbox API was designed with the intention that each user would link their own Dropbox account, in order to interact with their own files. However, it is technically possible to connect to just one account. The SDKs don't offer explicit support for it and we don't recommend doing so, for various technical and security reasons."  OK - but this can't be that unusual of a situation - where some headless app wants to push results into Dropbox.  Think of an ML analytics app that is processing a ton of data server-side and wants to publish a report summarizing the data into Dropbox.  Think of an app that receives invoices and wants to push the invoices into an "Accounts Payable" folder in Dropbox.  I could literally list dozens of use cases for this.  So if this isn't recommended - what is the approach for these types of use cases?

 

Below is the code I have now that is kinda/sorta working except for the expiring access token; note that the concern about security of access tokens is I believe addressed here by storing these in the GitHub Actions secret store.  I've also configured this Dropbox app to have only access to a single "app-specific" folder in Dropbox so it can't access other files on my Dropbox.

 

 

 

 

const Dropbox = require('dropbox').Dropbox
const fs = require('fs')
const fetch2 = require('node-fetch')
const core = require('@actions/core')
const github = require('@actions/github')
const glob = require('glob')

const accessToken = core.getInput('DROPBOX_ACCESS_TOKEN')
const globSource = core.getInput('GLOB')
const dropboxPathPrefix = core.getInput('DROPBOX_DESTINATION_PATH_PREFIX')
const isDebug = core.getInput('DEBUG')
const dropbox = new Dropbox({accessToken, fetch: fetch2})

function uploadMuhFile(filePath: string): Promise<any> {
  const file = fs.readFileSync(filePath)
  const destinationPath = `${dropboxPathPrefix}${filePath}`
  if (isDebug) console.log('uploaded file to Dropbox at: ', destinationPath)
  return dropbox
    .filesUpload({path: destinationPath, contents: file})
    .then((response: any) => {
      if (isDebug) console.log(response)
      return response
    })
    .catch((error: any) => {
      if (isDebug) console.error(error)
      throw error
    })
}

glob(globSource, {}, (err: any, files: string[]) => {
  if (err) core.setFailed('Error: glob failed', err)
  Promise.all(files.map(uploadMuhFile))
    .then((all) => {
      console.log('all files uploaded', all)
    })
    .catch((err) => {
      console.error('error', err)
    })
})

 

 

 

 

As I said, this works - for four hours.  Then I have to refresh the DROPBOX_ACCESS_TOKEN in GitHub secret storage.  This is apparently by design, so what would I change / fix here to be able to handle refresh tokens or other ways to authenticate that don't expire?

 

Thanks.

 

 

2 Replies 2

Здравко
Legendary | Level 20

Hi @MikeKell,

I think you are mixing 2 different things. Your referrals to post that describes security considerations about client side application can be applied to server side application (as your is) as much as for long lived access token - i.e. no so much applicable. When you provide to a client application, embedding your private security data (doesn't matter what exactly), is some attacker client able to extract embedded data? I think this is a serious security consideration. Is you case the same; i.e. are your clients able to access somehow your tokens? 🤷 That's it. 😉

You can keep refresh token in the same way  you do for access token. You can construct your "dropbox" object in the same way with only addition the refresh token for initialization. Everything else, while works (as you said), can stay the same and works... as long as you want (non restricted to 4 hours). The SDK (you are using) take care for the access token refresh on background (whenever needed).

Hope this clarifies matter.

tahsini
Dropbox Staff

 

As @Здравко mentioned you'll want to implement refresh tokens to manage access beyond the limit of short-lived tokens.

 

Our Javascript SDK handles refreshing of the short-lived tokens via refresh tokens for you. Here is an example that implements the PKCE Flow where a refresh token is stored.

Need more support?