cancel
Showing results for 
Show  only  | Search instead for 
Did you mean: 
Announcements
Want to learn some quick and useful tips to make your day easier? Check out how Calvin uses Replay to get feedback from other teams at Dropbox 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: Requests always fail from deployed website, but work fine in localhost

Requests always fail from deployed website, but work fine in localhost

polarzero
Helpful | Level 5
Go to solution

I'm using the SDK for requests from my Next.js website. Everything works fine in localhost, but as soon as it is deployed on Vercel, I can only get 400 Errors for my requests to the API.

 

See the following code:

 

// Get the Dropbox API client
export const getDropboxApiClient = async () => {
  const refreshToken = process.env.NEXT_PUBLIC_DROPBOX_REFRESH_TOKEN;
  const appKey = process.env.NEXT_PUBLIC_DROPBOX_APP_KEY;
  const appSecret = process.env.NEXT_PUBLIC_DROPBOX_APP_SECRET;

  const dbx = new Dropbox({
    clientId: appKey,
    clientSecret: appSecret,
    refreshToken: refreshToken,
  });

  return dbx;
};

 

 

 

// Get the available space for the account
const getAvailableSpace = async () => {
    const dbx = await getDropboxApiClient();
    const available = await dbx.usersGetSpaceUsage();
    console.log(available);
};

 

 

This request works perfectly on localhost, and returns:

polarzero_0-1669648944530.png

 

After I've deployed the App on Vercel, and added the environment variables the same way as usual (it works great with other SDKs), it doest work anymore.

 

All requests to the Dropbox API result in this:

polarzero_1-1669649023614.png

 

Looking more closely into the network tab, I noticed that the request headers are different on localhost and deployed website. See the following, from localhost:

polarzero_3-1669649210624.png

and on the website:

polarzero_4-1669649267902.png

The request is exactly the same, except that it is missing the `authorization` field! Which explains the error message in the response:

Error in call to API function "users/get_space_usage": Must provide HTTP header "Authorization" or URL parameter "authorization".

 

I have no idea what I'm doing wrong here, or what I should do. Moreover, it seems that the initial request to Dropbox works fine, given the Vercel logs:

polarzero_5-1669649489098.png

 

I hope someone will be familiar with this issue, and can provide me with some guidance. Here are some links to relevant parts of the code:

https://github.com/polar0/nilslepretre-photo-galley/blob/main/data/getDropboxApiClient.js

https://github.com/polar0/nilslepretre-photo-galley/blob/main/pages/admin/dashboard.js

 

Thanks!

 

 

1 Accepted Solution

Accepted Solutions

polarzero
Helpful | Level 5
Go to solution

Thank you for this additional guidance.

 

I could not make it work at first, but found a solution. For anyone else, new to making this kind of API calls and relying too much on abstracted calls with the SDK (like me), the data needs to be encoded before being passed to the body.

 

const data = {
    grant_type: 'refresh_token',
    refresh_token: refreshToken,
    client_id: clientId,
    client_secret: clientSecret,
};

const response = await fetch(`https://api.dropboxapi.com/oauth2/token`, {
  method: 'POST',
  headers: {
    'content-type': 'application/x-www-form-urlencoded',
  },
  // Note this: the data needs to be encoded first!
  body: new URLSearchParams(data),
});

console.log(await response.json());

 

Thanks again for your concern and patience!

View solution in original post

5 Replies 5

Greg-DB
Dropbox Staff
Go to solution

I see you're supplying a refresh token, app key, and app secret to the Dropbox JavaScript SDK. In that case, the SDK should automatically use those to retrieve a new short-lived access token (the string that starts with "sl.") whenever needed, which it would then use to perform Dropbox API calls (such as usersGetSpaceUsage).

 

As you pointed out, in the failing case it appears to be lacking an access token.

 

In the last screenshot you provided, I see what appears to be the call to use the refresh token to get an access token (with the 'grant_type' and 'refresh_token' parameters), however it looks like it's being made to your app's "/admin/dashboard" path, instead of the Dropbox token endpoint "https://api.dropboxapi.com/oauth2/token" as is necessary to receive the access token from Dropbox.

 

It's not clear why that is using the wrong URL though. It is technically possible to change the domain (relevant code here and here, meant for internal testing), but that should only change the domain and not the path. From the code you shared though, I don't see you changing that setting anyway. Is the code posted on GitHub exactly what is being run the failing case? Otherwise, is there anything on that environment that would be rewriting requests?

polarzero
Helpful | Level 5
Go to solution

Hi Greg. Thanks a lot for taking the time to help me.

 

The code in the repository is the exact one being used for the deployment (with Vercel), and as you could see I'm using a minimal setup. The only other API requests I'm using are made through Google Drive and Auth0 endpoints.

 

There is one thing I could think of, but I'm uncertain if it's relevant. Some pages are protected, and can only be accessed if the user is connected. And /admin/dashboard is one of them.

Therefore, navigating to this page, while not being connected, redirects to the Auth0 authentification page AUTH0_ISSUER_BASE_URL. On successful login, the user is sent back to the page.

 

However, I've just tried making that page public again. It does not solve the issue, though I'm getting a different error, similar to what you've seen on the Vercel screenshot:

polarzero_0-1669680483221.png

 

So it does make the issue a bit more specific. It looks like I should find out why these requests are not made to the Dropbox API url, though they are on localhost, and how to fix it.

I will try tweaking that domain tomorrow in the morning, it's getting late already here. Maybe I can get it back to the original one, though I did not change it. And I'll try to completely remove other libraries to isolate the problem.

 

Thanks again for your help! I'll keep you updated, if I were to find any lead!

polarzero
Helpful | Level 5
Go to solution

Hi Greg, I finally found a fix. I still have no idea why this request URL was changed, but most important was to get it working.

 

I had to make a direct request to the API (with the precise URL) to get a temporary access token, then create the Dropbox instance with it. Thereafter, I can keep the same system for making requests, since the only mismatch in the URL was related to the refresh token request. Some pieces of code, in case someone runs into the same issue:

 

export const getDropboxApiClient = async () => {
  // Get the refresh token, client ID and secret
  const refreshToken = process.env.NEXT_PUBLIC_DROPBOX_REFRESH_TOKEN;
  const clientId = process.env.NEXT_PUBLIC_DROPBOX_APP_KEY;
  const clientSecret = process.env.NEXT_PUBLIC_DROPBOX_APP_SECRET;

  // Make the call with a specific address
  const response = await fetch(
    `https://api.dropboxapi.com/oauth2/token?grant_type=refresh_token&refresh_token=${refreshToken}&client_id=${clientId}&client_secret=${clientSecret}`,
    {
      method: 'POST',
      headers: {
        'Content-Type': 'application/json',
      },
      body: null,
    },
  );
  const data = await response.json();

  const token = data.access_token;
  // Create the instance with that access token
  const dbx = new Dropbox({ accessToken: token });

  return dbx;
};

 

And then I can make requests the same way as before:

const dbx = await getDropboxApiClient();
const availableSpace = await dbx.usersGetSpaceUsage();

 

The only issue here is that my private keys (refresh token, app ID and secret) are visible in the Headers, when navigating to the request headers, in the Debugger. But I'm sure I'll find a quick fix to that.

 

Thanks a lot for your help, I had given up already before you pointed me to the right direction.

Greg-DB
Dropbox Staff
Go to solution

Thanks for following up, and for sharing your code. It does seem like something is rewriting those requests but unfortunately I can't say what that might be. In any case, I'm glad to hear you got this working.

 

By the way, the /oauth2/token endpoint does accept these parameter either on the URL or in the body, so for example (using curl for illustration), instead of:

curl -X POST "https://api.dropboxapi.com/oauth2/token?refresh_token=REFRESH_TOKEN&grant_type=refresh_token&client_id=APP_KEY&client_secret=APP_SECRET"

you can do:

curl -X POST "https://api.dropboxapi.com/oauth2/token" --data "refresh_token=REFRESH_TOKEN&grant_type=refresh_token&client_id=APP_KEY&client_secret=APP_SECRET"

 (The "content-type" would be "application/x-www-form-urlencoded".)

polarzero
Helpful | Level 5
Go to solution

Thank you for this additional guidance.

 

I could not make it work at first, but found a solution. For anyone else, new to making this kind of API calls and relying too much on abstracted calls with the SDK (like me), the data needs to be encoded before being passed to the body.

 

const data = {
    grant_type: 'refresh_token',
    refresh_token: refreshToken,
    client_id: clientId,
    client_secret: clientSecret,
};

const response = await fetch(`https://api.dropboxapi.com/oauth2/token`, {
  method: 'POST',
  headers: {
    'content-type': 'application/x-www-form-urlencoded',
  },
  // Note this: the data needs to be encoded first!
  body: new URLSearchParams(data),
});

console.log(await response.json());

 

Thanks again for your concern and patience!

Need more support?