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

Syncing on one device produces invalid token on another device (Google Drive) #1248

Closed
Jordy3D opened this issue May 14, 2021 · 9 comments · Fixed by #1362
Closed

Syncing on one device produces invalid token on another device (Google Drive) #1248

Jordy3D opened this issue May 14, 2021 · 9 comments · Fixed by #1362
Assignees
Labels

Comments

@Jordy3D
Copy link

Jordy3D commented May 14, 2021

  • Browser: Chrome
  • Operating System: Win 10
  • Stylus Version: 1.5.17
  • Screenshot:
    image

Not much I think I can say, really. I've had this issue for a week or so now. I use multiple devices, and syncing Stylus to Drive works after disconnecting and reconnecting to sync, but only on that device. When I go to another that I have previously synced, it will produce this icon image and fail to sync (with the message from above). Repeating the disconnect/reconnect steps then allow it to sync again, but other devices then fail once more.

@sillvva
Copy link

sillvva commented May 18, 2021

Having the same issue:

  • Browser: Microsoft Edge (chromium)
  • Operating System: Windows 10
  • Stylus Version: 1.5.17

@johnsonlin
Copy link

Same issue here

Browser: Chrome
Operating System: Win 10
Stylus Version: 1.5.17

@eight04
Copy link
Collaborator

eight04 commented Jul 14, 2021

There are two problems here:

  1. We should ask users to login i.e. mark the error as grant errors:
    throw new Error(`Invalid token: ${name}`);

    function isGrantError(err) {
  2. It seems that when I click "Disconnect" on one machine, Google invalidates all tokens from all machines. It is an unexpected behavior for me. Maybe we should stop revoking Google tokens until finding a workaround?
    return postQuery(`https://accounts.google.com/o/oauth2/revoke?${new URLSearchParams(params)}`);

@eight04
Copy link
Collaborator

eight04 commented Jul 14, 2021

And probably use a more descriptive message like You have been logged out instead of Invalid token.

@tophf tophf added the sync label Oct 19, 2021
@dayfuaim
Copy link

dayfuaim commented Nov 4, 2021

I have the same with Dropbox.

@ferdnyc
Copy link
Contributor

ferdnyc commented Dec 2, 2021

  1. We should ask users to login i.e. mark the error as grant errors:

I'm not sure that will help. I think logging in from a second machine will boot other machines for the same reason that disconnecting does: Google sees the login request from the second machine as a re-acquiring of access Stylus already has, since the access is managed by account. So it replaces the previous access with the new one.

If I choose "Login" from a second machine, where I already have sync set up on a first machine, this is the page that comes up:

image

Given that Stylus "already has some access", and what is being requested here is the SAME access, it's not particularly surprising that the previous access is overridden by the new grant. Seems as though Google grants access by account, not by host, so the same account shouldn't need to acquire access to the same service more than once — or if it does, only the most recent is valid..

Stylus' tokens for that access may need to be transferred from one machine to the other(s), but that could perhaps be done using Chrome's built-in Extension Settings sync.

@ferdnyc
Copy link
Contributor

ferdnyc commented Dec 2, 2021

Stylus' tokens for that access may need to be transferred from one machine to the other(s), but that could perhaps be done using Chrome's built-in Extension Settings sync.

Actually, that won't even work, because it still won't solve the related problem where if I connect Stylus in Chrome to my Google Drive account, then try to connect Stylus in Firefox to the same account (from the same machine), Chrome gets booted out. Feels like there must be a different API for updating access to add additional client instances, rather than effectively transferring the grant from one instance to another.

@ferdnyc
Copy link
Contributor

ferdnyc commented Dec 2, 2021

Ooh! You know what I don't see, in the existing code? I'm not sure, but this may be the path to retrieving authorization tokens on a second client without destroying the previous authorization from a different client.

(From Using OAuth 2.0 to Access Google APIs § Incremental authorization, emphasis mine):

The following rules apply to an access token obtained from an incremental authorization:

  • The token can be used to access resources corresponding to any of the scopes rolled into the new, combined authorization.
  • When you use the refresh token for the combined authorization to obtain an access token, the access token represents the combined authorization and can be used for any of the scope values included in the response.
  • The combined authorization includes all scopes that the user granted to the API project even if the grants were requested from different clients. For example, if a user granted access to one scope using an application's desktop client and then granted another scope to the same application via a mobile client, the combined authorization would include both scopes.
  • If you revoke a token that represents a combined authorization, access to all of that authorization's scopes on behalf of the associated user are revoked simultaneously.

(In the "OAuth 2.0 Endpoints" subtab:)

To add scopes to an existing access token, include the include_granted_scopes parameter in your request to Google's OAuth 2.0 server.

The following code snippet demonstrates how to do that. The snippet assumes that you have stored the scopes for which your access token is valid in the browser's local storage. (The complete example code stores a list of scopes for which the access token is valid by setting the oauth2-test-params.scope property in the browser's local storage.)

The snippet compares the scopes for which the access token is valid to the scope you want to use for a particular query. If the access token does not cover that scope, the OAuth 2.0 flow starts.

var SCOPE = 'https://www.googleapis.com/auth/drive.metadata.readonly';
var params = JSON.parse(localStorage.getItem('oauth2-test-params'));

var current_scope_granted = false;
if (params.hasOwnProperty('scope')) {
  var scopes = params['scope'].split(' ');
  for (var s = 0; s < scopes.length; s++) {
    if (SCOPE == scopes[s]) {
      current_scope_granted = true;
    }
  }
}

if (!current_scope_granted) {
  oauth2SignIn(); 
} else {
  // Since you already have access, you can proceed with the API request.
}

/*
 * Create form to request access token from Google's OAuth 2.0 server.
 */
function oauthSignIn() {
  // Google's OAuth 2.0 endpoint for requesting an access token
  var oauth2Endpoint = 'https://accounts.google.com/o/oauth2/v2/auth';

  // Create <form> element to submit parameters to OAuth 2.0 endpoint.
  var form = document.createElement('form');
  form.setAttribute('method', 'GET'); // Send as a GET request.
  form.setAttribute('action', oauth2Endpoint);

  // Parameters to pass to OAuth 2.0 endpoint.
  var params = {'client_id': 'YOUR_CLIENT_ID',
                'redirect_uri': 'YOUR_REDIRECT_URI',
                'response_type': 'token',
                'scope': 'https://www.googleapis.com/auth/drive.metadata.readonly',
 /* ===>> */    'include_granted_scopes': 'true',  /* <<=== */
                'state': 'pass-through value'};

  // Add form parameters as hidden input values.
  for (var p in params) {
    var input = document.createElement('input');
    input.setAttribute('type', 'hidden');
    input.setAttribute('name', p);
    input.setAttribute('value', params[p]);
    form.appendChild(input);
  }

  // Add form to page and submit it to open the OAuth 2.0 endpoint.
  document.body.appendChild(form);
  form.submit();
}

@eight04
Copy link
Collaborator

eight04 commented Dec 2, 2021

Google sees the login request from the second machine as a re-acquiring of access Stylus already has, since the access is managed by account. So it replaces the previous access with the new one.

Do you mean that the sync fails on the 1st machine if you connect to Google on the second machine? I didn't see this behavior when testing. I also sync to GDrive in both Edge and Firefox on my laptop.

  1. mark the error as grant errors:

By marking invalid token as a grant error, users can re-login without disconnect i.e. they can avoid revoking tokens in other machines.

Note that it is not just an issue of Google. Suppose you use Stylus on multiple machines and have setup browser sync, all other machines will disconnect all together if you disconnect one of them. Because changes to the setting also flow to other machines via browser sync.

Stylus "already has some access",

That's interesting. I think Google expect us to access the service via a single instance, which generally should be a web server. This also explains why Google invalidates all tokens from the same account if they get a single revoke request.

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