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

PKCE Flow in web environment rejects code_verifier #527

Closed
FloppyNotFound opened this issue Jan 31, 2021 · 19 comments · Fixed by #594
Closed

PKCE Flow in web environment rejects code_verifier #527

FloppyNotFound opened this issue Jan 31, 2021 · 19 comments · Fixed by #594
Labels

Comments

@FloppyNotFound
Copy link

I am trying to implement the PKCE OAuth flow in a web environment.
Therefore, I first call API to get a one time code:

return dropboxAuth .getAuthenticationUrl( this._redirectUrl, void 0, "code", void 0, void 0, void 0, true )

which leads to the following URL:
https://www.dropbox.com/oauth2/authorize?response_type=code&client_id=CLIENT_ID&redirect_uri=REDIRECT_URI&code_challenge_method=S256&code_challenge=CODE_CHALLENGE

Before navigating to this link, I save the code_verifier which was generated by the Dropbox SDK.
After navigating back from the URL, I get the one time code as a param:
http://localhost:5000/?code=CODE

With this code, and the code_verifier I saved previously, I try to get an access token by calling (there is no method to set the code verifier, but since the app is left for navigating to the OAuth page, I somehow have to store it, therefore I set it "manually" after returning to my app, to make sure the SDK uses the same verifier as used previously to create the challenge):
dropboxAuth.codeVerifier = verifier; const accessToken = await dropboxAuth.getAccessTokenFromCode(this._redirectUrl, code);
which leads to the following URL:

POST | https://api.dropboxapi.com/oauth2/token?grant_type=authorization_code&code=CODE&client_id=CLIENT_ID&code_verifier=CODE_VERIFIER&redirect_uri=REDIRECT_URI

and the following result returned from Dropbox:
{"error_description": "invalid code verifier", "error": "invalid_grant"}

I have checked multiple times by debugging, the code_verifier sent to the token endpoint matches the code_verifier generated by the Dropbox SDK before navigation to the OAuth page happens.

Am I missing something here?
Any help is highly appreciated.

SDK-Version: 9.0.0
Browsers: Firefox 84.0.2 64bit
Chrome: 87.0.4280.141 (Official Build) (64-bit)

@alex-fer
Copy link

alex-fer commented Feb 1, 2021

Same problem here.
I suggest inserting a method to handle the case where the dropboxAuth object cannot be kept.
Since the user is redirected, I have to store the codeChallenge and codeVerifier in the local storage and then recreate the dropboxAuth object.

@FloppyNotFound
Copy link
Author

FloppyNotFound commented Feb 1, 2021

That's not really the problem, the issue I'm experiencing is, that even though I store the verifier and re-set it once I get back, I cannot get an access token, because the verifier is rejected by the backend, although it's the same verifier the challenge was created with.
Does this work for you?

But yes, a method for getting and setting the verifier and challenge would also be nice.

@alex-fer
Copy link

alex-fer commented Feb 1, 2021

I have the same problem with the verifier. The backend returns the same error.

It is not clean to set those variables in this way.
I suggested inserting a method since I do not want to broke the implementation if they change the variables names or other internal things.

@greg-db
Copy link
Contributor

greg-db commented Feb 1, 2021

Thanks for writing this up! I'm checking with the team on this.

@alex-fer
Copy link

Any news on this problem? @greg-db
Thanks

@greg-db
Copy link
Contributor

greg-db commented Feb 11, 2021

@Blodhgard I don't have an update on this right now. I'll check in on it again.

@FloppyNotFound
Copy link
Author

@greg-db Do you have any updates on this matter? Since you plan to depreciate long lived tokens this summer, we really need this to work in order to build server less apps.

@greg-db
Copy link
Contributor

greg-db commented Mar 1, 2021

@FloppyNotFound I don't have any news on this yet unfortunately. I'll follow up here once I do.

@HerrZatacke
Copy link

Hi there,

I created a standalone example which uses a popup and a callback on window.opener to persist the codeChallenge and codeVerifier

<!DOCTYPE html>
<html lang="en">
  <head>
    <meta charset="utf-8">
    <title>Dropbox Demo</title>
    <script type="text/javascript" src="https://cdn.jsdelivr.net/npm/dropbox@9.2.0/dist/Dropbox-sdk.min.js"></script>
  </head>
  <body>
    <button id="auth" style='display:none'>Start Auth</button>
    <pre id="log"></pre>
    <script>

      const DROPBOX_APP_KEY = 'PUT IT HERE';

      const button = document.getElementById('auth');
      const log = document.getElementById('log');

      const dbx = new Dropbox.Dropbox({
        clientId: DROPBOX_APP_KEY,
      });

      if (window.opener) {
        window.setTimeout(() => {
          const searchParams = new URLSearchParams(window.location.search.substr(1));
          const dropboxCode = searchParams.get('code');
          window.opener.setCode(dropboxCode);
          window.setTimeout(() => {
            window.close();
          }, 400);
        }, 100);
      } else {
        button.style.display = 'block'
      }

      document.addEventListener('DOMContentLoaded', () => {
        button.addEventListener('click',
          () => {
            const redirectUri = encodeURIComponent(`${window.location.protocol}//${window.location.host}/`);
            // const redirectUri = null;
            dbx.auth.getAuthenticationUrl(
              redirectUri,
              '',
              'code',
              'offline',
              [
                'account_info.read',
                'files.content.write',
                'files.content.read',
              ],
              'user',
              true,
            )
              .then((authUrl) => {

                log.innerHTML += `AuthUrl: ${authUrl}\n\n`;
                log.innerHTML += `CodeChallenge: ${dbx.auth.codeChallenge}\n\n`;
                log.innerHTML += `CodeVerifier: ${dbx.auth.codeVerifier}\n\n`;

                window.open(authUrl, 'authwin', 'width=700,height=800');

                // eslint-disable-next-line no-alert
                window.setCode = (code) => {
                  log.innerHTML += `Code: ${code}\n\n`;
                  dbx.auth.getAccessTokenFromCode(redirectUri, code)
                    .then(({ result }) => {
                      console.log(result);
                      log.innerHTML += `Result: ${JSON.stringify(result, null, 2)}\n\n`;
                    })
                    .catch((error) => {
                      console.log(error);
                      log.innerHTML += `Error: ${JSON.stringify(error, null, 2)}\n\n`;
                    });
                };
              });
          }
        );
      })
    </script>
  </body>
</html>

@HerrZatacke
Copy link

From what I understand dropbox is currently not offering a non-deprecated authentication for unhosted/serverless JS-Apps that do not force the user to re-authenticate every four hours.

Is that correct?

@FloppyNotFound
Copy link
Author

FloppyNotFound commented Mar 14, 2021

@HerrZatacke yes, that is my understanding, since the provided PKCE flow method is not working.

@HerrZatacke
Copy link

It's not working for me either, I just provided a standalone example that does not require to "extract" and store and after reload "insert" the codeChallenge and codeVerifier

@greg-db
Copy link
Contributor

greg-db commented Mar 15, 2021

Thanks for the notes all! I'm checking in on this with the team.

@mbret
Copy link

mbret commented Mar 16, 2021

Can confirm, dropbox services recently started working and we are receiving the same error since then with a previously working flow.

Is there any alternative for a client app to get a token in the meantime ?

@greg-db
Copy link
Contributor

greg-db commented Mar 16, 2021

@mbret I'm not sure I understand your message. I don't believe anything that was previously working should have stopped working. Can you elaborate?

In any case, the non-PKCE flow should still work, such as in this example.

@mbret
Copy link

mbret commented Mar 18, 2021

@greg-db i am using the same flow as @FloppyNotFound and it appears that in fact the version 9.x is not working anymore. Downgrading to 8.x with the same code fix the issue for now. I followed the upgrade guide and used the promises for the auth url but I do get the same error as described here.

@greg-db
Copy link
Contributor

greg-db commented Mar 18, 2021

@mbret Thanks for clarifying! This is open with the team and I'll follow up here once I have an update.

@rogebrd
Copy link
Contributor

rogebrd commented Mar 24, 2021

This should be fixed as of v9.4.0. Please reopen this issue if it was not resolved.

@rogebrd rogebrd closed this as completed Mar 24, 2021
@rogebrd rogebrd linked a pull request Mar 24, 2021 that will close this issue
@greg-db
Copy link
Contributor

greg-db commented Mar 25, 2021

For reference, the new example for using PKCE in the browser can be found here.

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

Successfully merging a pull request may close this issue.

7 participants
@mbret @HerrZatacke @greg-db @alex-fer @FloppyNotFound @rogebrd and others