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

"expired_access_token" error messages #1185

Open
simatec opened this issue Mar 19, 2022 · 16 comments
Open

"expired_access_token" error messages #1185

simatec opened this issue Mar 19, 2022 · 16 comments

Comments

@simatec
Copy link

simatec commented Mar 19, 2022

We use your Libary in our backup project for iobroker

https://github.com/simatec/ioBroker.backitup

Dropbox currently seems to have changes that only have a token validity of 4 hours for newly created apps.
This is a problem for our plugin because we use the token to upload the backups to Dropbox.

How to work around the "expired_access_token" issue?

@adasq
Copy link
Owner

adasq commented Mar 19, 2022

Since recent changes to the Dropbox API, we should probably go through OAuth flow to tackle that.

Great reading on the topic (flow described) - please get familiar with this: https://developers.dropbox.com/pl-pl/oauth-guide (especially Using Refresh Tokens section)

The new package version has been released (2.5.0) which includes a new function dropboxV2Api.refreshToken(token) designed to refresh a token. Also note, that to generate long-living refresh_token we have to specify token_access_type: 'offline' option in dropboxV2Api.authenticate(), as below:

const dropbox = dropboxV2Api.authenticate({
    client_id: 'APP_KEY',
    client_secret: 'APP_SECRET',
    redirect_uri: 'REDIRECT_URI',
    token_access_type: 'offline', // if you need an offline long-living refresh token
    state: 'OPTIONAL_STATE_VALUE'
});
//generate and visit authorization sevice
const authUrl = dropbox.generateAuthUrl();
//after redirection, you should receive code
dropbox.getToken(code, (err, result, response) => {
    // you are authorized now!
    // ...then you can refresh your token! (flow for token_access_type='offline')
    dropbox.refreshToken(response.refresh_token, (err, result, response) => {
        //token is refreshed!
    });
});

Hopefully, that answers your question.
Cheers

@simatec
Copy link
Author

simatec commented Mar 20, 2022

I have already tried this.

However, I get the error that "code" is not defined.
dropbox.getToken(code, (err, result, response) => {

What exactly is the function of the variable "code"?

And what does the variable do?
const authUrl = dropbox.generateAuthUrl();

Unfortunately the code doesn't work

@simatec
Copy link
Author

simatec commented Mar 20, 2022

Unfortunately, another test still gives the error

code is not defined

@adasq
Copy link
Owner

adasq commented Mar 20, 2022

However, I get the error that "code" is not defined.

The code is what you get from OAuth flow.

Here's a full example of how to go throughout the OAuth process:
https://github.com/adasq/dropbox-v2-api/blob/master/example/oauth2-flow.js

@simatec
Copy link
Author

simatec commented Mar 20, 2022

Do I also have to write the example code in my source code or is the hapi server created automatically here?

To be honest, I don't quite understand oauth2

@simatec
Copy link
Author

simatec commented Mar 20, 2022

Unfortunately this method does not work.
I can't get it to work with Hapi Server etc. either.

the variable "code" is still not passed and it seems to me that Dropbox has changed something in general.
It doesn't seem to accept token_access_type either

@simatec
Copy link
Author

simatec commented Mar 20, 2022

Here is the current error log

`

backitup.0 2022-03-20 14:34:05.314 error Cannot read property 'refresh_token' of undefined
backitup.0 2022-03-20 14:34:05.314 error TypeError: Cannot read property 'refresh_token' of undefined at /opt/iobroker/node_modules/iobroker.backitup/lib/dropboxAuthenticate.js:26:39 at Request._callback (/opt/iobroker/node_modules/iobroker.backitup/node_modules/dropbox-v2-api/src/dropbox-api.js:187:8) at Request.self.callback (/opt/iobroker/node_modules/iobroker.backitup/node_modules/request/request.js:185:22) at Request.emit (events.js:400:28) at Request. (/opt/iobroker/node_modules/iobroker.backitup/node_modules/request/request.js:1154:10) at Request.emit (events.js:400:28) at IncomingMessage. (/opt/iobroker/node_modules/iobroker.backitup/node_modules/request/request.js:1076:12) at Object.onceWrapper (events.js:519:28) at IncomingMessage.emit (events.js:412:35) at endReadableNT (internal/streams/readable.js:1334:12)
backitup.0 2022-03-20 14:34:05.314 error uncaught exception: Cannot read property 'refresh_token' of undefined
`

@simatec
Copy link
Author

simatec commented Mar 20, 2022

I think the problem is somewhere with opn

Opn(dropbox.generateAuthUrl());

If I manually fetch the code from the browser and put it in the "code" variable, it loops through and updates the token.

Can you look there again? ... unfortunately I don't really understand the code and can't find the error.

I only copied the example code in our code and adjusted the variables with the account data

Would it be possible to read out the URL with the code with axios?

@adasq
Copy link
Owner

adasq commented Mar 21, 2022

Hey @simatec

OAuth flow implies authenticating via your browser. You can't automate it. Once you open the URL (dropbox.generateAuthUrl()) and authenticate via Dropbox account you'll be responded with a code. The code then Is utilized to generate a refresh_token (via dropboxSession.getToken(code)) and ultimately to refresh this token once it's expired (dropboxSession.refreshToken(refresh_token))

@simatec
Copy link
Author

simatec commented Mar 21, 2022

Thanks for the explanation ...
I think this is exactly where the problem lies.
Opn wants to open the URL in a browser, which is not possible.

iobroker mostly runs on Linux machines that have no GUI and no browser.

Is there a way to query the code via request or axios?

Currently, the Dropbox documentation still says "no expiration" tokens, but the dropdown can no longer be found in the app console.

Our backup plugin works independently in the background.

In the end, the tokens would have to be updated in order to be able to use the Dropbox.

As a developer myself, I have several apps in Dropbox.
All older apps still have a "no expiration" token.

If I want to create a new app, I only get "short-live".

Unfortunately, this is a very bad situation for all new users at the moment, as this token expires after 4 hours.

Maybe you have an idea how to get around that topic.

@adasq
Copy link
Owner

adasq commented Mar 21, 2022

I see your point now. As described here:

Applications that require offline access to the API - meaning using the API when the end user is not actively interacting through your app - will not be able to prompt for re-authorization. These apps may instead use long-lived refresh tokens can be used to obtain new access tokens.

When using refresh tokens, your call to the /oauth2/token endpoint with the grant_type of authorization_code will return a short-lived access token and a refresh token, which should be securely stored.

you can still "refresh" a short-living token (the one that expires every 4h) by using refresh_token, which should be stored securely:

To update your access token, call the /oauth2/token endpoint - specifying your refresh_token as a parameter and using the grant_type of refresh_token. The endpoint will return a new short-lived access token and a timestamp indicating its expiration time.

The problem is your users need to walk through OAuth flow, but only once - to generate refresh_token. I've seen some implementation how to tackle that on server-side, where authentication url is printed on command-line interface, then a user can copy it, authenticate via browser and set a newly generated refresh_token in the app, which stores it securely.

@simatec
Copy link
Author

simatec commented Mar 21, 2022

I think it's a shame that the integration has changed so drastically here.

With a good 45,000 users of our plugin, this probably means a huge amount of support if suddenly saving to Dropbox no longer works.

I'll try to add authorization to our configuration menu.

If you have a link to the server side implementation for me that would be cool.

I find it very unfortunate how to use redirect URL to get access code. I don't think this can be solved really well in the GUI.

With localhost as the redirect url, you don't get a reasonable output of the code for the user, but it is only displayed very unhappily in the browser's address line.

This is certainly a very big hurdle for some users.

But thanks in advance for your help.

If you still need information about the integration from me, let me know.

Here is the link to our project.

https://github.com/simatec/ioBroker.backitup

@adasq
Copy link
Owner

adasq commented Mar 21, 2022

Totally agree. Certainly, they have some strong arguments (probably security considerations) to ditch an old way of creating long-living tokens

If you have a link to the server side implementation for me that would be cool.

Sorry, I don't. I've seen this in action while using CLI (I recall Heroku CLI does it).

With a good 45,000 users of our plugin, this probably means a huge amount of support if suddenly saving to Dropbox no longer works.

Great job on your project. I can see you have UI for it so you could leverage it to implement OAuth flow.

Even more details on the OAuth flow from Dropbox:
https://dropbox.tech/developers/migrating-app-permissions-and-access-tokens

@simatec
Copy link
Author

simatec commented Mar 24, 2022

I've made quite a bit of progress now and have added authorization to our configuration menu.

Now Dropbox has approved an app for productive use.

I want to make this app available to the users.

However, the authorization should be done via PKCE.

Can you please add the PKCE option for getToken and refreshToken.

I'm currently doing this with request, but it would be great if you could do it all from your library.

Here are examples of how to do this with PKCE:

getToken:
curl https://api.dropbox.com/1/oauth2/token -d code=xxxxxxxxxxxxxxxxxx -d grant_type=authorization_code -d code_verifier=xxxxxxxxxxxxxxxxxxxxxxxxxxxx -d client_id=xxxxxxxxx

refreshToken:
curl https://api.dropbox.com/1/oauth2/token -d refresh_token=xxxxxxxxxxxxxxxxxxxxxxxxx -d grant_type=refresh_token -d client_id=xxxxxxxx

@MrVectorPath
Copy link

it will be actually good if your examples worked in documentation you say Redirection URI is optional but cant seem to run anything without it .

@cyril23
Copy link

cyril23 commented Oct 1, 2023

and ultimately to refresh this token once it's expired (dropboxSession.refreshToken(refresh_token))
[...]
you can still "refresh" a short-living token (the one that expires every 4h) by using refresh_token, which should be stored securely:

you can avoid implementing your own validity check of the access token according to the 4h expiration, by switching to the other official Dropbox JavaScript NPM package https://www.npmjs.com/package/dropbox which handles refreshing itself (you just need set dbx.auth.setRefreshToken(refresh_token) once!), see full example on https://github.com/dropbox/dropbox-sdk-js/blob/main/examples/javascript/simple-backend/code_flow_example.js

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

No branches or pull requests

4 participants