Customizing scopes in the OAuth app authorization flow

// By Dropbox Developer Support Team • Sep 13, 2024

As you may know, the Dropbox API authorization system uses "scopes" for granular control over what functionality an app can access. This allows app developers to select what API functionality their apps can use, so that users can feel more comfortable granting apps access to their accounts. This can help give users peace of mind that the apps will only be able to perform the operations that the apps actually need. It may not be obvious though that you can further customize exactly which scopes your app requests and when. Let's look at the options for configuring and customizing scopes in more detail.


First, it's important to note that the scopes you enable on the Permissions tab of the app's page on the App Console define the maximum, as well as the default, set of scopes that the app can request. For example, let's look at a user-linked app. By default, it has the account_info.read scope, which is required to be registered for user-linked apps. We'll also enable files.content.read and files.metadata.read  scopes for this example.

A screenshot showing the app’s scopes configuration.

A screenshot showing the app’s scopes configuration.

When we send a user to the app authorization page, by default, they'll be prompted to authorize the app with all of those scopes:

https://www.dropbox.com/oauth2/authorize?client_id=<APP_KEY>&response_type=code

A screenshot of the app authorization page defaulting to the scopes registered to the app.

A screenshot of the app authorization page defaulting to the scopes registered to the app.

However, if you don't need all of the scopes that are currently enabled on the app, you can instead set the scope parameter on the /oauth2/authorize URL you construct. In that parameter, you can put a space-delimited list of scopes to specify just a sub-set of scopes to request for that authorization. This can be useful in scenarios where the app doesn't need all of the app's potential access, or as a way to more gradually gain the user's trust.

For example, say we just want the app to be able to read the metadata of the files and folders in the user’s account; we would construct the URL like this:

https://www.dropbox.com/oauth2/authorize?client_id=<APP_KEY>&response_type=code&scope=files.metadata.read

A screenshot of the app authorization page requesting a sub-set of the scopes registered to the app.

A screenshot of the app authorization page requesting a sub-set of the scopes registered to the app.

Tip: Note how even though account_info.read is required to be enabled on the app itself, you don't have to request it during authorization. For more privacy-oriented scenarios where the app doesn't need access to the user's account information, you can set the scope parameter without the account_info.read scope as above. 

If a user authorizes the app using that /oauth2/authorize URL, the app will then receive a payload like the following when it subsequently makes the corresponding call to /oauth2/token using the resulting authorization code:


{
  "access_token": "<ACCESS_TOKEN>",
  "token_type": "bearer",
  "expires_in": 14400,
  "scope": "files.metadata.read",
  "uid": "<USER_ID>",
  "account_id": "<ACCOUNT_ID>"
}

If the app needs additional scopes later, it can prompt the user to authorize the app again, with the scope parameter configured with more scopes, or without the scope parameter set at all, to request all of the app’s scopes.


You can also use the include_granted_scopes parameter to make it easier to request additional scopes without explicitly listing the previously granted scopes again. For example, if we then additionally want the app to be able to read the content of files in that same user’s account, we would construct another URL like this:


https://www.dropbox.com/oauth2/authorize?client_id=<APP_KEY>&response_type=code&scope=files.content.read&include_granted_scopes=user

A screenshot of the app authorization page requesting additional scopes registered to the app.

A screenshot of the app authorization page requesting additional scopes registered to the app.

If the user authorizes the app using that /oauth2/authorize URL, the app will then receive a payload like the following when it subsequently makes the corresponding call to /oauth2/token using the resulting authorization code:


{
  "access_token": "<ACCESS_TOKEN>",
  "token_type": "bearer",
  "expires_in": 14400,
  "scope": "files.content.read files.metadata.read",
  "uid": "<USER_ID>",
  "account_id": "<ACCOUNT_ID>"
}

Note how this time, the access token has permission to both files.metadata.read as well as files.content.read.

Also, if the app needs long-term unattended access, it can use this functionality while also requesting “offline” access. Check out our "Using OAuth 2.0 with offline access" blog post for information on that.

By the way, the owner of the app can change the scopes enabled on an app, but doing so doesn't affect the scopes that have already been granted by a particular user. For example, if a user authorized an app with a particular set of scopes and then the app owner enabled another scope on the app, that preexisting grant doesn't automatically get that additional scope added. Likewise, if the app owner disabled a scope on the app, the preexisting grant doesn't automatically get that scope removed. That way, apps don't get more access than users have authorized, and existing connections don't break by changing the amount of access they had.

By using this /oauth2/authorize scope functionality, you can build apps that are functional but flexible and request only the minimum permissions needed to get the job done.

If you have any questions, you can always reach us on our forum or via our contact form.

 

 


// Copy link