Skip to content

Commit

Permalink
Allow PKCE flow for browser based apps (#507)
Browse files Browse the repository at this point in the history
Co-authored-by: Brent Bumann <[email protected]>
  • Loading branch information
Brent1LT and Brent Bumann authored Jan 28, 2021
1 parent 8802f72 commit 9c91442
Show file tree
Hide file tree
Showing 15 changed files with 215 additions and 143 deletions.
18 changes: 18 additions & 0 deletions UPGRADING.md
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,24 @@

This document is designed to show you how to upgrade to the latest version of the SDK accomodating any breaking changes introduced by major version updates. If you find any issues with either this guide on upgrading or the changes introduced in the new version, please see [CONTRIBUTING.md][contributing]

# Upgrading from v8.X.X to v9.0.0

## 1. Unblocking browser PKCE flow

Previously, there was an issue in which Node and the Browser use different processes to generate the `codeVerifier` and `codeChallenge`. In order to remedy this, both `generatePKCECodes` and `getAuthenticationUrl` now return promises due to the how the browser digests hashes.

Previous Implementation(synchronous):
```
var authUrl = dbxAuth.getAuthenticationUrl(redirectUri, null, 'code', 'offline', null, 'none', false)
// logic for navigating to authUrl
```
New Implementation(async):
```
dbxAuth.getAuthenticationUrl(redirectUri, null, 'code', 'offline', null, 'none', false)
.then((authUrl) => {
// logic for navigating to authUrl
});
```
# Upgrading from v7.X.X to v8.0.0

## 1. Throwing Errors as `DropboxResponseError` rather than a literal object
Expand Down
10 changes: 6 additions & 4 deletions examples/javascript/PKCE-backend/code_flow_example.js
Original file line number Diff line number Diff line change
Expand Up @@ -19,11 +19,13 @@ const { Dropbox } = require('dropbox'); // eslint-disable-line import/no-unresol
const dbx = new Dropbox(config);

const redirectUri = `http://${hostname}:${port}/auth`;
const authUrl = dbx.auth.getAuthenticationUrl(redirectUri, null, 'code', 'offline', null, 'none', true);

app.get('/', (req, res) => {
res.writeHead(302, { Location: authUrl });
res.end();
dbx.auth.getAuthenticationUrl(redirectUri, null, 'code', 'offline', null, 'none', true)
.then((authUrl) => {
res.writeHead(302, { Location: authUrl });
res.end();
});
});

app.get('/auth', (req, res) => { // eslint-disable-line no-unused-vars
Expand All @@ -33,7 +35,7 @@ app.get('/auth', (req, res) => { // eslint-disable-line no-unused-vars
dbx.auth.getAccessTokenFromCode(redirectUri, code)
.then((token) => {
console.log(`Token Result:${JSON.stringify(token)}`);
dbx.auth.setRefreshToken(token.refreshToken);
dbx.auth.setRefreshToken(token.result.refresh_token);
dbx.usersGetCurrentAccount()
.then((response) => {
console.log('response', response);
Expand Down
6 changes: 4 additions & 2 deletions examples/javascript/auth/index.html
Original file line number Diff line number Diff line change
Expand Up @@ -87,8 +87,10 @@ <h2 class="code">

// Set the login anchors href using dbx.getAuthenticationUrl()
var dbx = new Dropbox.Dropbox({ clientId: CLIENT_ID });
var authUrl = dbx.auth.getAuthenticationUrl('http://localhost:8080/auth');
document.getElementById('authlink').href = authUrl;
var authUrl = dbx.auth.getAuthenticationUrl('http://localhost:8080/auth')
.then((authUrl) => {
document.getElementById('authlink').href = authUrl;
})
}
</script>
</body>
Expand Down
2 changes: 1 addition & 1 deletion examples/javascript/download/index.html
Original file line number Diff line number Diff line change
Expand Up @@ -28,7 +28,7 @@ <h2 class="code">

<!-- Example description and UI -->
<section class="container main">
<p>This example shows how to use the <code>Dropbox.sharingGetSharedLinkFile()</code> [<a href="http://dropbox.github.io/dropbox-sdk-js/DropboxTeam.html#sharingGetSharedLinkFile">docs</a>] method to download the file for the given shared link.</p>
<p>This example shows how to use the <code>Dropbox.sharingGetSharedLinkFile()</code> [<a href="http://dropbox.github.io/dropbox-sdk-js/Dropbox.html#sharingGetSharedLinkFile">docs</a>] method to download the file for the given shared link.</p>

<form onSubmit="return downloadFile()">
<input type="text" id="access-token" placeholder="Access token" />
Expand Down
11 changes: 6 additions & 5 deletions examples/javascript/simple-backend/code_flow_example.js
Original file line number Diff line number Diff line change
Expand Up @@ -20,11 +20,12 @@ const { Dropbox } = require('dropbox'); // eslint-disable-line import/no-unresol
const dbx = new Dropbox(config);

const redirectUri = `http://${hostname}:${port}/auth`;
const authUrl = dbx.auth.getAuthenticationUrl(redirectUri, null, 'code', 'offline', null, 'none', false);

app.get('/', (req, res) => {
res.writeHead(302, { Location: authUrl });
res.end();
dbx.auth.getAuthenticationUrl(redirectUri, null, 'code', 'offline', null, 'none', false)
.then((authUrl) => {
res.writeHead(302, { Location: authUrl });
res.end();
});
});

app.get('/auth', (req, res) => { // eslint-disable-line no-unused-vars
Expand All @@ -34,7 +35,7 @@ app.get('/auth', (req, res) => { // eslint-disable-line no-unused-vars
dbx.auth.getAccessTokenFromCode(redirectUri, code)
.then((token) => {
console.log(`Token Result:${JSON.stringify(token)}`);
dbx.auth.setRefreshToken(token.refreshToken);
dbx.auth.setRefreshToken(token.result.refresh_token);
dbx.usersGetCurrentAccount()
.then((response) => {
console.log('response', response);
Expand Down
4 changes: 2 additions & 2 deletions examples/javascript/team-as-user/index.html
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@
<link rel="stylesheet" href="/styles.css">
<script src="https://cdn.jsdelivr.net/npm/promise-polyfill@7/dist/polyfill.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/fetch/2.0.3/fetch.js"></script>
<script src="/__build__/DropboxTeam-sdk.min.js"></script>
<script src="/__build__/Dropbox-sdk.min.js"></script>
</head>
<body>
<!-- Example layout boilerplate -->
Expand All @@ -28,7 +28,7 @@ <h2 class="code">

<!-- Example description and UI -->
<section class="container main">
<p>This example shows how to use the DropboxTeam class and the <code>DropboxTeam.actAsUser()</code> [<a href="http://dropbox.github.io/dropbox-sdk-js/DropboxTeam.html#actAsUser">docs</a>] method, to retreive a Dropbox class that is acting as a specific user on the team.</p>
<p>This example shows how to use the Dropbox class and the <code>Dropbox.actAsUser()</code> [<a href="http://dropbox.github.io/dropbox-sdk-js/Dropbox.html#actAsUser">docs</a>] method, to retreive a Dropbox class that is acting as a specific user on the team.</p>

<form onSubmit="return listFiles()">
<input type="text" id="access-token" placeholder="Access token" />
Expand Down
4 changes: 2 additions & 2 deletions examples/javascript/team/index.html
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@
<link rel="stylesheet" href="/styles.css">
<script src="https://cdn.jsdelivr.net/npm/promise-polyfill@7/dist/polyfill.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/fetch/2.0.3/fetch.js"></script>
<script src="/__build__/DropboxTeam-sdk.min.js"></script>
<script src="/__build__/Dropbox-sdk.min.js"></script>
</head>
<body>
<!-- Example layout boilerplate -->
Expand All @@ -28,7 +28,7 @@ <h2 class="code">

<!-- Example description and UI -->
<section class="container main">
<p>This example shows how to use the DropboxTeam class and the <code>DropboxTeam.teamDevicesListTeamDevices()</code> method [<a href="http://dropbox.github.io/dropbox-sdk-js/DropboxTeam.html#teamDevicesListTeamDevices">docs</a>].</p>
<p>This example shows how to use the Dropbox class and the <code>Dropbox.teamDevicesListTeamDevices()</code> method [<a href="http://dropbox.github.io/dropbox-sdk-js/Dropbox.html#teamDevicesListTeamDevices">docs</a>].</p>

<form onSubmit="return listDevices()">
<input type="text" id="access-token" placeholder="Access token" />
Expand Down
8 changes: 4 additions & 4 deletions generator/generate_routes.py
Original file line number Diff line number Diff line change
Expand Up @@ -77,31 +77,31 @@ def main():
if verbose:
print('Generating JS types')
subprocess.check_output(
(['python', '-m', 'stone.cli', 'js_types', dropbox_pkg_path] +
(['python3', '-m', 'stone.cli', 'js_types', dropbox_pkg_path] +
specs + ['-b', 'team'] + ['-a', 'host', '-a', 'style', '-a', 'auth'] +
['--', 'types.js', '-e', json.dumps(upload_arg)]),
cwd=stone_path)

if verbose:
print('Generating JS client routes for user routes')
subprocess.check_output(
(['python', '-m', 'stone.cli', 'js_client', dropbox_pkg_path] +
(['python3', '-m', 'stone.cli', 'js_client', dropbox_pkg_path] +
specs + ['-a', 'host', '-a', 'style', '-a', 'auth'] +
['--', 'routes.js', '-c', 'Dropbox', '--wrap-response-in', 'DropboxResponse']),
cwd=stone_path)

if verbose:
print('Generating TSD types')
subprocess.check_output(
(['python', '-m', 'stone.cli', 'tsd_types', typescript_template_path] +
(['python3', '-m', 'stone.cli', 'tsd_types', typescript_template_path] +
specs + ['-b', 'team'] + ['-a', 'host', '-a', 'style'] +
['--', 'dropbox_types.d.tstemplate', 'dropbox_types.d.ts', '-e', json.dumps(upload_arg), '--export-namespaces']),
cwd=stone_path)

if verbose:
print('Generating TSD client routes for user routes')
subprocess.check_output(
(['python', '-m', 'stone.cli', 'tsd_client', typescript_template_path] +
(['python3', '-m', 'stone.cli', 'tsd_client', typescript_template_path] +
specs + ['-a', 'host', '-a', 'style'] +
['--', 'index.d.tstemplate', 'index.d.ts', '--wrap-response-in', 'DropboxResponse']),
cwd=stone_path)
Expand Down
5 changes: 3 additions & 2 deletions generator/typescript/index.d.tstemplate
Original file line number Diff line number Diff line change
Expand Up @@ -66,9 +66,10 @@ export class DropboxAuth {
* @arg {boolean} [usePKCE] - Whether or not to use Sha256 based PKCE. PKCE should be only use on
* client apps which doesn't call your server. It is less secure than non-PKCE flow but
* can be used if you are unable to safely retrieve your app secret
* @returns {String} Url to send user to for Dropbox API authentication
* @returns {Promise<String>} - Url to send user to for Dropbox API authentication
* returned in a promise
*/
getAuthenticationUrl(redirectUri: string, state?: string, authType?: 'token' | 'code', tokenAccessType?: null | 'legacy' | 'offline' | 'online', scope?: Array<String>, includeGrantedScopes?: 'none' | 'user' | 'team', usePKCE?: boolean): string;
getAuthenticationUrl(redirectUri: string, state?: string, authType?: 'token' | 'code', tokenAccessType?: null | 'legacy' | 'offline' | 'online', scope?: Array<String>, includeGrantedScopes?: 'none' | 'user' | 'team', usePKCE?: boolean): Promise<String>;

/**
* Get the client id
Expand Down
2 changes: 1 addition & 1 deletion package-lock.json

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

2 changes: 1 addition & 1 deletion package.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
{
"name": "dropbox",
"version": "8.3.0",
"version": "9.0.0",
"registry": "npm",
"description": "The Dropbox JavaScript SDK is a lightweight, promise based interface to the Dropbox v2 API that works in both nodejs and browser environments.",
"main": "cjs/index.js",
Expand Down
Loading

0 comments on commit 9c91442

Please sign in to comment.